This protocol is used to communicate between the Moleculer nodes.
After the client is connected to the message broker (NATS, Redis, MQTT), it subscribes to the following topics:
| Type | Topic name |
|---|---|
| Event | MOL.EVENT.<nodeID> |
| Event (balanced) | MOL.EVENTB.<event> |
| Request | MOL.REQ.<nodeID> |
| Request (balanced) | MOL.REQB.<action> |
| Response | MOL.RES.<nodeID> |
| Discover | MOL.DISCOVER |
| Discover (targeted) | MOL.DISCOVER.<nodeID> |
| Info | MOL.INFO |
| Info (targeted) | MOL.INFO.<nodeID> |
| Heartbeat | MOL.HEARTBEAT |
| Ping | MOL.PING |
| Ping (targeted) | MOL.PING.<nodeID> |
| Pong | MOL.PONG.<nodeID> |
| Disconnect | MOL.DISCONNECT |
If
namespaceis defined, the topic prefix isMOL-<namespace>instead ofMOL. E.g.:MOL-dev.EVENTwhen the namespace isdev.
Variables in topic names:
<namespace>- Namespace from broker options<nodeID>- Target nodeID<action>- Action name. E.g.:posts.find<group>- Event group name. E.g.:users<event>- Event name. E.g.:user.created
After subscribing, the transporter broadcasts a DISCOVER packet. In response to this, all connected nodes send back INFO packet to the sender node. From these responses, the client builds its own service registry. At last, the client broadcasts own INFO packet to all other nodes.
The client has to broadcast HEARTBEAT packets periodically. The period value comes from broker options (heartbeatInterval). The default value is 5 secs.
If the client does not receive HEARTBEAT for heartbeatTimeout seconds from a node, marks it broken and doesn't route requests to this node.
When you call the broker.call method, the broker sends a REQUEST packet to the targeted node. It processes the request and sends back a RESPONSE packet to the requester node.
When you call the broker.emit method, the broker sends an EVENT packet to the subscriber nodes. The broker groups & balances the subscribers, so only one instance per service receives the event. If you call the broker.broadcast method, the broker sends an ĘVENT packet to all subscriber nodes. It doesn't balance the subscribers.
When you call the broker.ping method, the broker sends a PING packet to the targeted node. If node is not defined, it sends to all nodes. If the client receives the PING packet, sends back a PONG response packet. If it receives, broker broadcasts a local $node.pong event to the local services.
When a node is stopping, it broadcasts a DISCONNECT packet to all nodes.
When the transporter established the connection with the message broker, it broadcasts a DISCOVER message to all other nodes.
Topic name:
MOL.DISCOVER(if broadcasts)MOL.DISCOVER.node-1(if sent only tonode-1)MOL-dev.DISCOVER(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
Example with JSON serializer
{
"ver": "5",
"sender": "node-100"
}When the node receives an INFO packet, it sends an INFO packet which contains all mandatory information about the nodes and the loaded services.
Topic name:
MOL.INFO(if broadcasts)MOL.INFO.node-1(if sent only tonode-1)MOL-dev.INFO(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
services |
object |
✔ | Services list. (*) |
config |
object |
✔ | Client configuration. (*) |
instanceID |
string |
✔ | Instance ID |
ipList |
[string] |
✔ | IP address list of node |
hostname |
string |
✔ | Hostname of node |
client |
object |
✔ | Client information |
client.type |
string |
✔ | Type of client implementation(nodejs, java, go) |
client.version |
string |
✔ | Client (Moleculer) version |
client.langVersion |
string |
✔ | NodeJS/Java/Go version |
metadata |
object |
✔ | Node-specific metadata. (*) |
(*) In case of schema-based serializers, the field value is encoded to JSON string.
Example with JSON serializer
{
"services": [
{
"name": "$node",
"settings": {},
"metadata": {},
"actions": [],
"events": {}
},
{
"name": "greeter",
"settings": {},
"metadata": {},
"actions": [],
"events": {}
}
],
"ipList": ["10.35.0.34"],
"hostname": "moleculer-server",
"client": {
"type": "nodejs",
"version": "0.14.0-beta3",
"langVersion": "v12.10.0"
},
"config": {},
"instanceID": "ee21e97d-9fd0-4d7e-a303-70b1605f477f",
"metadata": {},
"seq": 2,
"ver": "5",
"sender": "nodeID-1"
}Topic name:
MOL.HEARTBEATMOL-dev.HEARTBEAT(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
cpu |
double |
✔ | Current CPU utilization (percentage). |
Example with JSON serializer
{
"ver": "5",
"sender": "node-100",
"cpu": 13.5
}Topic name:
MOL.REQ.node-2MOL.REQB.<action>(if built-in balancer is disabled)MOL-dev.REQ.node-2(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
id |
string |
✔ | Context ID. |
action |
string |
✔ | Action name. E.g.: posts.find |
params |
object |
ctx.params object. |
|
meta |
object |
✔ | ctx.meta object. |
headers |
object |
headers in calling options. |
|
timeout |
double |
✔ | Request timeout (distributed) in milliseconds. |
level |
int32 |
✔ | Level of request. |
tracing |
boolean |
✔ | Need to send tracing events. |
parentID |
string |
Parent context ID. | |
requestID |
string |
Request ID from ctx.requestID. |
|
caller |
string |
Action name of the caller. | |
stream |
boolean |
✔ | Stream request. |
seq |
int32 |
Stream sequence number. |
Example with JSON serializer
{
"id": "41238213-da6b-4313-9909-e6edd0e40a96",
"action": "greeter.hello",
"params": {},
"meta": {},
"headers": {},
"timeout": 10000,
"level": 1,
"tracing": null,
"parentID": null,
"requestID": "41238213-da6b-4313-9909-e6edd0e40a96",
"caller": null,
"stream": false,
"ver": "5",
"sender": "nodeID-1"
}Topic name:
MOL.RES.node-1MOL-dev.RES.node-1(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
id |
string |
✔ | Context ID (from REQUEST). |
success |
boolean |
✔ | Is it a success response? |
data |
object |
Response data if success. | |
error |
object |
Error object if not success. | |
meta |
object |
✔ | ctx.meta object. |
headers |
object |
ctx.responseHeaders object |
|
stream |
boolean |
✔ | Stream request. |
seq |
int32 |
Stream sequence number. |
Example with JSON serializer
{
"id": "a4dd3ae5-2eff-4924-94bc-4acb8ac034aa",
"meta": {},
"headers": {},
"success": true,
"data": {
"message": "Hello Moleculer"
},
"ver": "5",
"sender": "nodeID-2"
}Topic name:
MOL.EVENT.node-1MOL.EVENTB.<group>.<event>(if built-in balancer is disabled)MOL-dev.EVENT.node-1(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
id |
string |
✔ | Context ID. |
event |
string |
✔ | Event name. E.g.: users.created |
data |
object |
Event payload. | |
meta |
object |
✔ | ctx.meta object. |
headers |
object |
headers in event options |
|
level |
int32 |
✔ | Level of event. |
tracing |
boolean |
✔ | Need to send tracing events. |
parentID |
string |
Parent context ID. | |
requestID |
string |
Request ID from ctx.requestID. |
|
caller |
string |
Action/Event name of the caller. | |
stream |
boolean |
✔ | Stream request. |
seq |
int32 |
Stream sequence number. | |
groups |
Array<string> |
Groups for balanced events. | |
broadcast |
boolean |
✔ | Broadcast event |
Example with JSON serializer
{
"id": "e102630b-c702-4ff9-a0a1-52428395d57a",
"event": "some.test",
"data": {
"name": "John"
},
"groups": ["greeter"],
"broadcast": false,
"meta": {},
"headers": {},
"level": 1,
"tracing": null,
"parentID": null,
"requestID": "e102630b-c702-4ff9-a0a1-52428395d57a",
"caller": null,
"needAck": null,
"ver": "5",
"sender": "nodeID-1"
}Not implemented yet.
Topic name:
MOL.EVENTACK.node-1MOL-dev.EVENTACK(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
id |
string |
✔ | Event Context ID. |
success |
boolean |
✔ | Is it successful? |
group |
string |
Group of event handler. | |
error |
object |
Error object if not success. (*) |
(*) In case of schema-based serializers, the field value is encoded to JSON string.
Example with JSON serializer
{
"ver": "5",
"sender": "node-100"
}!!TODO!!
Topic name:
MOL.PING(if broadcasts)MOL.PING.node-1(if sent only tonode-1)MOL-dev.PING(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
id |
string |
✔ | Message ID. |
time |
int64 |
✔ | Time of sent. (*) |
(*) The number of milliseconds between 1 January 1970 00:00:00 UTC and the given date.
Example with JSON serializer
{
"time": 1567677050576,
"id": "3e09738f-cedf-4985-85fe-344860c06cfd",
"ver": "5",
"sender": "nodeID-2"
}Topic name:
MOL.PONG.node-1MOL-dev.PONG(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
id |
string |
✔ | Message ID. |
time |
int64 |
✔ | Timestamp of sent. (*) |
arrived |
int64 |
✔ | Timestamp of arrived. (*) |
(*) The number of milliseconds between 1 January 1970 00:00:00 UTC and the given date.
Example with JSON serializer
{
"time": 1567677050576,
"id": "3e09738f-cedf-4985-85fe-344860c06cfd",
"arrived": 1567677050577,
"ver": "5",
"sender": "nodeID-1"
}Topic name:
MOL.DISCONNECTMOL-dev.DISCONNECT(if namespace isdev)
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
ver |
string |
✔ | Protocol version. |
sender |
string |
✔ | Sender nodeID. |
Example with JSON serializer
{
"ver": "5",
"sender": "node-100"
}The broker starts by establishing connection with the transporter (e.g., NATS server, MQTT broker). After successfully establishing the connection, it starts all services, i.e., calls service started handlers. Once all services have started successfully, broker publishes the local service list to remote nodes. Hence remote nodes will send requests only after all local service have started properly.
Start lifecycle sequence
When broker is stopping, it starts by publishing an empty service list to all remote nodes. This is done to inform all remote nodes that the node and it's service will be shut down. Next, broker starts stopping all local services. After that, the transporter disconnects.
Stop lifecycle sequence
While transferring streams, sequence number (seq) field is used to keep track of the chunks and their order. This is especially important in "multi-threaded systems" that can shuffle the packets before sending them. If packets arrive unordered they are stored in a pool until previous chucks arrive.
The
seq = 0is the first initial package and it hasparams,headersandmetaproperties. This package doesn't contain stream data.
When built-in load balancing mechanisms are disabled, the balancing is done by the brokers (e.g., NATS, RabbitMQ). This means that there are no local calls in actions and events. All requests are transferred to transporter that will be responsible for choosing the destination of the request and delivery of the message.
Removed schema-based serializers (ProtoBuf, Avro, Thrift)
REQUEST
- removed
dataType: enumproperty - new
headersproperty
RESPONSE
- removed
dataType: enumproperty - new
headersproperty
EVENT
- removed
dataType: enumproperty - new
headersproperty





