下载和编译 EMQ X
EMQ X 的插件需要调用 EMQ X 内部的一些函数,所以实际上是没有办法单独编译 EMQ X 插件的,而是需要将插件的代码放到 EMQ X 源码中一起编译。所以首先要搭建 EMQ X 的编译环境。EMQ X 的代码需要从 EMQ X 的 Release Project 下载,因为本课程是基于 EMQ X 3.2.0 开发的,所以我们要编译版本为 3.2.0 的 EMQ X :
git clone -b v3.2.0 https://github.com/emqx/emqx-rel.git
然后先编译一次 EMQ X 来看我们的编译环境是否搭建成功:
make
如果你看到如下输出,则代表编译成功完成了:
===> Resolved emqx-3.2.0
===> Including Erts from /usr/local/lib/erlang
===> release successfully created!
然后运行emqx-rel/_build/emqx/rel/emqx/bin/emqx console
,这个是 EMQ X 的控制台模式(和emqx start
不同),会启动一个交互式的 Erlang 控制台方便开发和调试,如果看到以下输出:
EMQ X Broker v3.2.0 is running now!
Eshell V10.3 (abort with ^G)
说明 EMQ X 编译成功了。现在关闭 emqx console,进行下一步。
使用插件模板
EMQ X 提供了一个模板插件 emqx-plugin-template 可以用作编写的插件的参考,这个插件没有什么功能,就是在事件,例如 “client.connected” 发生时,将事件的内容在打印在 emqx console 上面。在编写我们自己的插件前,可以先通过这个插件模板来学习如何编译插件和代码结构。
编译插件
插件在 EMQX 是作为一个编译期的依赖库和 EMQX 的核心代码一起进行编译的,EMQX 的 Makefile 默认情况下已经把 emqx-plugin-template 作为一个依赖了, 我们可以在 rebar.config 里找到对 emqx-plugin-template 相关的配置项:
{deps,
[ emqx
....
, emqx_plugin_template
].
但是默认情况下这个插件并不会随 emqx 发布,为了在 emqx 里面使用这个插件,我们还需要修改 rebar.config,加入以下内容:
{relx,
.....
[ kernel
....
, {emqx_psk_file, load}
, {emqx_plugin_template, load}
]
我们在编译自己的插件时,也需要在这 rebar.config 中加入类似的关于插件的配置。
在 EMQ X 的编译工具里,编译期依赖是由一个 git 地址指定的,所以要使用一个 git 仓库来保存插件的代码。
EMQ X 编译完成后可以在emqx-rel/ _build/emqx/lib
下找到编译完成的 emqx-plugin-template,然后把整个emqx_plugin_template目录复制到< EMQ X 安装目录>/lib
下(注意不是编译得到的 EMQ X 的目录),以 console 模式启动 EMQ X: < EMQ X 安装目录>/bin/emqx console
,然后加载模板插件:
< EMQ X 安装目录>/bin/emqx_ctl plugins load emqx_plugin_template
然后运行一个 MQTT Client: mosquitto_sub -t "test/pc"
,我们可以在 EMQ X console 上打印出以下信息:
这不是出错的信息,而是 Client 触发的诸如 connected、subscribed 等事件的打印。
这样我们就可以编译和使用 emqx-plugin-template 了,接下来我们看一下 emqx-plugin-template 的代码结构。
代码结构
在成功的进行一次编译以后,emqx-rel 项目会把依赖的代码都 checkout 到emqx-rel/deps
目录下,我们可以在emqx-rel/deps/emqx_plugin_template
目录中找到模板插件的所有代码,如下图所示:
图中序号 1、2、3 分别表示:插件的代码、插件的配置文件、插件的编译配置(比如依赖等)。
序号 2 和 3 我们会在编写插件的时候顺带讲一下,这一节我们主要来看下插件的代码结构。
EMQ X 的插件和 EMQ X 一样,是一个运行在 OTP 上面的 APP,这个 APP 的入口是emqx_plugin_template_app.erl
:
它主要完成两个功能:
- 运行 App 的监视器;
- 加载插件的主要功能代码。
首先我们来看一下插件的主要功能代码 emqx_plugin_template.erl
:
插件的功能很简单:
- 在插件启动的时候,注册钩子函数,在相应的事件方式时,会触发钩子函数;
- 钩子函数的实现,在事件发生时打印出事件的内容。
然后是监视器的代码emqx_plugin_template_sub.erl
:
这部分代码基本是固定的,唯一要注意的是划线部分的代码设置了 OTP 监控 App 的测量,一个 OTP App 会存在多个并行的 Worker, one_for_all
代表如果一个 Worker 因为某种异常崩溃了,则重启所有的 Worker,可选的设置还有one_for_one
,代表只重启崩溃的Worker。
最后是插件 APP 的描述文件emqx_plugin_template_app.erl
:
它主要是描述 App 的名字、加载的模块等。
“emqx_cli_demo.erl” 用于添加自定义 console 命令,暂时用不到,本课程就跳过了。
修改插件
我们可以尝试修改一点代码,比如修改一下 Client 连接时,用户名和密码的打印内容:
%% emqx_plugin_template.erl
on_client_authenticate(Credentials = #{client_id := ClientId, password := Password}, _Env) ->
io:format("Modified: Client(~s) authenticate, Password:~p ~n", [ClientId, Password]),
{stop, Credentials#{auth_result => success}}.
重新编译make
,然后用同样的方法将编译出来的插件复制到< EMQ X 安装目录>/lib
,重新启动 EMQ X console, 用任意的 MQTT Client 连接到 EMQ X,我们会看到以下输出:
(emqx@127.0.0.1)1> Modified: Client(mosq/lRZIa6iiZQ43roYR8P) authenticate, Password:undefined
说明我们修改的代码已经生效了。
当插件代码用 git 的方式被引用进来以后,我们就可以在本地进行修改而不用将修改提交到 git 仓库再编译了。
EMQ X 的插件不支持热加载,所以修改了插件代码以后需要重启 EMQ X 。
这一节我们学习了插件的代码结构和编译方法,下一节我们开始编写 RabbitMQ Hook 插件。