这一课,我们开始搭建 EMQ X 插件开发的环境。

下载和编译 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 :

  1. git clone -b v3.2.0 https://github.com/emqx/emqx-rel.git

然后先编译一次 EMQ X 来看我们的编译环境是否搭建成功:

  1. make

如果你看到如下输出,则代表编译成功完成了:

  1. ===> Resolved emqx-3.2.0
  2. ===> Including Erts from /usr/local/lib/erlang
  3. ===> release successfully created!

然后运行emqx-rel/_build/emqx/rel/emqx/bin/emqx console,这个是 EMQ X 的控制台模式(和emqx start不同),会启动一个交互式的 Erlang 控制台方便开发和调试,如果看到以下输出:

  1. EMQ X Broker v3.2.0 is running now!
  2. 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 相关的配置项:

  1. {deps,
  2. [ emqx
  3. ....
  4. , emqx_plugin_template
  5. ].

但是默认情况下这个插件并不会随 emqx 发布,为了在 emqx 里面使用这个插件,我们还需要修改 rebar.config,加入以下内容:

  1. {relx,
  2. .....
  3. [ kernel
  4. ....
  5. , {emqx_psk_file, load}
  6. , {emqx_plugin_template, load}
  7. ]

我们在编译自己的插件时,也需要在这 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,然后加载模板插件:

  1. < EMQ X 安装目录>/bin/emqx_ctl plugins load emqx_plugin_template

然后运行一个 MQTT Client: mosquitto_sub -t "test/pc",我们可以在 EMQ X console 上打印出以下信息:
40.搭建开发和编译环境 - 图1
这不是出错的信息,而是 Client 触发的诸如 connected、subscribed 等事件的打印。
这样我们就可以编译和使用 emqx-plugin-template 了,接下来我们看一下 emqx-plugin-template 的代码结构。

代码结构

在成功的进行一次编译以后,emqx-rel 项目会把依赖的代码都 checkout 到emqx-rel/deps目录下,我们可以在emqx-rel/deps/emqx_plugin_template目录中找到模板插件的所有代码,如下图所示:
40.搭建开发和编译环境 - 图2
图中序号 1、2、3 分别表示:插件的代码、插件的配置文件、插件的编译配置(比如依赖等)。
序号 2 和 3 我们会在编写插件的时候顺带讲一下,这一节我们主要来看下插件的代码结构。
EMQ X 的插件和 EMQ X 一样,是一个运行在 OTP 上面的 APP,这个 APP 的入口是emqx_plugin_template_app.erl:
40.搭建开发和编译环境 - 图3
它主要完成两个功能:

  1. 运行 App 的监视器;
  2. 加载插件的主要功能代码。

首先我们来看一下插件的主要功能代码 emqx_plugin_template.erl:
40.搭建开发和编译环境 - 图4
插件的功能很简单:

  1. 在插件启动的时候,注册钩子函数,在相应的事件方式时,会触发钩子函数;
  2. 钩子函数的实现,在事件发生时打印出事件的内容。

然后是监视器的代码emqx_plugin_template_sub.erl
40.搭建开发和编译环境 - 图5
这部分代码基本是固定的,唯一要注意的是划线部分的代码设置了 OTP 监控 App 的测量,一个 OTP App 会存在多个并行的 Worker, one_for_all代表如果一个 Worker 因为某种异常崩溃了,则重启所有的 Worker,可选的设置还有one_for_one,代表只重启崩溃的Worker。
最后是插件 APP 的描述文件emqx_plugin_template_app.erl
40.搭建开发和编译环境 - 图6
它主要是描述 App 的名字、加载的模块等。

“emqx_cli_demo.erl” 用于添加自定义 console 命令,暂时用不到,本课程就跳过了。

修改插件

我们可以尝试修改一点代码,比如修改一下 Client 连接时,用户名和密码的打印内容:

  1. %% emqx_plugin_template.erl
  2. on_client_authenticate(Credentials = #{client_id := ClientId, password := Password}, _Env) ->
  3. io:format("Modified: Client(~s) authenticate, Password:~p ~n", [ClientId, Password]),
  4. {stop, Credentials#{auth_result => success}}.

重新编译make,然后用同样的方法将编译出来的插件复制到< EMQ X 安装目录>/lib,重新启动 EMQ X console, 用任意的 MQTT Client 连接到 EMQ X,我们会看到以下输出:

  1. (emqx@127.0.0.1)1> Modified: Client(mosq/lRZIa6iiZQ43roYR8P) authenticate, Password:undefined

说明我们修改的代码已经生效了。
当插件代码用 git 的方式被引用进来以后,我们就可以在本地进行修改而不用将修改提交到 git 仓库再编译了。

EMQ X 的插件不支持热加载,所以修改了插件代码以后需要重启 EMQ X 。

这一节我们学习了插件的代码结构和编译方法,下一节我们开始编写 RabbitMQ Hook 插件。