chore(gw): update README files

This commit is contained in:
JianBo He 2023-04-03 14:30:41 +08:00
parent 5138e6371c
commit 205e97fdca
4 changed files with 98 additions and 902 deletions

View File

@ -1,443 +1,31 @@
# emqx_coap
# Table of Contents The CoAP gateway implements publish, subscribe, and receive messages as standard
with [Publish-Subscribe Broker for the CoAP](https://datatracker.ietf.org/doc/html/draft-ietf-core-coap-pubsub-09).
1. [EMQX 5.0 CoAP Gateway](#org61e5bb8) ## Quick Start
1. [Features](#orgeddbc94)
1. [PubSub Handler](#orgfc7be2d)
2. [MQTT Handler](#org55be508)
3. [Heartbeat](#org3d1a32e)
4. [Query String](#org9a6b996)
2. [Implementation](#org9985dfe)
1. [Request/Response flow](#orge94210c)
3. [Example](#ref_example)
In EMQX 5.0, CoAP gateways can be configured and enabled through the Dashboard.
It can also be enabled via the HTTP API or emqx.conf, e.g. In emqx.conf:
<a id="org61e5bb8"></a> ```properties
gateway.coap {
# EMQX 5.0 CoAP Gateway mountpoint = "coap/"
emqx-coap is a CoAP Gateway for EMQX. It translates CoAP messages into MQTT messages and make it possible to communiate between CoAP clients and MQTT clients. connection_required = false
listeners.udp.default {
<a id="orgeddbc94"></a> bind = "5683"
max_connections = 1024000
## Features max_conn_rate = 1000
}
- Partially achieves [Publish-Subscribe Broker for the Constrained Application Protocol (CoAP)](https://datatracker.ietf.org/doc/html/draft-ietf-core-coap-pubsub-09) }
we called this as ps handler, include following functions:
- Publish
- Subscribe
- UnSubscribe
- Long connection and authorization verification called as MQTT handler
<a id="orgfc7be2d"></a>
### PubSub Handler
1. Publish
Method: POST\
URI Schema: ps/{+topic}{?q\*}\
q\*: [Shared Options](#orgc50043b)\
Response:
- 2.04 "Changed" when success
- 4.00 "Bad Request" when error
- 4.01 "Unauthorized" when with wrong auth uri query
2. Subscribe
Method: GET
Options:
- Observer = 0
URI Schema: ps/{+topic}{?q\*}\
q\*: see [Shared Options](#orgc50043b)\
Response:
- 2.05 "Content" when success
- 4.00 "Bad Request" when error
- 4.01 "Unauthorized" when with wrong auth uri query
```
Client1 Client2 Broker
| | Subscribe |
| | ----- GET /ps/topic1 Observe:0 Token:XX ----> |
| | |
| | <---------- 2.05 Content Observe:10---------- |
| | |
| | |
| | Publish |
| ---------|----------- PUT /ps/topic1 "1033.3" --------> |
| | Notify |
| | <---------- 2.05 Content Observe:11 --------- |
| | |
``` ```
3. UnSubscribe > Note:
> Configuring the gateway via emqx.conf requires changes on a per-node basis,
Method : GET > but configuring it via Dashboard or the HTTP API will take effect across the cluster.
Options:
- Observe = 1
URI Schema: ps/{+topic}{?q\*}\
q\*: see [Shared Options](#orgc50043b)\
Response:
- 2.07 "No Content" when success
- 4.00 "Bad Request" when error
- 4.01 "Unauthorized" when with wrong auth uri query
<a id="org55be508"></a>
### MQTT Handler
Establishing a connection is optional. If the CoAP client needs to use connection-based operations, it must first establish a connection.
At the same time, the connectionless mode and the connected mode cannot be mixed.
In connection mode, the Publish/Subscribe/UnSubscribe sent by the client must be has Token and ClientId in query string.
If the Token and Clientid is wrong/miss, EMQX will reset the request.
The communication token is the data carried in the response payload after the client successfully establishes a connection.
After obtaining the token, the client's subsequent request must attach "token=Token" to the Query String
ClientId is necessary when there is a connection, and is a unique identifier defined by the client.
The server manages the client through the ClientId. If the ClientId is wrong, EMQX will reset the request.
1. Create a Connection
Method: POST
URI Schema: mqtt/connection{?q\*}
q\*:
- clientid := client uid
- username
- password
Response:
- 2.01 "Created" when success
- 4.00 "Bad Request" when error
- 4.01 "Unauthorized" wrong username or password
Payload: Token if success
2. Close a Connection
Method : DELETE
URI Schema: mqtt/connection{?q\*}
q\*:
- clientid := client uid
- token
Response:
- 2.01 "Deleted" when success
- 4.00 "Bad Request" when error
- 4.01 "Unauthorized" wrong clientid or token
<a id="org3d1a32e"></a>
### Heartbeat
The Coap client can maintain the "connection" with the server through the heartbeat,
regardless of whether it is authenticated or not,
so that the server will not release related resources
Method : PUT
URI Schema: mqtt/connection{?q\*}
q\*:
- clientid if authenticated
- token if authenticated
Response:
- 2.01 "Changed" when success
- 4.00 "Bad Request" when error
- 4.01 "Unauthorized" wrong clientid or token
<a id="org9a6b996"></a>
### Query String
CoAP gateway uses some options in query string to conversion between MQTT CoAP.
1. Shared Options <a id="orgc50043b"></a>
- clientid
- token
2. Connect Options
- username
- password
3. Publish
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-left" />
<col class="org-left" />
</colgroup>
<thead>
<tr>
<th scope="col" class="org-left">Option</th>
<th scope="col" class="org-left">Type</th>
<th scope="col" class="org-left">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="org-left">retain</td>
<td class="org-left">boolean</td>
<td class="org-left">false</td>
</tr>
<tr>
<td class="org-left">qos</td>
<td class="org-left">MQTT Qos</td>
<td class="org-left">See <a href="#org0345c3e">here</a></td>
</tr>
<tr>
<td class="org-left">expiry</td>
<td class="org-left">Message Expiry Interval</td>
<td class="org-left">0(Never expiry)</td>
</tr>
</tbody>
</table>
4. Subscribe
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-left" />
<col class="org-right" />
</colgroup>
<thead>
<tr>
<th scope="col" class="org-left">Option</th>
<th scope="col" class="org-left">Type</th>
<th scope="col" class="org-right">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="org-left">qos</td>
<td class="org-left">MQTT Qos</td>
<td class="org-right">See <a href="#org2325c7d">here</a></td>
</tr>
<tr>
<td class="org-left">nl</td>
<td class="org-left">MQTT Subscribe No Local</td>
<td class="org-right">0</td>
</tr>
<tr>
<td class="org-left">rh</td>
<td class="org-left">MQTT Subscribe Retain Handing</td>
<td class="org-right">0</td>
</tr>
</tbody>
</table>
5. MQTT Qos <=> CoAP non/con
1.notif_type
Control the type of notify messages when the observed object has changed.Can be:
- non
- con
- qos
in this value, MQTT Qos0 -> non, Qos1/Qos2 -> con
2.subscribe_qos <a id="org2325c7d"></a>
Control the qos of subscribe.Can be:
- qos0
- qos1
- qos2
- coap
in this value, CoAP non -> qos0, con -> qos1
3.publish_qos <a id="org0345c3e"></a>
like subscribe_qos, but control the qos of the publish MQTT message
<a id="org9985dfe"></a>
## Implementation
<a id="orge94210c"></a>
### Request/Response flow
![img](./doc/flow.png)
1. Authorization check
Check whether the clientid and token in the query string match the current connection
2. Session
Manager the "Transport Manager" "Observe Resources Manager" and next message id
3. Transport Mnager
Manager "Transport" create/close/dispatch
4. Observe resources Mnager
Mnager observe topic and token
5. Transport
![img](./doc/transport.png)
1. Shared State
![img](./doc/shared_state.png)
6. Handler
1. pubsub
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-right" />
<col class="org-left" />
</colgroup>
<thead>
<tr>
<th scope="col" class="org-left">Method</th>
<th scope="col" class="org-right">Observe</th>
<th scope="col" class="org-left">Action</th>
</tr>
</thead>
<tbody>
<tr>
<td class="org-left">GET</td>
<td class="org-right">0</td>
<td class="org-left">subscribe and reply result</td>
</tr>
<tr>
<td class="org-left">GET</td>
<td class="org-right">1</td>
<td class="org-left">unsubscribe and reply result</td>
</tr>
<tr>
<td class="org-left">POST</td>
<td class="org-right">X</td>
<td class="org-left">publish and reply result</td>
</tr>
</tbody>
</table>
2. mqtt
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<colgroup>
<col class="org-left" />
<col class="org-left" />
</colgroup>
<thead>
<tr>
<th scope="col" class="org-left">Method</th>
<th scope="col" class="org-left">Action</th>
</tr>
</thead>
<tbody>
<tr>
<td class="org-left">PUT</td>
<td class="org-left">reply result</td>
</tr>
<tr>
<td class="org-left">POST</td>
<td class="org-left">return create connection action</td>
</tr>
<tr>
<td class="org-left">DELETE</td>
<td class="org-left">return close connection action</td>
</tr>
</tbody>
</table>
<a id="ref_example"></a>
## Example
1. Create Connection
```
coap-client -m post -e "" "coap://127.0.0.1/mqtt/connection?clientid=123&username=admin&password=public"
```
Server will return token **X** in payload
2. Update Connection
```
coap-client -m put -e "" "coap://127.0.0.1/mqtt/connection?clientid=123&token=X"
```
3. Publish
```
coap-client -m post -e "Hellow" "obstoken" "coap://127.0.0.1/ps/coap/test?clientid=123&username=admin&password=public"
```
if you want to publish with auth, you must first establish a connection, and then post publish request on the same socket, so libcoap client can't simulation publish with a token
```
coap-client -m post -e "Hellow" "coap://127.0.0.1/ps/coap/test?clientid=123&token=X"
```
4. Subscribe
```
coap-client -m get -s 60 -O 6,0x00 -o - -T "obstoken" "coap://127.0.0.1/ps/coap/test?clientid=123&username=admin&password=public"
```
**Or**
```
coap-client -m get -s 60 -O 6,0x00 -o - -T "obstoken" "coap://127.0.0.1/ps/coap/test?clientid=123&token=X"
```
5. Close Connection
```
coap-client -m delete -e "" "coap://127.0.0.1/mqtt/connection?clientid=123&token=X
```
More documentations: [CoAP Gateway](https://www.emqx.io/docs/en/v5.0/gateway/coap.html)

View File

@ -1,332 +1,58 @@
# emqx_gateway # emqx_gateway
EMQX Gateway EMQX Gateway is an application that managing all gateways in EMQX.
## Concept It provides a set of standards to define how to implement a certain type of
protocol access on EMQX. For example:
EMQX Gateway Management - Frame parsing
- Gateway-Registry (or Gateway Type) - Access authentication
- *Load - Publish and subscribe
- *UnLoad - Configuration & Schema
- *List - HTTP/CLI management interfaces
- Gateway There are some standard implementations available, such as [Stomp](../emqx_stomp/README.md),
- *Create [MQTT-SN](../emqx_mqttsn/README.md), [CoAP](../emqx_coap/README.md),
- *Delete and [LwM2M](../emqx_lwm2m/README.md) gateway.
- *Update
- *Stop-And-Start
- *Hot-Upgrade
- *Satrt/Enable
- *Stop/Disable
- Listener
## ROADMAP The emqx_gateway application depends on `emqx`, `emqx_authn`, `emqx_ctl` that
provide the foundation for protocol access.
Gateway v0.1: "Basic Functionals" ## Three ways to create your gateway
- Management support
- Conn/Frame/Protocol Template
- Support Stomp/MQTT-SN/CoAP/LwM2M/ExProto
Gateway v0.2: "Integration & Friendly Management" ## Raw Erlang Application
- Hooks & Metrics & Statistic
- HTTP APIs
- Management in the cluster
- Integrate with AuthN
- Integrate with `emqx_config`
- Improve hocon config
- Mountpoint & ClientInfo's Metadata
- The Concept Review
Gateway v0.3: "Fault tolerance and high availability" This approach is the same as in EMQX 4.x. You need to implement an Erlang application,
- A common session modoule for message delivery policy which is packaged in EMQX as a [Plugin](todo) or as a source code dependency.
- The restart mechanism for gateway-instance In this approach, you do not need to respect any specifications of emqx_gateway,
- Consistency of cluster state and you can freely implement the features you need.
- Configuration hot update
Gateway v1.0: "Best practices for each type of protocol"
- CoAP
- Stomp
- MQTT-SN
- LwM2M
### Compatible with EMQX Steps guide: [Implement Gateway via Raw Application](doc/implement_gateway_via_raw_appliction.md)
> Why we need to compatible ## Respect emqx_gateway framework
1. Authentication Similar to the first approach, you still need to implement an application using Erlang
2. Hooks/Event system and package it into EMQX.
3. Messages Mode & Rule Engine The only difference is that you need to follow the standard behaviors(callbacks) provided
4. Cluster registration by emqx_gateway.
5. Metrics & Statistic
> How to do it This is the approach we recommend. In this approach, your implementation can be managed
by the emqx_gateway framework, even if it may require you to understand more details about it.
>
### User Interface Steps guide: [Implement Gateway via Gateway framework](doc/implement_gateway_via_gateway_framekwork.md)
#### Configurations ## Use ExProto Gateway (Non-Erlang developers)
```hocon If you want to implement your gateway using other programming languages such as
gateway { Java, Python, Go, etc.
## ... some confs for top scope You need to implement a gRPC service in the other programming language to parse
.. your device protocol and integrate it with EMQX.
## End.
## Gateway Instances Refer to: [ExProto Gateway](../emqx_exproto/README.md)
lwm2m[.name] { ## Cookbook for emqx_gateway framework
## variable support *WIP*
mountpoint: lwm2m/%e/
lifetime_min: 1s
lifetime_max: 86400s
#qmode_time_window: 22
#auto_observe: off
#update_msg_publish_condition: contains_object_list
xml_dir: {{ platform_etc_dir }}/lwm2m_xml
clientinfo_override: {
username: ${register.opts.uname}
password: ${register.opts.passwd}
clientid: ${epn}
}
#authenticator: allow_anonymous
authenticator: [
{
type: auth-http
method: post
//?? how to generate clientinfo ??
params: $client.credential
}
]
translator: {
downlink: "dn/#"
uplink: {
notify: "up/notify"
response: "up/resp"
register: "up/resp"
update: "up/reps"
}
}
%% ?? listener.$type.name ??
listener.udp[.name] {
listen_on: 0.0.0.0:5683
max_connections: 1024000
max_conn_rate: 1000
## ?? udp keepalive in socket level ???
#keepalive:
## ?? udp proxy-protocol in socket level ???
#proxy_protocol: on
#proxy_timeout: 30s
recbuf: 2KB
sndbuf: 2KB
buffer: 2KB
tune_buffer: off
#access: allow all
read_packets: 20
}
listener.dtls[.name] {
listen_on: 0.0.0.0:5684
...
}
}
## The CoAP Gateway
coap[.name] {
#enable_stats: on
authenticator: [
...
]
listener.udp[.name] {
...
}
listener.dtls[.name] {
...
}
}
## The Stomp Gateway
stomp[.name] {
allow_anonymous: true
default_user.login: guest
default_user.passcode: guest
frame.max_headers: 10
frame.max_header_length: 1024
frame.max_body_length: 8192
listener.tcp[.name] {
...
}
listener.ssl[.name] {
...
}
}
exproto[.name] {
proto_name: DL-648
authenticators: [...]
adapter: {
type: grpc
options: {
listen_on: 9100
}
}
handler: {
type: grpc
options: {
url: <http://127.0.0.1:9001>
}
}
listener.tcp[.name] {
...
}
}
## ============================ Enterpise gateways
## The JT/T 808 Gateway
jtt808[.name] {
idle_timeout: 30s
enable_stats: on
max_packet_size: 8192
clientinfo_override: {
clientid: $phone
username: xxx
password: xxx
}
authenticator: [
{
type: auth-http
method: post
params: $clientinfo.credential
}
]
translator: {
subscribe: [jt808/%c/dn]
publish: [jt808/%c/up]
}
listener.tcp[.name] {
...
}
listener.ssl[.name] {
...
}
}
gbt32960[.name] {
frame.max_length: 8192
retx_interval: 8s
retx_max_times: 3
message_queue_len: 10
authenticators: [...]
translator: {
## upstream
login: gbt32960/${vin}/upstream/vlogin
logout: gbt32960/${vin}/upstream/vlogout
informing: gbt32960/${vin}/upstream/info
reinforming: gbt32960/${vin}/upstream/reinfo
## downstream
downstream: gbt32960/${vin}/dnstream
response: gbt32960/${vin}/upstream/response
}
listener.tcp[.name] {
...
}
listener.ssl[.name] {
...
}
}
privtcp[.name] {
max_packet_size: 65535
idle_timeout: 15s
enable_stats: on
force_gc_policy: 1000|1MB
force_shutdown_policy: 8000|800MB
translator: {
up_topic: tcp/%c/up
dn_topic: tcp/%c/dn
}
listener.tcp[.name]: {
...
}
}
}
```
#### CLI
##### Gateway
```bash
## List all started gateway and gateway-instance
emqx_ctl gateway list
emqx_ctl gateway lookup <GatewayId>
emqx_ctl gateway stop <GatewayId>
emqx_ctl gateway start <GatewayId>
emqx_ctl gateway-registry re-searching
emqx_ctl gateway-registry list
emqx_ctl gateway-clients list <Type>
emqx_ctl gateway-clients show <Type> <ClientId>
emqx_ctl gateway-clients kick <Type> <ClientId>
## Banned ??
emqx_ctl gateway-banned
## Metrics
emqx_ctl gateway-metrics [<GatewayId>]
```
#### Management by HTTP-API/Dashboard/
#### How to integrate a protocol to your platform
### Develop your protocol gateway
There are 3 way to create your protocol gateway for EMQX 5.0:
1. Use Erlang to create a new emqx plugin to handle all of protocol packets (same as v5.0 before)
2. Based on the emqx-gateway-impl-bhvr and emqx-gateway
3. Use the gRPC Gateway

View File

@ -1,110 +1,34 @@
# MQTT-SN Gateway # emqx_mqttsn
EMQX MQTT-SN Gateway. The MQTT-SN gateway is based on the
[MQTT-SN v1.2](https://www.oasis-open.org/committees/download.php/66091/MQTT-SN_spec_v1.2.pdf).
## Configure Plugin ## Quick Start
In EMQX 5.0, MQTT-SN gateway can be configured and enabled through the Dashboard.
File: etc/emqx_sn.conf It can also be enabled via the HTTP API or emqx.conf, e.g. In emqx.conf:
``` ```properties
## The UDP port which emq-sn is listening on. gateway.mqttsn {
##
## Value: IP:Port | Port
##
## Examples: 1884, 127.0.0.1:1884, ::1:1884
mqtt.sn.port = 1884
## The duration(seconds) that emq-sn broadcast ADVERTISE message through. mountpoint = "mqtt/sn"
##
## Value: Second
mqtt.sn.advertise_duration = 900
## The MQTT-SN Gateway id in ADVERTISE message. gateway_id = 1
##
## Value: Number
mqtt.sn.gateway_id = 1
## To control whether write statistics data into ETS table for dashboard to read. broadcast = true
##
## Value: on | off
mqtt.sn.enable_stats = off
## To control whether accept and process the received publish message with qos=-1. enable_qos3 = true
##
## Value: on | off
mqtt.sn.enable_qos3 = off
## The pre-defined topic name corresponding to the pre-defined topic id of N. listeners.udp.default {
## Note that the pre-defined topic id of 0 is reserved. bind = 1884
mqtt.sn.predefined.topic.0 = reserved max_connections = 10240000 max_conn_rate = 1000
mqtt.sn.predefined.topic.1 = /predefined/topic/name/hello }
mqtt.sn.predefined.topic.2 = /predefined/topic/name/nice }
## Default username for MQTT-SN. This parameter is optional. If specified,
## emq-sn will connect EMQ core with this username. It is useful if any auth
## plug-in is enabled.
##
## Value: String
mqtt.sn.username = mqtt_sn_user
## This parameter is optional. Pair with username above.
##
## Value: String
mqtt.sn.password = abc
``` ```
- mqtt.sn.port > Note:
* The UDP port which emqx-sn is listening on. > Configuring the gateway via emqx.conf requires changes on a per-node basis,
- mqtt.sn.advertise_duration > but configuring it via Dashboard or the HTTP API will take effect across the cluster.
* The duration(seconds) that emqx-sn broadcast ADVERTISE message through.
- mqtt.sn.gateway_id
* Gateway id in ADVERTISE message.
- mqtt.sn.enable_stats
* To control whether write statistics data into ETS table for dashboard to read.
- mqtt.sn.enable_qos3
* To control whether accept and process the received publish message with qos=-1.
- mqtt.sn.predefined.topic.N
* The pre-defined topic name corresponding to the pre-defined topic id of N. Note that the pre-defined topic id of 0 is reserved.
- mqtt.sn.username
* This parameter is optional. If specified, emqx-sn will connect EMQX core with this username. It is useful if any auth plug-in is enabled.
- mqtt.sn.password
* This parameter is optional. Pair with username above.
## Load Plugin More documentations: [MQTT-SN Gateway](https://www.emqx.io/docs/en/v5.0/gateway/mqttsn.html)
```
./bin/emqx_ctl plugins load emqx_sn
```
## Client
### NOTE
- Topic ID is per-client, and will be cleared if client disconnected with broker or keepalive failure is detected in broker.
- Please register your topics again each time connected with broker.
- If your udp socket(mqtt-sn client) has successfully connected to broker, don't try to send another CONNECT on this socket again, which will lead to confusing behaviour. If you want to start from beging, please do as following:
+ destroy your present socket and create a new socket to connect again
+ or send DISCONNECT on the same socket and connect again.
### Library
- https://github.com/eclipse/paho.mqtt-sn.embedded-c/
- https://github.com/ty4tw/MQTT-SN
- https://github.com/njh/mqtt-sn-tools
- https://github.com/arobenko/mqtt-sn
### sleeping device
PINGREQ must have a ClientId which is identical to the one in CONNECT message. Without ClientId, emqx-sn will ignore such PINGREQ.
### pre-defined topics
The mapping of a pre-defined topic id and topic name should be known inadvance by both client's application and gateway. We define this mapping info in emqx_sn.conf file, and which shall be kept equivalent in all client's side.
## License
Apache License Version 2.0
## Author
EMQX Team.

View File

@ -1,73 +1,31 @@
# emqx_stomp
# emqx-stomp The Stomp Gateway is based on the
[Stomp v1.2](https://stomp.github.io/stomp-specification-1.2.html) and is
compatible with the Stomp v1.0 and v1.1 specification.
## Quick Start
The plugin adds STOMP 1.0/1.1/1.2 protocol supports to the EMQX broker. In EMQX 5.0, Stomp gateway can be configured and enabled through the Dashboard.
The STOMP clients could PubSub to the MQTT clients. It can also be enabled via the HTTP API or emqx.conf, e.g. In emqx.conf:
## Configuration ```properties
gateway.stomp {
etc/emqx_stomp.conf mountpoint = "stomp/"
``` listeners.tcp.default {
## The Port that stomp listener will bind. bind = 61613
## acceptors = 16
## Value: Port max_connections = 1024000
stomp.listener = 61613 max_conn_rate = 1000
}
## The acceptor pool for stomp listener. }
##
## Value: Number
stomp.listener.acceptors = 4
## Maximum number of concurrent stomp connections.
##
## Value: Number
stomp.listener.max_connections = 512
## Default login user
##
## Value: String
stomp.default_user.login = guest
## Default login password
##
## Value: String
stomp.default_user.passcode = guest
## Allow anonymous authentication.
##
## Value: true | false
stomp.allow_anonymous = true
## Maximum numbers of frame headers.
##
## Value: Number
stomp.frame.max_headers = 10
## Maximum length of frame header.
##
## Value: Number
stomp.frame.max_header_length = 1024
## Maximum body length of frame.
##
## Value: Number
stomp.frame.max_body_length = 8192
``` ```
## Load the Plugin > Note:
> Configuring the gateway via emqx.conf requires changes on a per-node basis,
``` > but configuring it via Dashboard or the HTTP API will take effect across the cluster.
./bin/emqx_ctl plugins load emqx_stomp
```
## License
Apache License Version 2.0
## Author
EMQX Team.
More documentations: [Stomp Gateway](https://www.emqx.io/docs/en/v5.0/gateway/stomp.html)