mole-rpc-transport-mqtt
Advanced tools
Comparing version 0.3.0 to 1.0.0
@@ -5,2 +5,2 @@ { | ||
"singleQuote": true | ||
} | ||
} |
{ | ||
"name": "mole-rpc-transport-mqtt", | ||
"version": "0.3.0", | ||
"version": "1.0.0", | ||
"description": "MQTT transport for Mole-RPC (JSON RPC library)", | ||
@@ -14,8 +14,9 @@ "main": "index.js", | ||
"peerDependencies": { | ||
"mole-rpc": "^0.3.0", | ||
"async-mqtt": "^2.0.0" | ||
"mole-rpc": "1.*", | ||
"async-mqtt": "^2.5.0" | ||
}, | ||
"devDependencies": { | ||
"mole-rpc": "^0.3.0", | ||
"async-mqtt": "^2.0.0" | ||
"async-mqtt": "^2.5.0", | ||
"mole-rpc": "1.*", | ||
"mole-rpc-autotester": "1.*" | ||
}, | ||
@@ -43,2 +44,2 @@ "keywords": [ | ||
"homepage": "https://github.com/koorchik/node-mole-rpc-transport-mqtt#readme" | ||
} | ||
} |
184
README.md
MQTT Transport for Mole RPC (JSON RPC Library) | ||
--------------------------------------------- | ||
[![npm version](https://badge.fury.io/js/mole-rpc-transport-mqtt.svg)](https://badge.fury.io/js/mole-rpc-transport-mqtt) | ||
[![Build Status](https://travis-ci.org/koorchik/node-mole-rpc-transport-mqtt.svg?branch=master)](https://travis-ci.org/koorchik/node-mole-rpc-transport-mqtt) | ||
[![Known Vulnerabilities](https://snyk.io/test/github/koorchik/node-mole-rpc-transport-mqtt/badge.svg?targetFile=package.json)](https://snyk.io/test/github/koorchik/node-mole-rpc-transport-mqtt?targetFile=package.json) | ||
Mole-RPC is a tiny transport agnostic JSON-RPC 2.0 client and server which can work both in NodeJs, Browser, Electron etc. | ||
This is MQTT based tranport but there are [many more transports](https://www.npmjs.com/search?q=keywords:mole-transport). | ||
**What is the reason of using MQTT for JSON-RPC?** | ||
MQTT is a standard broker in IoT world. It is lightweight and fast message broker and the same time it contains a lot of advanced features like (persistence, retained messaged, QoS, ACL etc). | ||
You can organize many to many RPC-connunication between microservices via MQTT. | ||
*For example, in our case, we use this module to connect microservices deployed to an IoT Hub* | ||
**Which MQTT server to use?** | ||
You can use anyone you wish. As for us, we use in production: | ||
1. [Eclipse Mosquitto](https://mosquitto.org/). Very lightweight but works greatly. | ||
2. [EMQ X](https://www.emqx.io/). More powerful if you need more features. | ||
The easiest way to start playing is to run mosquitto in docker: | ||
```sh | ||
docker run -it -p 1883:1883 eclipse-mosquitto | ||
``` | ||
After that you can try the example below. | ||
## Usage Example | ||
```javascript | ||
@@ -13,71 +48,128 @@ const MoleClient = require('mole-rpc/MoleClient'); | ||
async function main() { | ||
await runServer(); | ||
await runClients(); | ||
await runServer(); | ||
await runClients(); | ||
} | ||
async function runServer() { | ||
const mqttClient = MQTT.connect("tcp://localhost:1883"); | ||
await waitForEvent(mqttClient, 'connect'); | ||
const mqttClient = MQTT.connect("tcp://localhost:1883"); | ||
const server = new MoleServer({ | ||
transports: [ | ||
new MQTTTransportServer({ | ||
mqttClient, | ||
inTopic: 'fromClient/+', | ||
outTopic: ({inTopic}) => inTopic.replace('fromClient', 'toClient') | ||
// outTopic: 'toClient/1' // static outTopic | ||
}) | ||
], | ||
}); | ||
server.expose({ | ||
getGreeting(name) { | ||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
resolve(`Hi, ${name}`) | ||
}, 1000); | ||
}); | ||
} | ||
}); | ||
} | ||
const server = new MoleServer({ | ||
transports: [ | ||
new MQTTTransportServer({ | ||
mqttClient, | ||
inTopic: 'fromClient/+', | ||
outTopic: ({inTopic}) => inTopic.replace('fromClient', 'toClient') | ||
}) | ||
], | ||
}); | ||
server.expose({ | ||
getGreeting(name) { | ||
return `Hi, ${name}`; | ||
} | ||
}); | ||
await server.run(); | ||
} | ||
async function runClients() { | ||
const mqttClient = MQTT.connect("tcp://localhost:1883"); | ||
await waitForEvent(mqttClient, 'connect'); | ||
const client1 = new MoleClient({ | ||
transport: new MQTTTransportClient({ | ||
mqttClient, | ||
inTopic: 'toClient/1', | ||
outTopic: 'fromClient/1' | ||
}), | ||
transport: new MQTTTransportClient({ | ||
mqttClient, | ||
inTopic: 'toClient/1', | ||
outTopic: 'fromClient/1' | ||
}), | ||
}); | ||
const client2 = new MoleClient({ | ||
transport: new MQTTTransportClient({ | ||
mqttClient, | ||
inTopic: 'toClient/2', | ||
outTopic: 'fromClient/2' | ||
}), | ||
transport: new MQTTTransportClient({ | ||
mqttClient, | ||
inTopic: 'toClient/2', | ||
outTopic: 'fromClient/2' | ||
}), | ||
}); | ||
console.log( | ||
'CLIENT 1', | ||
await client1.callMethod('getGreeting', ['User1']) | ||
'CLIENT 1', | ||
await client1.callMethod('getGreeting', ['User1']) | ||
); | ||
console.log( | ||
'CLIENT 2', | ||
await client2.callMethod('getGreeting', ['User2']) | ||
'CLIENT 2', | ||
await client2.callMethod('getGreeting', ['User2']) | ||
); | ||
} | ||
function waitForEvent(emitter, eventName) { | ||
return new Promise((resolve, reject) => { | ||
emitter.on(eventName, resolve); | ||
}); | ||
main(console.log, console.error); | ||
``` | ||
## How does it work? | ||
MQTT has topics. Different services can subscribe to different topic. You can use wildcard characters in names like "+" or "#". | ||
**MQTTTransportClient** has two topic related parameters: | ||
* outTopic - sends request to this topic. | ||
* inTopic - gets response in this topic. | ||
**MQTTTransportServer** has two topic related parameters: | ||
* inTopic - listend to requests from client in this topic. If you have many clients it makes sense use wildcard topic. | ||
* outTopic - sends response to this topic. You can pass callback which will convert in topic to outtopic. | ||
## Universal approach for to how to name topics in scalable way | ||
One of the pattern can be the following: | ||
* Send data to: `/rpc/${FROM}/${TO}` | ||
* Get data from: `/rpc/${TO}/${FROM}` | ||
*See "Usage Example" for a simpler approach* | ||
### Many RPC clients and one RPC Server. | ||
Let's assume that is an authentication server | ||
**Server:** | ||
```js | ||
const inTopic = '/rpc/+/auth-server'; | ||
const outTopic = inTopicToOutTopic; | ||
function inTopicToOutTopic({ inTopic }) { | ||
const [ namespace, from, to ] = inTopic.split('/'); | ||
const outTopic = `/${namespace}/${to}/${from}`; | ||
return outTopic; | ||
} | ||
``` | ||
main(); | ||
``` | ||
**Client** | ||
```js | ||
const clientId = 'client123'; // you can use UUID for automatic id generation. | ||
const inTopic = `/rpc/auth-server/${clientId}`; | ||
const outTopic = `/rpc/${clientId}/auth-server` | ||
``` | ||
So, for each clients connection to server you will have a pair of topics. | ||
It looks a little bit more complecated but allows easely switch to many-to-many connection apprach. | ||
### Many-to-many connections | ||
For this case, you can use the same approach as for "Many RPC clients and one RPC Server" case. | ||
1. You run every service as an Mole RPC server. | ||
2. You use instantiate Mole RPC clients with corresponding topics. | ||
You can notive that if SERVICE_A talks to SEVICE_B we need 2 topics. But when SERVICE_B talks to SERVICE_A we will use the same topic names and that is ok. This transport handles this situation, so you can use understandable topics which always follow this pattern: | ||
* Always send data to: `/rpc/${FROM}/${TO}` | ||
* Always get data from: `/rpc/${TO}/${FROM}` | ||
This is the approach we use for many-to-many communication approach but you can use other approaces. | ||
For example, if you want to allow SERVICE_A to get request only from SERVICE_B, you can use inTopic name without wildcard - `/rpc/SERVICE_B/SERVICE_A` (outTopic can be set to static value `/rpc/SERVICE_A/SERVICE_B` as well ). |
@@ -5,3 +5,4 @@ const MQTT = require('async-mqtt'); | ||
const MoleServer = require('mole-rpc/MoleServer'); | ||
const AutoTester = require('mole-rpc/AutoTester'); | ||
const X = require('mole-rpc/X'); | ||
const AutoTester = require('mole-rpc-autotester'); | ||
@@ -11,2 +12,4 @@ const TransportClient = require('../TransportClient'); | ||
const MQTT_ENDPOINT = 'tcp://test.mosquitto.org:1883'; // "tcp://localhost:1883" for local testing | ||
async function main() { | ||
@@ -17,2 +20,3 @@ const server = await prepareServer(); | ||
const autoTester = new AutoTester({ | ||
X, | ||
server, | ||
@@ -27,4 +31,3 @@ simpleClient: clients.simpleClient, | ||
async function prepareServer() { | ||
const mqttClient = MQTT.connect('tcp://localhost:1883'); | ||
await waitForEvent(mqttClient, 'connect'); | ||
const mqttClient = MQTT.connect(MQTT_ENDPOINT); | ||
@@ -43,4 +46,3 @@ return new MoleServer({ | ||
async function prepareClients() { | ||
const mqttClient = MQTT.connect('tcp://localhost:1883'); | ||
await waitForEvent(mqttClient, 'connect'); | ||
const mqttClient = MQTT.connect(MQTT_ENDPOINT); | ||
@@ -68,10 +70,4 @@ const simpleClient = new MoleClient({ | ||
function waitForEvent(emitter, eventName) { | ||
return new Promise((resolve, reject) => { | ||
emitter.on(eventName, resolve); | ||
}); | ||
} | ||
main().then(() => { | ||
process.exit(); | ||
}, console.error); |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
13181
10
153
0
174
3