├── .autod.conf.js ├── .eslintignore ├── .eslintrc ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── nodejs.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.EN.md ├── README.md ├── agent.js ├── app.js ├── appveyor.yml ├── bootstrap.js ├── config └── config.default.js ├── lib ├── DataBus.js └── mqttClient.js ├── package-lock.json ├── package.json └── test ├── fixtures └── apps │ └── mqtt-plugin-test │ ├── app │ ├── controller │ │ └── home.js │ └── router.js │ ├── config │ └── config.default.js │ └── package.json └── mqtt-plugin.test.js /.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | write: true, 5 | prefix: '^', 6 | plugin: 'autod-egg', 7 | test: [ 8 | 'test', 9 | 'benchmark', 10 | ], 11 | devdep: [ 12 | 'egg', 13 | 'egg-ci', 14 | 'egg-bin', 15 | 'autod', 16 | 'autod-egg', 17 | 'eslint', 18 | 'eslint-config-egg', 19 | ], 20 | exclude: [ 21 | './test/fixtures', 22 | './docs', 23 | './coverage', 24 | ], 25 | }; 26 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-egg" 3 | } 4 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | ##### Checklist 12 | 13 | 14 | - [ ] `npm test` passes 15 | - [ ] tests and/or benchmarks are included 16 | - [ ] documentation is changed or added 17 | - [ ] commit message follows commit guidelines 18 | 19 | ##### Affected core subsystem(s) 20 | 21 | 22 | 23 | ##### Description of change 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ${{ matrix.os }} 16 | 17 | strategy: 18 | matrix: 19 | node-version: [8.x, 10.x] 20 | os: [ubuntu-latest, windows-latest, macos-latest] 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v1 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm i -g npminstall && npminstall 29 | - run: npm run ci 30 | env: 31 | CI: true 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | node_modules/ 4 | coverage/ 5 | .idea/ 6 | run/ 7 | .DS_Store 8 | *.swp 9 | 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: node_js 3 | node_js: 4 | - '8' 5 | - '10' 6 | before_install: 7 | - npm i npminstall -g 8 | install: 9 | - npminstall 10 | script: 11 | - npm run ci 12 | after_script: 13 | - npminstall codecov && codecov 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-present Alibaba Group Holding Limited and other contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.EN.md: -------------------------------------------------------------------------------- 1 | # egg-mqtt-plugin 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![build status][travis-image]][travis-url] 5 | [![Test coverage][codecov-image]][codecov-url] 6 | [![David deps][david-image]][david-url] 7 | [![Known Vulnerabilities][snyk-image]][snyk-url] 8 | [![npm download][download-image]][download-url] 9 | 10 | [npm-image]: https://img.shields.io/npm/v/egg-mqtt-plugin.svg?style=flat-square 11 | [npm-url]: https://npmjs.org/package/egg-mqtt-plugin 12 | [travis-image]: https://img.shields.io/travis/eggjs/egg-mqtt-plugin.svg?style=flat-square 13 | [travis-url]: https://travis-ci.org/eggjs/egg-mqtt-plugin 14 | [codecov-image]: https://img.shields.io/codecov/c/github/eggjs/egg-mqtt-plugin.svg?style=flat-square 15 | [codecov-url]: https://codecov.io/github/eggjs/egg-mqtt-plugin?branch=master 16 | [david-image]: https://img.shields.io/david/eggjs/egg-mqtt-plugin.svg?style=flat-square 17 | [david-url]: https://david-dm.org/eggjs/egg-mqtt-plugin 18 | [snyk-image]: https://snyk.io/test/npm/egg-mqtt-plugin/badge.svg?style=flat-square 19 | [snyk-url]: https://snyk.io/test/npm/egg-mqtt-plugin 20 | [download-image]: https://img.shields.io/npm/dm/egg-mqtt-plugin.svg?style=flat-square 21 | [download-url]: https://npmjs.org/package/egg-mqtt-plugin 22 | 23 | 26 | 27 | 更详细的使用请阅读[中文文档](README.md) 28 | 29 | ## Install 30 | 31 | ```bash 32 | $ npm i egg-mqtt-plugin --save 33 | ``` 34 | 35 | ## Usage 36 | 37 | ```js 38 | // {app_root}/config/plugin.js 39 | exports.mqtt = { 40 | enable: true, 41 | package: 'egg-mqtt-plugin', 42 | }; 43 | ``` 44 | 45 | ## Configuration 46 | 47 | ```js 48 | // {app_root}/config/config.default.js 49 | exports.mqtt = { 50 | host: 'mqtt://xxx.xxx.x.x', 51 | port: 1883, 52 | username: 'username', 53 | password: 'password', 54 | clientId: 'client_id', 55 | options: { 56 | keeplive: 60, 57 | protocolId: 'MQTT', 58 | protocol: 'MQTT', 59 | protocolVersion: 4, 60 | clean: true, 61 | rejectUnauthorized: false, 62 | reconnectPeriod: 1000, 63 | connectTimeout: 30 * 1000, 64 | }, 65 | topics: { 66 | 'topic-topic-topic': { qos: 0 }, 67 | }, 68 | }; 69 | ``` 70 | 71 | see [config/config.default.js](config/config.default.js) for more detail. 72 | 73 | ## Example 74 | 75 | 76 | 77 | ## Questions & Suggestions 78 | 79 | Please open an issue [here](https://github.com/eggjs/egg/issues). 80 | 81 | ## License 82 | 83 | [MIT](LICENSE) 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # egg-mqtt-plugin 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![build status][travis-image]][travis-url] 5 | [![Test coverage][codecov-image]][codecov-url] 6 | [![David deps][david-image]][david-url] 7 | [![Known Vulnerabilities][snyk-image]][snyk-url] 8 | [![npm download][download-image]][download-url] 9 | 10 | [npm-image]: https://img.shields.io/npm/v/egg-mqtt-plugin.svg?style=flat-square 11 | [npm-url]: https://npmjs.org/package/egg-mqtt-plugin 12 | [travis-image]: https://img.shields.io/travis/eggjs/egg-mqtt-plugin.svg?style=flat-square 13 | [travis-url]: https://travis-ci.org/eggjs/egg-mqtt-plugin 14 | [codecov-image]: https://img.shields.io/codecov/c/github/eggjs/egg-mqtt-plugin.svg?style=flat-square 15 | [codecov-url]: https://codecov.io/github/eggjs/egg-mqtt-plugin?branch=master 16 | [david-image]: https://img.shields.io/david/eggjs/egg-mqtt-plugin.svg?style=flat-square 17 | [david-url]: https://david-dm.org/eggjs/egg-mqtt-plugin 18 | [snyk-image]: https://snyk.io/test/npm/egg-mqtt-plugin/badge.svg?style=flat-square 19 | [snyk-url]: https://snyk.io/test/npm/egg-mqtt-plugin 20 | [download-image]: https://img.shields.io/npm/dm/egg-mqtt-plugin.svg?style=flat-square 21 | [download-url]: https://npmjs.org/package/egg-mqtt-plugin 22 | 23 | 24 | ## 依赖说明 25 | 26 | ### 依赖的 egg 版本 27 | 28 | egg-mqtt-plugin 版本 | egg 2.x 29 | --- | --- 30 | 1.x | 😁 31 | 0.x | ❌ 32 | 33 | 34 | ## 开启插件 35 | 36 | ```js 37 | // config/plugin.js 38 | exports.mqtt = { 39 | enable: true, 40 | package: 'egg-mqtt-plugin', 41 | }; 42 | 43 | // config/default.js 44 | config.mqtt = { 45 | host: 'mqtt://xxx.xxx.x.x', 46 | port: 1883, 47 | username: 'username', 48 | password: 'password', 49 | clientId: 'client_id', 50 | topics: { 51 | 'topic-topic-topic': { qos: 0 }, 52 | }, 53 | // options:{}, 54 | // DataBus: true, 55 | } 56 | ``` 57 | 58 | 59 | [详细配置](#config) 60 | [插件介绍](#info) 61 | [插件的使用](#use) 62 | [插件的使用.建立链接](#use-link) 63 | [插件的使用.发布](#use-publish) 64 | [插件的使用.订阅](#use-subscribe) 65 | [插件的使用.取消订阅](#use-unsubscribe) 66 | [插件的使用.监听订阅消息](#use-message) 67 | [Mqtt类](#mqtt) 68 | [示例](#example) 69 | [issue](#issue) 70 | [license](#license) 71 | 72 | ## 详细配置 73 | 74 | 请到 [config/config.default.js](config/config.default.js) 查看详细配置项说明。 75 | 76 | |配置项|类型|必填|默认值|描述| 77 | |:- | :-| :-| :-| :-| 78 | |host|string|√| 79 | |port|number|√| 80 | |username|string|√| 81 | |password|string|√| 82 | |clientId|string|√| 83 | |DataBus|boolean|×|true|是否需要统一的数据输出| 84 | |topics|object|×| |需要订阅的topic,参照[mqtt.subsceibe()](https://www.npmjs.com/package/mqtt#connect) 85 | |options|object|×||keeplive,connectTimeout等配置,参照[mqtt.connect()](https://www.npmjs.com/package/mqtt#connect) 86 | 87 | > `topics`用来创建订阅,会传入`mqtt.subsceibe()`方法,`options`会传入`mqtt.connect()`方法,更加详细的配置参数请参考[文档](https://www.npmjs.com/package/mqtt#connect) 88 | 89 | 90 | 91 | 92 | ## 插件介绍 93 | 94 | 使用过一些mqtt插件,发现并不能满足业务开发的需求。有的插件在生产环境和多进程环境下无法使用,为了能够在多进程下使用mqtt客户端并且能够有一个比较舒服的使用方式。 95 | 96 | 为了在多进程下使用mqtt,根据egg官方文档的建议,长连接的维护一般放在agent进程中。所以`egg-mqtt-plugin`插件基于`mqtt.js`在agent进程中维护长连接,并且由agent进程来完成发布和订阅事件,通过进程间的通讯由agent进程将订阅的消息内容随机的发送到一个work进程,work进程将需要发布的消息发送给agent进程。agent进程不参与具体的业务逻辑,只维护长连接和监听发布订阅事件。 97 | 98 | mqtt实例运行在agent进程上,所以在work进程上无法对mqtt实例进行操作,`egg-mqtt-plugin`提供了统一数据出口(需要开启`DataBus`)和Mqtt类。 99 | 100 | 101 | ## 插件的使用 102 | 103 | > 插件得到publish、subscribe、unsubscribe等方法建议在agent.js或者在一个http请求的生命周期(controller、service、middleware、route)中使用。如果在非agent进程和一个http请求的生命周期中使用,多进程 104 | 环境下会导致每个worker进程都调用一次,从而导致了重复调用。这个问题会在后续版本中优化,可以在任何地方调用并且不会导致重复调用的情况。 105 | 106 | 获取订阅消息的message()方法不会多次触发,可以在任何地方使用。 107 | 108 | ### 建立链接 109 | 110 | 开启插件并填写好相关配置,在框架启动的时候时候会自动进行mqtt链接。 111 | 112 | ### 发布 113 | 114 | > 因为多进程的原因最好在一个http请求的生命周期(controller、service、middleware、route)中或者agent中调用,如果其他地方使用会出现重复发布的情况。 115 | 116 | 消息的发布需要使用Mqtt实例,调用Mqtt实例的`publish()`方法即可。 117 | 118 | Mqtt类由 'egg-mqtt-start/bootstrap.js'文件暴露出来,实例化Mqtt的时候需要使用`app`作为参数。 119 | 120 | publish()方法需要传入一个对象参数,其中包括topic、message、options等信息,更详细的参数可以参考[mqtt.publish()文档](https://www.npmjs.com/package/mqtt#connect)。 121 | 122 | ```js 123 | 'use strict'; 124 | 125 | const Mqtt = require('egg-mqtt-start/bootstrap'); 126 | 127 | const mqtt = new Mqtt(this.app); 128 | 129 | const topic = 'xxx-xxx-xxx'; 130 | const message = '这是我要发布的信息'; 131 | const options = { qos: 0 }; 132 | 133 | mqtt.publish(topic, message, options, err => { 134 | console.log('发布完成'); 135 | }); 136 | 137 | ``` 138 | 139 | ### 订阅 140 | 141 | > 因为多进程的原因最好在一个http请求的生命周期(controller、service、middleware、route)中或者agent中调用,如果其他地方使用会出现重复订阅的情况。 142 | 143 | 订阅有两种方式,一种是通过config.default.js配置文件中`topics`参数进行配置,另一种是手动调用`subscribe()`方法。更详细的参数可以参考[mqtt.subscribe()文档](https://www.npmjs.com/package/mqtt#connect)。 144 | 145 | 146 | ```js 147 | 'use strict'; 148 | 149 | const Mqtt = require('egg-mqtt-start/bootstrap'); 150 | 151 | const mqtt = new Mqtt(this.app); 152 | 153 | const topic = 'xxx-xxx-xxx'; 154 | const options = { qos: 0 }; 155 | 156 | mqtt.subscribe(topic, options, (err, granted) => { 157 | console.log(granted.topic); 158 | }); 159 | 160 | ``` 161 | 162 | 163 | ### 取消订阅 164 | 165 | 取消订阅直接调用`Mqtt`类的`unsubscribe()`方法就可以了。 166 | 167 | ```js 168 | const Mqtt = require('egg-mqtt-start/bootstrap'); 169 | 170 | const mqtt = new Mqtt(this.app); 171 | 172 | const topic = 'xxx-xxx-xxx'; 173 | const options = { qos: 0 }; 174 | 175 | mqtt.unsubscribe(topic, options, err => { 176 | console.log('取消订阅'); 177 | });; 178 | 179 | ``` 180 | 181 | ### 监听订阅消息 182 | 183 | 获取订阅的消息有两种方式,一种是开启`config.default.js`配置文件中`DataBus:true`选项(默认开启),订阅的所有消息和`app`都会统一的被发送到`/app/mqtt/DataBus.js`文件中, 184 | 开启此功能后`DataBus.js`文件会在项目启动后自动创建,也可以自己手动创建,格式如下例子: 185 | 186 | ```js 187 | 'use strict'; 188 | 189 | // /app/mqtt/DataBus.js 190 | 191 | module.exports = async (app, data) => { 192 | console.log('收到数据'); 193 | console.log(data.topic); 194 | }; 195 | 196 | ``` 197 | 198 | 另一种获取订阅的消息的方式是调用`message()`方法,需要传入一个回调函数,插件会将`topic`和相应的消息信息传入回调函数的参数中。 199 | 200 | ```js 201 | 202 | const Mqtt = require('egg-mqtt-plugin/bootstrap'); 203 | 204 | const mqtt = new Mqtt(this.app); 205 | 206 | mqtt.message((topic, message) => { 207 | console.log(topic); 208 | }) 209 | 210 | ``` 211 | 212 | 213 | ## Mqtt类 214 | 215 | `egg-mqtt-plugin`是基于`mqtt.js`进行封装并且是在`agent`进程上进行实例化的,所以在我们的业务代码中,无法使用`mqtt.js`实例提供的相应的方法,为了保证正常的时候用, 216 | `egg-mqtt-plugin`通过`bootstrap.js`文件导出一个`Mqtt`类,在这个类中通过进程间通讯间接的实现了`mqtt.js`实例上的部分方法。目前的版本暂时没有显示部分方法的回调函数功能。 217 | 218 | ```js 219 | 220 | const Mqtt = require('egg-mqtt-plugin/bootstrap'); 221 | 222 | // 获取mqtt实例的时候需要传入当前环境下的app对象 223 | cosnt mqtt = new Mqtt(this.app); 224 | 225 | // 发布 226 | mqtt.publish('topic', '我是发布的消息体', { qos: 0 }, callback); 227 | 228 | // 订阅 229 | mqtt.subscribe('topic', { qos: 0 }, callback); 230 | 231 | // 取消订阅 232 | mqtt.unsubscribe('topic', { qos: 0 }, callback); 233 | 234 | // 监听消息 235 | mqtt.message((topic, message) => { 236 | console.log(topic); 237 | }); 238 | 239 | 240 | ``` 241 | 242 | 243 | ## 示例 244 | 245 | 246 | ## 提问交流 247 | 248 | 请到 [egg issues](https://github.com/lp-liupan/egg-mqtt-plugin/issues) 异步交流。 249 | 250 | ## License 251 | 252 | [MIT](LICENSE) 253 | -------------------------------------------------------------------------------- /agent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const mqttClient = require('./lib/mqttClient'); 5 | 6 | class AppBootHook { 7 | 8 | constructor(agent) { 9 | this.agent = agent; 10 | } 11 | 12 | 13 | async didLoad() { 14 | // 建立mqtt链接 15 | this.agent.mqtt = mqttClient(this.agent.config.mqtt, this.agent); 16 | } 17 | 18 | 19 | async serverDidReady() { 20 | 21 | // 通过配置直接订阅 22 | if (this.agent.config.mqtt.topics) { 23 | await this.agent.mqtt.subscribe({ ...this.agent.config.mqtt.topics }, (err, granted) => { 24 | if (err) { 25 | this.agent.coreLogger.error('[egg-mqtt-plugin] subscribe err : %s', err); 26 | return; 27 | } 28 | granted.forEach(element => { 29 | this.agent.logger.info('[egg-mqtt-plugin] subscribe topic: %s success!', element.topic); 30 | }); 31 | }); 32 | } 33 | 34 | 35 | // 检查是否需要统一的数据出口,需要存在指定的文件,不存在就创建 36 | if (this.agent.config.mqtt.DataBus && !fs.existsSync('./app/mqtt/DataBus.js')) { 37 | if (!fs.existsSync('./app/mqtt')) { 38 | fs.mkdirSync('./app/mqtt'); 39 | } 40 | const DataBus = fs.readFileSync('./node_modules/egg-mqtt-plugin/lib/DataBus.js', 'utf8'); 41 | fs.writeFileSync('./app/mqtt/DataBus.js', DataBus); 42 | } 43 | 44 | 45 | this.agent.mqtt.on('message', (topic, message) => { 46 | // 向worker进程发送数据 47 | this.agent.messenger.sendRandom('mqtt-subscribe', { 48 | topic, 49 | message: message.toString(), 50 | }); 51 | }); 52 | 53 | // 注册mqtt的publish事件 54 | this.agent.messenger.on('mqtt-publish', data => { 55 | this.agent.mqtt.publish(data.topic, data.message, data.options, err => { 56 | this.agent.coreLogger.error('[egg-mqtt-plugin] publish error : %s', err); 57 | this.agent.messenger.sendTo(data.pid, data.action, err); 58 | }); 59 | }); 60 | 61 | // 注册mqtt的subscribe事件 62 | this.agent.messenger.on('mqtt-subscribe', data => { 63 | this.agent.mqtt.subscribe(data.topic, data.options, (err, granted) => { 64 | if (err) { 65 | this.agent.coreLogger.error('[egg-mqtt-plugin] subscribe error : %s', err); 66 | return; 67 | } 68 | this.agent.coreLogger.info('[egg-mqtt-plugin] subscribe succese : %s', granted); 69 | this.agent.messenger.sendTo(data.pid, data.action, { err, granted }); 70 | }); 71 | }); 72 | 73 | // 注册mqtt的unsubscribe事件 74 | this.agent.messenger.on('mqtt-unsubscribe', data => { 75 | this.agent.mqtt.unsubscribe(data.topic, data.options, err => { 76 | this.agent.coreLogger.error('[egg-mqtt-plugin] unsubscribe error : %s', err); 77 | this.agent.messenger.sendTo(data.pid, data.action, err); 78 | }); 79 | }); 80 | } 81 | 82 | 83 | } 84 | 85 | module.exports = AppBootHook; 86 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | class AppBootHook { 6 | constructor(app) { 7 | this.app = app; 8 | 9 | } 10 | 11 | async serverDidReady() { 12 | 13 | if (this.app.config.mqtt.DataBus) { 14 | const src = path.join(path.resolve('./'), '/app/mqtt/DataBus.js'); 15 | this.app.messenger.on('mqtt-subscribe', data => { 16 | this.app.coreLogger.info('[egg-mqtt-plugin] send to worker DataBus success!'); 17 | (require(src))(this.app, data); 18 | }); 19 | } 20 | 21 | } 22 | } 23 | 24 | module.exports = AppBootHook; 25 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '8' 4 | - nodejs_version: '10' 5 | 6 | install: 7 | - ps: Install-Product node $env:nodejs_version 8 | - npm i npminstall && node_modules\.bin\npminstall 9 | 10 | test_script: 11 | - node --version 12 | - npm --version 13 | - npm run test 14 | 15 | build: off 16 | -------------------------------------------------------------------------------- /bootstrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { v4 } = require('uuid'); 4 | 5 | class Mqtt { 6 | 7 | constructor(app) { 8 | this.app = app; 9 | this.pid = process.pid; 10 | } 11 | 12 | 13 | publish(topic, message, options, callback) { 14 | const action = `mqtt-publish:${v4()}`; 15 | const data = { topic, message, options, action, pid: this.pid }; 16 | this.action('mqtt-publish', data, callback, action); 17 | } 18 | 19 | subscribe(topic, options, callback) { 20 | const action = `mqtt-subscribe:${v4()}`; 21 | const data = { topic, options, action, pid: this.pid }; 22 | this.action('mqtt-subscribe', data, callback, action); 23 | } 24 | 25 | unsubscribe(topic, options, callback) { 26 | const action = `mqtt-unsubscribe:${v4()}`; 27 | const data = { topic, options, action, pid: this.pid }; 28 | this.action('mqtt-unsubscribe', data, callback, action); 29 | } 30 | 31 | message(callback) { 32 | this.app.messenger.on('mqtt-subscribe', data => { 33 | callback(data.topic, data.message); 34 | }); 35 | } 36 | 37 | 38 | /** 39 | * 进程间通讯 40 | * @param {string} name 41 | * @param {*} data 42 | * @param {function} callback 43 | * @param {string} action 44 | */ 45 | action(name, data, callback, action) { 46 | 47 | this.app.messenger.sendToAgent(name, data); 48 | 49 | // 提供回调函数 50 | this.app.messenger.once(action, data => { 51 | if (data && data.granted) { 52 | callback(data.err, data.granted); 53 | } else { 54 | callback(data); 55 | } 56 | }); 57 | } 58 | 59 | } 60 | 61 | module.exports = Mqtt; 62 | -------------------------------------------------------------------------------- /config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * egg-mqtt-plugin default config 5 | * @member Config#mqtt 6 | * @property {String} SOME_KEY - some description 7 | */ 8 | exports.mqtt = { 9 | host: 'mqtt://xxx.xxx.x.x', 10 | port: 1883, 11 | username: 'username', 12 | password: 'password', 13 | clientId: 'client_id', 14 | options: { 15 | keeplive: 60, 16 | protocolId: 'MQTT', 17 | protocol: 'MQTT', 18 | protocolVersion: 4, 19 | clean: true, 20 | rejectUnauthorized: false, 21 | reconnectPeriod: 1000, 22 | connectTimeout: 30 * 1000, 23 | }, 24 | 25 | DataBus: true, 26 | 27 | // topics: { 28 | // 'topic-topic-topic': { qos: 0 }, 29 | // }, 30 | }; 31 | -------------------------------------------------------------------------------- /lib/DataBus.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = async (app, data) => { 4 | console.log('收到数据'); 5 | console.log(data.topic); 6 | }; 7 | -------------------------------------------------------------------------------- /lib/mqttClient.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mqtt = require('mqtt'); 4 | 5 | module.exports = config => { 6 | const mqttClient = mqtt.connect(config.host, { 7 | host: config.host, 8 | port: config.port, 9 | username: config.username, 10 | password: config.password, 11 | clientId: config.clientId, 12 | ...config.options, 13 | }); 14 | return mqttClient; 15 | }; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "egg-mqtt-plugin", 3 | "version": "1.1.1", 4 | "description": "mqtt-client for egg. 使用mqtt包封装的egg客户端插件", 5 | "private": false, 6 | "eggPlugin": { 7 | "name": "mqtt" 8 | }, 9 | "keywords": [ 10 | "mqtt", 11 | "egg-mqtt", 12 | "egg-mqtt-plugin", 13 | "egg", 14 | "eggPlugin", 15 | "egg-plugin" 16 | ], 17 | "dependencies": { 18 | "mqtt": "^4.1.0", 19 | "uuid": "^8.3.0" 20 | }, 21 | "devDependencies": { 22 | "autod": "^3.0.1", 23 | "autod-egg": "^1.1.0", 24 | "egg": "^2.16.0", 25 | "egg-bin": "^4.11.0", 26 | "egg-ci": "^1.11.0", 27 | "egg-mock": "^3.21.0", 28 | "eslint": "^5.13.0", 29 | "eslint-config-egg": "^7.1.0" 30 | }, 31 | "engines": { 32 | "node": ">=8.0.0" 33 | }, 34 | "scripts": { 35 | "test": "npm run lint -- --fix && egg-bin pkgfiles && npm run test-local", 36 | "test-local": "egg-bin test", 37 | "cov": "egg-bin cov", 38 | "lint": "eslint .", 39 | "ci": "egg-bin pkgfiles --check && npm run lint && npm run cov", 40 | "pkgfiles": "egg-bin pkgfiles", 41 | "autod": "autod" 42 | }, 43 | "files": [ 44 | "config", 45 | "agent.js", 46 | "lib", 47 | "app.js", 48 | "bootstrap.js" 49 | ], 50 | "ci": { 51 | "version": "8, 10" 52 | }, 53 | "repository": { 54 | "type": "git", 55 | "url": "git+https://github.com/lp-liupan/egg-mqtt-plugin.git" 56 | }, 57 | "bugs": { 58 | "url": "https://github.com/lp-liupan/egg-mqtt-plugin/issues" 59 | }, 60 | "homepage": "https://github.com/lp-liupan/egg-mqtt-plugin", 61 | "author": "liupan", 62 | "license": "MIT" 63 | } -------------------------------------------------------------------------------- /test/fixtures/apps/mqtt-plugin-test/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class HomeController extends Controller { 6 | async index() { 7 | this.ctx.body = 'hi, ' + this.app.plugins.mqtt.name; 8 | } 9 | } 10 | 11 | module.exports = HomeController; 12 | -------------------------------------------------------------------------------- /test/fixtures/apps/mqtt-plugin-test/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | const { router, controller } = app; 5 | 6 | router.get('/', controller.home.index); 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/apps/mqtt-plugin-test/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123456'; 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/mqtt-plugin-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mqtt-plugin-test", 3 | "version": "0.0.1" 4 | } -------------------------------------------------------------------------------- /test/mqtt-plugin.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mock = require('egg-mock'); 4 | 5 | describe('test/mqtt-plugin.test.js', () => { 6 | let app; 7 | before(() => { 8 | app = mock.app({ 9 | baseDir: 'apps/mqtt-plugin-test', 10 | }); 11 | return app.ready(); 12 | }); 13 | 14 | after(() => app.close()); 15 | afterEach(mock.restore); 16 | 17 | it('should GET /', () => { 18 | return app.httpRequest() 19 | .get('/') 20 | .expect('hi, mqtt') 21 | .expect(200); 22 | }); 23 | }); 24 | --------------------------------------------------------------------------------