Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 577. MQTT - emqx.io 서비스 소개 [링크 복사], [링크+제목 복사],
조회: 18945
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

MQTT - emqx.io 서비스 소개

winget에서도 볼 수 있는 emqx.mqttx 서비스가 있는데요,

C:temp> winget search --name mqtt
Name          Id                            Version
---------------------------------------------------
MQTT Explorer thomasnordquist.MQTT-Explorer 0.3.5
mqttx         emqx.mqttx                    1.5.2

해당 사이트에서 직접 zip 파일을 다운로드해,

EMQ X Broker
; https://www.emqx.io/downloads#broker

emqx-windows-4.3.2.zip
; https://www.emqx.io/downloads/broker/v4.3.2/emqx-windows-4.3.2.zip

압축 해제 후 실행해 볼 수도 있습니다. 관련한 명령행 옵션은 다음의 문서에서 확인할 수 있고,

Start EMQ X
; https://docs.emqx.io/en/broker/v4.3/getting-started/start.html

따라서 명령행에서 단순히 ".\bin\emqx start"를 관리자 권한으로 실행하면 erl.exe가 다음의 명령행으로 실행되고,

C:\tmp\emqx\erts-11.0\bin\erl.exe -e 256000 -- -root C:\tmp\emqx -progname erl.exe -- -home "C:\Users\SeongTae Jeong" -- -noshell -noinput -setcookie emqxsecretcookie -name emqx@127.0.0.1 -- -vm_args c:/tmp/emqx/data/configs/vm.2021.06.04.13.38.15.args -mnesia dir 'c:/tmp/emqx/data/mnesia/emqx@127.0.0.1' -- -boot C:\tmp\emqx\releases\4.3.2\start -config c:/tmp/emqx/data/configs/app.2021.06.04.13.38.15.config


서비스 상태는 이렇게 조회할 수 있으며,

C:\tmp\emqx> .\bin\emqx_ctl status
Node 'emqx@127.0.0.1' 4.3.2 is started

종료하려면 ".\bin\emqx stop" 명령어를 내리면 됩니다.




EMQ X Broker는 REST API를 제공하기 때문에 아래의 문서에 따라 쉽게 테스트하는 것이 가능합니다.

HTTP API
; https://docs.emqx.io/en/broker/v4.3/advanced/http-api.html#api-endpoints

몇 개를 추려서 한 번 실습해 볼까요? 우선 MQTT broker가 정상적으로 실행되었는지를 알 수 있습니다. (json_pp 명령어)

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/brokers" 2> nul | json_ppM
{
  "data": [
    {
      "version": "4.3.2",
      "uptime": "1 hours, 6 minutes, 17 seconds",
      "sysdescr": "EMQ X",
      "otp_release": "23.0/11.0",
      "node_status": "Running",
      "node": "emqx@127.0.0.1",
      "datetime": "2021-06-04 23:08:26"
    }
  ],
  "code": 0
}

보는 바와 같이 "emqx@127.0.0.1"로 식별되는 노드 1개가 실행 중임을 알 수 있고, 해당 broker만 지정해 조회할 수도 있습니다.

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/brokers/emqx@127.0.0.1"

node 개념이 있는 것을 보니 클러스터 구성이 가능한 것 같은데요, 그래서 nodes 조회로도 환경을 검색할 수 있습니다.

c:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes" 2> nul | json_pp
{
  "data": [
    {
      "version": "4.3.2",
      "uptime": "1 hours, 9 minutes, 37 seconds",
      "process_used": 326,
      "process_available": 262144,
      "otp_release": "23.0/11.0",
      "node_status": "Running",
      "node": "emqx@127.0.0.1",
      "memory_used": "54.37M",
      "memory_total": "71.69M",
      "max_fds": 16384,
      "load5": "0.00",
      "load15": "0.00",
      "load1": "0.00",
      "connections": 1
    }
  ],
  "code": 0
}

이것 역시 node를 지정해 조회할 수 있습니다.

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1"




MQTT broker에 대한 서비스를 제공하는 listener를 다음의 명령어로 조회할 수 있고,

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/listeners" 2> nul | json_pp
{
  "data": [
    {
      "node": "emqx@127.0.0.1",
      "listeners": [
        {
          "shutdown_count": [],
          "protocol": "mqtt:ssl",
          "max_conns": 102400,
          "listen_on": "8883",
          "identifier": "mqtt:ssl:external",
          "current_conns": 0,
          "acceptors": 16
        },
        {
          "shutdown_count": [],
          "protocol": "mqtt:tcp",
          "max_conns": 1024000,
          "listen_on": "0.0.0.0:1883",
          "identifier": "mqtt:tcp:external",
          "current_conns": 0,
          "acceptors": 8
        },
        {
          "shutdown_count": [],
          "protocol": "mqtt:tcp",
          "max_conns": 1024000,
          "listen_on": "127.0.0.1:11883",
          "identifier": "mqtt:tcp:internal",
          "current_conns": 0,
          "acceptors": 4
        },
        {
          "shutdown_count": [],
          "protocol": "http:dashboard",
          "max_conns": 512,
          "listen_on": "18083",
          "current_conns": 0,
          "acceptors": 4
        },
        {
          "shutdown_count": [],
          "protocol": "http:management",
          "max_conns": 512,
          "listen_on": "8081",
          "current_conns": 1,
          "acceptors": 2
        },
        {
          "shutdown_count": [],
          "protocol": "mqtt:ws:8083",
          "max_conns": 102400,
          "listen_on": "8083",
          "current_conns": 0,
          "acceptors": 4
        },
        {
          "shutdown_count": [],
          "protocol": "mqtt:wss:8084",
          "max_conns": 16,
          "listen_on": "8084",
          "current_conns": 0,
          "acceptors": 4
        }
      ]
    }
  ],
  "code": 0
}

특정 리스너의 재시작도 가능합니다.

C:\temp> curl --basic -u admin:public -X PUT "http://localhost:8081/api/v4/listeners/mqtt:tcp:external/restart"
{"code":0}

여기서 mqtt:management는 우리가 지금 REST API를 수행할 수 있도록 하는 listener입니다. 그리고, mqtt:tcp가 바로 MQTT broker의 endpoint인데요, 실제로 이 포트로 지난 글에서 만든 MqttClient를,

C# - MQTT를 이용한 클라이언트/서버(Broker) 통신 예제
; https://www.sysnet.pe.kr/2/0/12661

포트만 mqtt:tcp가 대기하는 1883으로 바꾸면,

ManagedMqttClientOptions options = new ManagedMqttClientOptions();

options.ClientOptions = new MqttClientOptions()
{
    ClientId = "MyMqttClient",
    ChannelOptions = new MqttClientTcpOptions
    {
        Server = "localhost",
        Port = 1883,
    }
};

접속이 됩니다. 위와 같이 변경하고 실행하면 EMQ X Broker에 연결된 MQTT client를 다음의 API로 조회할 수 있습니다.

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients?_page=1&_limit=10"

실습할 때는 클라이언트가 몇 개 없을 테니 그냥 전체 조회를 해도 됩니다.

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients" 2> nul | json_pp
{
  "meta": {
    "page": 1,
    "limit": 10000,
    "hasnext": false,
    "count": 1
  },
  "data": [
    {
      "recv_cnt": 5,
      "heap_size": 2586,
      "recv_pkt": 5,
      "connected_at": "2021-06-04 23:00:21",
      "node": "emqx@127.0.0.1",
      "send_oct": 14,
      "max_subscriptions": 0,
      "send_msg": 0,
      "reductions": 75586,
      "proto_ver": 4,
      "mqueue_dropped": 0,
      "mqueue_len": 0,
      "expiry_interval": 0,
      "keepalive": 15,
      "recv_oct": 104,
      "is_bridge": false,
      "send_pkt": 3,
      "created_at": "2021-06-04 23:00:21",
      "mailbox_len": 1,
      "max_mqueue": 1000,
      "port": 26175,
      "zone": "external",
      "inflight": 0,
      "recv_msg": 3,
      "mountpoint": "undefined",
      "proto_name": "MQTT",
      "awaiting_rel": 0,
      "max_inflight": 32,
      "subscriptions_cnt": 2,
      "clientid": "MyMqttClient",
      "username": "undefined",
      "max_awaiting_rel": 100,
      "clean_start": true,
      "connected": true,
      "send_cnt": 3,
      "ip_address": "127.0.0.1"
    }
  ],
  "code": 0
}

보는 바와 같이 우리가 MQTTnet을 이용해 만든 "MyMqttClient"가 보입니다. 전체 조회뿐만 아니라, 특정 clientid를 지정해 조회할 수도 있고,

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients/MyMqttClient"

해당 클라이언트를 강제로 접속 해제하는 것도 가능합니다.

curl -i --basic -u admin:public -X DELETE "http://localhost:8081/api/v4/clients/MyMqttClient"

그런데 위의 명령어를 내린 후 다시 "GET /api/v4/clients"로 알아 보면 해당 클라이언트가 여전히 붙어 있습니다. 하지만 분명히 연결 종료는 되었는데, 이것은 Process Explorer를 통해 TCP 연결이 삭제된 것으로 확인할 수 있습니다. 연결이 여전히 붙어있는 이유는, 우리가 만든 MqttClient의 코드 중에 재접속을 자동으로 하는 옵션이 있었기 때문입니다.

options.AutoReconnectDelay = TimeSpan.FromSeconds(1);

이 설정으로 인해 1초 만에 재차 연결을 했기 때문에 "GET /api/v4/clients"를 했을 때 여전히 연결이 붙은 것처럼 보인 것입니다.

이하, 다음의 명령들은 직관적으로 어떤 결과를 나타낼지 상상할 수 있을 것입니다.

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/clients?_page=1&_limit=10"
curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/clients"
curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/clients/MyMqttClient"

curl --basic -u admin:public -X DELETE "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/clients/MyMqttClient"

(참고로, 위에서 nodes/.../clients/...로 DELETE하는 명령어는 제가 실습했을 때 정상 동작을 하지 않았습니다.)




재미있는 것은, Broker에 등록된 topic과 그 클라이언트 목록을 조회하는 것도 가능해서,

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/subscriptions?_page=1&_limit=10"
curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/subscriptions?_page=1&limit=10"
curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/subscriptions/MyMqttClient"
curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/subscriptions/MyMqttClient"

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/subscriptions" 2> nul | json_pp
{
  "meta": {
    "page": 1,
    "limit": 10000,
    "hasnext": false,
    "count": 2
  },
  "data": [
    {
      "topic": "xyz",
      "qos": 0,
      "node": "emqx@127.0.0.1",
      "clientid": "MyMqttClient"
    },
    {
      "topic": "abc",
      "qos": 0,
      "node": "emqx@127.0.0.1",
      "clientid": "MyMqttClient"
    }
  ],
  "code": 0
}

개발 시 테스트 용도로 편리하게 사용할 수 있습니다. (위에서 "xyz", "abc"가 나온 것은 MqttClient의 코드에서 해당 토픽을 구독하도록 했기 때문입니다.)

await managedClient.SubscribeAsync(new MqttTopicFilter { Topic = "xyz", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
await managedClient.SubscribeAsync(new MqttTopicFilter { Topic = "abc", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });

아울러 토픽에 메시지를 전송할 수 있고,

c:\temp> curl --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/publish" -d "{\"topic\":\"abc\",\"payload\":\"Hello World\",\"qos\":1,\"retain\":false,\"clientid\":\"MyMqttClient3\"}"
{"code":0}

특정 클라이언트에 토픽을 구독시키는 것도 가능합니다. 아래는 "test1", "test2"라는 새로운 토픽을 현재 연결된 MyMqttClient에 구독시키고 있습니다.

c:\temp> curl --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/subscribe" -d "{\"topics\":\"test1,test2\",\"qos\":1,\"clientid\":\"MyMqttClient\"}"
{"code":0}

확인은 publish API로 실행하면,

C:\temp> curl --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/publish" -d "{\"topic\":\"test1\",\"payload\":\"Hello World\",\"qos\":1,\"retain\":false,\"clientid\":\"MyMqttClient3\"}"
{"code":0}

MqttClient 화면에 ">> RECEIVED: test1"의 출력으로 알 수 있습니다.

당연히, 토픽 구독을 해제하는 것도 가능합니다.

curl --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/unsubscribe" -d "{\"topic\":\"test2\",\"qos\":1,\"clientid\":\"MyMqttClient\"}"
curl --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/unsubscribe" -d "{\"topic\":\"test1\",\"qos\":1,\"clientid\":\"MyMqttClient\"}"




REST API를 보면 사용자 계정으로 조회하는 것도 볼 수 있습니다.

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients/username/{username}"

위의 명령어는 해당 사용자 계정으로 실행 중인 모든 클라이언트를 열거하는데요, 이를 실습하기 위해서는 MqttClient의 코드에서 "Credentials" 속성을 다음과 같이 부여해 실행해야 합니다.

options.ClientOptions = new MqttClientOptions()
{
    ClientId = "MyMqttClient",
    Credentials = new RandomPassword(),
    ChannelOptions = new MqttClientTcpOptions
    {
        Server = "localhost",
        Port = 1883,
    }
};

public class RandomPassword : IMqttClientCredentials
{
    public byte[] Password => Guid.NewGuid().ToByteArray();

    public string Username => "the_test_user";
}

위와 같이 변경한 MqttClient를 실행해 broker 측으로 연결하면 이제 다음과 같은 결과를 볼 수 있습니다.

c:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients/username/the_test_user"
{"data":[{"recv_cnt":9,"heap_size":987,"recv_pkt":9,"connected_at":"2021-06-04 14:21:44","node":"emqx@127.0.0.1","send_oct":22,"max_subscriptions":0,"send_msg":0,"reductions":77899,"clean_start":true,"mqueue_dropped":0,"mqueue_len":0,"expiry_interval":0,"keepalive":15,"recv_oct":112,"is_bridge":false,"send_pkt":7,"created_at":"2021-06-04 14:21:44","inflight":0,"mailbox_len":0,"max_mqueue":1000,"port":5254,"zone":"external","awaiting_rel":0,"recv_msg":3,"mountpoint":"undefined","proto_ver":4,"proto_name":"MQTT","max_inflight":32,"subscriptions_cnt":2,"clientid":"MyMqttClient","username":"the_test_user","max_awaiting_rel":100,"connected":true,"send_cnt":7,"ip_address":"127.0.0.1"}],"code":0}

acl_cache도 조회 및 삭제할 수 있다고 하는데, 아직 이에 대한 MqttClient 측에서의 사용법은 모르겠습니다. ^^

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients/MyMqttClient/acl_cache"
curl --basic -u admin:public -X DELETE "http://localhost:8081/api/v4/clients/MyMqttClient/acl_cache"
curl --basic -u admin:public -X DELETE "http://localhost:8081/api/v4/acl-cache"

route도 있다는데 아직 이것도 개념을 모르니 실행만 해봤습니다. (route 조회와 topic 조회의 차이점을 모르겠습니다.)

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/routes"
{"meta":{"page":1,"limit":10000,"count":2},"data":[{"topic":"abc","node":"emqx@127.0.0.1"},{"topic":"xyz","node":"emqx@127.0.0.1"}],"code":0}

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/routes/xyz"
{"data":[{"topic":"xyz","node":"emqx@127.0.0.1"}],"code":0}




알아본 김에 기타 여러 가지 명령어도 보겠습니다. 보니까, plugins도 지원하는 것 같고,

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/plugins"
{"data":[{"plugins":[{"type":"auth","name":"emqx_auth_http","description":"EMQ X Authentication/ACL with HTTP API","active":false},{"type":"auth","name":"emqx_auth_jwt","description":"EMQ X Authentication with JWT","active":false},{"type":"auth","name":"emqx_auth_ldap","description":"EMQ X Authentication/ACL with LDAP","active":false},{"type":"auth","name":"emqx_auth_mnesia","description":"EMQ X Authentication with Mnesia","active":false},{"type":"auth","name":"emqx_auth_mongo","description":"EMQ X Authentication/ACL with MongoDB","active":false},{"type":"auth","name":"emqx_auth_mysql","description":"EMQ X Authentication/ACL with MySQL","active":false},{"type":"auth","name":"emqx_auth_pgsql","description":"EMQ X Authentication/ACL with PostgreSQL","active":false},{"type":"auth","name":"emqx_auth_redis","description":"EMQ X Authentication/ACL with Redis","active":false},{"type":"bridge","name":"emqx_bridge_mqtt","description":"EMQ X Bridge to MQTT Broker","active":false},{"type":"protocol","name":"emqx_coap","description":"EMQ X CoAP Gateway","active":false},{"type":"feature","name":"emqx_dashboard","description":"EMQ X Web Dashboard","active":true},{"type":"feature","name":"emqx_exhook","description":"EMQ X Extension for Hook","active":false},{"type":"feature","name":"emqx_exproto","description":"EMQ X Extension for Protocol","active":false},{"type":"feature","name":"emqx_lua_hook","description":"EMQ X Lua Hooks","active":false},{"type":"protocol","name":"emqx_lwm2m","description":"EMQ X LwM2M Gateway","active":false},{"type":"feature","name":"emqx_management","description":"EMQ X Management API and CLI","active":true},{"type":"feature","name":"emqx_prometheus","description":"Prometheus for EMQ X","active":false},{"type":"feature","name":"emqx_psk_file","description":"EMQX PSK Plugin from File","active":false},{"type":"feature","name":"emqx_recon","description":"EMQ X Recon Plugin","active":true},{"type":"feature","name":"emqx_retainer","description":"EMQ X Retainer","active":true},{"type":"feature","name":"emqx_rule_engine","description":"EMQ X Rule Engine","active":true},{"type":"feature","name":"emqx_sasl","description":"EMQ X SASL","active":false},{"type":"protocol","name":"emqx_sn","description":"EMQ X MQTT-SN Plugin","active":false},{"type":"protocol","name":"emqx_stomp","description":"EMQ X Stomp Protocol Plugin","active":false},{"type":"feature","name":"emqx_telemetry","description":"EMQ X Telemetry","active":true},{"type":"feature","name":"emqx_web_hook","description":"EMQ X WebHook Plugin","active":false}],"node":"emqx@127.0.0.1"}],"code":0}

"name" 필드로 특정 plugin을 로드/언로드하는 것이 가능합니다.

C:\temp> curl --basic -u admin:public -X PUT "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/plugins/emqx_auth_jwt/load"
{"code":0}

C:\temp> curl --basic -u admin:public -X PUT "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/plugins/emqx_auth_jwt/unload"
{"code":0}

클러스터 내의 통계 수치와 상태 정보를 각각 metrics와 stats로 구할 수 있고,

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/metrics"
{"data":[{"node":"emqx@127.0.0.1","metrics":{"actions.exception":0,"packets.publish.error":0,"packets.publish.received":13,"packets.subscribe.error":0,"packets.pubrel.sent":0,"packets.connack.error":0,"messages.acked":3,"packets.pubcomp.received":0,"delivery.dropped.queue_full":0,"packets.puback.sent":5,"client.disconnected":11,"messages.publish":20,"messages.dropped":11,"packets.suback.sent":7,"packets.subscribe.received":7,"packets.pubcomp.inuse":0,"messages.delivered":9,"packets.pubcomp.missed":0,"session.resumed":0,"packets.unsuback.sent":2,"actions.retry":0,"delivery.dropped.qos0_msg":0,"packets.auth.sent":0,"messages.forward":0,"client.authenticate":15,"packets.subscribe.auth_error":0,"packets.pubrec.received":0,"client.connected":15,"messages.qos0.received":8,"messages.qos2.received":0,"messages.sent":9,"rules.matched":0,"delivery.dropped":0,"client.connect":15,"packets.pingresp.sent":643,"packets.received":686,"client.auth.failure":0,"session.discarded":0,"messages.qos1.sent":3,"packets.puback.received":3,"delivery.dropped.too_large":0,"packets.disconnect.sent":0,"packets.publish.auth_error":0,"messages.retained":312,"packets.sent":681,"packets.unsubscribe.error":0,"actions.taken":0,"packets.pubrec.inuse":0,"packets.publish.inuse":0,"client.subscribe":7,"messages.received":20,"packets.connack.sent":15,"delivery.dropped.expired":0,"packets.publish.sent":9,"delivery.dropped.no_local":0,"session.terminated":11,"client.check_acl":21,"packets.unsubscribe.received":2,"packets.pubrel.missed":0,"packets.puback.inuse":0,"bytes.received":2097,"session.takeovered":0,"messages.dropped.no_subscribers":11,"packets.pingreq.received":643,"packets.pubrec.sent":0,"actions.error":0,"bytes.sent":1550,"packets.auth.received":0,"session.created":15,"messages.qos0.sent":6,"messages.qos1.received":12,"packets.connack.auth_error":0,"packets.pubcomp.sent":0,"client.connack":15,"client.auth.success":0,"packets.publish.dropped":0,"client.auth.ignore":0,"packets.pubrec.missed":0,"messages.qos2.sent":0,"client.unsubscribe":2,"packets.disconnect.received":3,"actions.success":0,"packets.pubrel.received":0,"messages.dropped.expired":0,"client.auth.anonymous":15,"messages.delayed":0,"packets.connect.received":15,"packets.puback.missed":0}}],"code":0}

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/stats"
{"data":[{"stats":{"topics.max":4,"topics.count":2,"subscriptions.shared.max":0,"subscriptions.shared.count":0,"subscriptions.max":4,"subscriptions.count":2,"subscribers.max":4,"subscribers.count":2,"suboptions.max":4,"suboptions.count":2,"sessions.max":2,"sessions.count":2,"rules.max":0,"rules.count":0,"routes.max":4,"routes.count":2,"retained.max":3,"retained.count":3,"resources.max":0,"resources.count":0,"connections.max":2,"connections.count":2,"channels.max":2,"channels.count":2,"actions.max":5,"actions.count":5},"node":"emqx@127.0.0.1"}],"code":0}

알람 기능도 있는 듯하고,

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/alarms/present"
Not found.

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/alarms/history"
Not found.

blacklist 등의 관리 기능도 있습니다.

curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/banned"
{"meta":{"page":1,"limit":10000,"count":0},"data":[],"code":0}

curl --basic -u admin:public -X POST "http://localhost:8081/api/v4/banned" -d "{\"who\":\"MyMqttClient2\",\"as\":\"clientid\"}"
Internal Error

curl -i --basic -u admin:public -X DELETE "http://localhost:8081/api/v4/banned/clientid/MyMqttClient2"

대단하군요. ^^ 이런 것들을 일일이 MQTTnet 라이브러리를 이용해 구현하는 것이 귀찮을 수 있겠다는 생각이 들 정도로 잘해놨습니다. 게다가 web_hook까지 제공하는 것 같습니다.

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/resource_types/web_hook"
{"data":{"title":{"zh":"WebHook","en":"WebHook"},"provider":"emqx_web_hook","params":{"verify":{"type":"boolean","title":{"zh":"校验服务器证书","en":"Verify Server Certfile"},"order":8,"description":{"zh":"是 否校验服务器证书。 默认客户端不会去校验服务器的证书,如果需要校验,请设置成true。","en":"Whether to verify the server certificate. By default, the client will not verify the server's certificate. If verification is required, please set it to true."},"default":false},"url":{"type":"string","title":{"zh":"请求 URL","en":"Request URL"},"required":true,"order":1,"format":"url","description":{"zh":"用于接收 Webhook 请求的服务器的 URL。","en":"The URL of the server that will receive the Webhook requests."}},"server_name_indication":{"type":"string","title":{"zh":"服务器名称指示","en":"Server Name Indication"},"order":9,"description":{"zh":"指定用于对端证书验证时使用的主机名,或者设置为 disable 以关闭此项验证。","en":"Specify the hostname used for peer certificate verification, or set to disable to turn off this verification."}},"request_timeout":{"type":"string","title":{"zh":"请求超时时间时间","en":"Request Timeout"},"order":3,"description":{"zh":"请求超时时间","en":"Request Timeout In Seconds"},"default":"5s"},"pool_size":{"type":"number","title":{"zh":"连接池大小","en":"Pool Size"},"order":4,"description":{"zh":"连接池大小","en":"Connection Pool"},"default":8},"keyfile":{"type":"file","title":{"zh":"SSL Key","en":"SSL Key"},"order":6,"description":{"zh":"SSL 私钥","en":"Your ssl keyfile"},"default":""},"connect_timeout":{"type":"string","title":{"zh":"连接超时时间","en":"Connect Timeout"},"order":2,"description":{"zh":"连接超时时间","en":"Connect Timeout In Seconds"},"default":"5s"},"certfile":{"type":"file","title":{"zh":"SSL Cert","en":"SSL Cert"},"order":7,"description":{"zh":"SSL 证书","en":"Your ssl certfile"},"default":""},"cacertfile":{"type":"file","title":{"zh":"CA 证书文件","en":"CA Certificate File"},"order":5,"description":{"zh":"CA 证书文件","en":"CA Certificate file"},"default":""}},"name":"web_hook","description":{"zh":"WebHook","en":"WebHook"}},"code":0}

뜬금없이 ^^ 리소스 이름에 중국어 문자가 나오는데요, EMQX라는 회사 자체가 중국계 인물들로 이뤄진 스타트업으로 보입니다.

About / Company
; https://www.emqx.io/about

암튼, 결국 MQTT broker를 혹시나 저런 규모로 만들 생각이라면 그냥 이미 저렇게 만들어 둔 서비스를 사용하는 것도 나쁘지 않은 선택으로 보입니다. 그 외에, 자신만의 소프트웨어에 간단하게 구현할 목적이라면 MQTTnet 라이브러리를 통해 만드는 것이 좋겠고!




이 글을 실습하면서, "Bad Request" 오류가 발생한다면?

c:\temp> url -i --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/publish" -d '{"topic":"abc","payload":"Hello World","qos":1,"retain":false,"clientid":"MyMqttClient"}'
HTTP/1.1 400 Bad Request
content-length: 11
content-type: text/plain
date: Fri, 04 Jun 2021 05:36:30 GMT
server: Cowboy

Bad Request

다음의 글에 정리한,

curl로 호출할 때 발생하는 오류 정리
; https://www.sysnet.pe.kr/2/0/11426

내용에 따라 윈도우에서는 json 텍스트를 다음과 같이 다뤄야 합니다.

'{"topic":"abc","payload":"Hello World","qos":1,"retain":false,"clientid":"MyMqttClient"}'

==>
"{\"topic\":\"abc\",\"payload\":\"Hello World\",\"qos\":1,\"retain\":false,\"clientid\":\"MyMqttClient\"}"

마지막으로, 아래는 EMQ X Broker가 제공하는 모든 API들의 endpoint와, 리소스 타입들입니다.

C:\temp> curl --basic -u admin:public -X GET "http://localhost:8081/api/v4" 2> nul | json_pp
{
  "data": [
    {
      "path": "/acl/clientid",
      "name": "list_clientid",
      "method": "GET",
      "descr": "List available mnesia in the cluster"
    },
    {
      "path": "/acl/username",
      "name": "list_username",
      "method": "GET",
      "descr": "List available mnesia in the cluster"
    },
    {
      "path": "/acl/$all",
      "name": "list_all",
      "method": "GET",
      "descr": "List available mnesia in the cluster"
    },
    {
      "path": "/acl/clientid/:clientid",
      "name": "lookup_clientid",
      "method": "GET",
      "descr": "Lookup mnesia in the cluster"
    },
    {
      "path": "/acl/username/:username",
      "name": "lookup_username",
      "method": "GET",
      "descr": "Lookup mnesia in the cluster"
    },
    {
      "path": "/acl",
      "name": "add",
      "method": "POST",
      "descr": "Add mnesia in the cluster"
    },
    {
      "path": "/acl/clientid/:clientid/topic/:topic",
      "name": "delete_clientid",
      "method": "DELETE",
      "descr": "Delete mnesia in the cluster"
    },
    {
      "path": "/acl/username/:username/topic/:topic",
      "name": "delete_username",
      "method": "DELETE",
      "descr": "Delete mnesia in the cluster"
    },
    {
      "path": "/acl/$all/topic/:topic",
      "name": "delete_all",
      "method": "DELETE",
      "descr": "Delete mnesia in the cluster"
    },
    {
      "path": "/auth_clientid",
      "name": "list_clientid",
      "method": "GET",
      "descr": "List available clientid in the cluster"
    },
    {
      "path": "/auth_clientid/:clientid",
      "name": "lookup_clientid",
      "method": "GET",
      "descr": "Lookup clientid in the cluster"
    },
    {
      "path": "/auth_clientid",
      "name": "add_clientid",
      "method": "POST",
      "descr": "Add clientid in the cluster"
    },
    {
      "path": "/auth_clientid/:clientid",
      "name": "update_clientid",
      "method": "PUT",
      "descr": "Update clientid in the cluster"
    },
    {
      "path": "/auth_clientid/:clientid",
      "name": "delete_clientid",
      "method": "DELETE",
      "descr": "Delete clientid in the cluster"
    },
    {
      "path": "/auth_username",
      "name": "list_username",
      "method": "GET",
      "descr": "List available username in the cluster"
    },
    {
      "path": "/auth_username/:username",
      "name": "lookup_username",
      "method": "GET",
      "descr": "Lookup username in the cluster"
    },
    {
      "path": "/auth_username",
      "name": "add_username",
      "method": "POST",
      "descr": "Add username in the cluster"
    },
    {
      "path": "/auth_username/:username",
      "name": "update_username",
      "method": "PUT",
      "descr": "Update username in the cluster"
    },
    {
      "path": "/auth_username/:username",
      "name": "delete_username",
      "method": "DELETE",
      "descr": "Delete username in the cluster"
    },
    {
      "path": "/auth",
      "name": "auth_user",
      "method": "POST",
      "descr": "Authenticate an user"
    },
    {
      "path": "/users/",
      "name": "create_user",
      "method": "POST",
      "descr": "Create an user"
    },
    {
      "path": "/users/",
      "name": "list_users",
      "method": "GET",
      "descr": "List users"
    },
    {
      "path": "/users/:name",
      "name": "update_user",
      "method": "PUT",
      "descr": "Update an user"
    },
    {
      "path": "/users/:name",
      "name": "delete_user",
      "method": "DELETE",
      "descr": "Delete an user"
    },
    {
      "path": "/change_pwd/:username",
      "name": "change_pwd",
      "method": "PUT",
      "descr": "Change password for an user"
    },
    {
      "path": "/acl-cache",
      "name": "clean_acl_cache_all",
      "method": "DELETE",
      "descr": "Clean acl cache on all nodes"
    },
    {
      "path": "nodes/:node/acl-cache",
      "name": "clean_acl_cache_node",
      "method": "DELETE",
      "descr": "Clean acl cache on specific node"
    },
    {
      "path": "/alarms",
      "name": "list_all_alarms",
      "method": "GET",
      "descr": "List all alarms in the cluster"
    },
    {
      "path": "nodes/:node/alarms",
      "name": "list_node_alarms",
      "method": "GET",
      "descr": "List all alarms on a node"
    },
    {
      "path": "/alarms/activated",
      "name": "list_all_activated_alarms",
      "method": "GET",
      "descr": "List all activated alarm in the cluster"
    },
    {
      "path": "nodes/:node/alarms/activated",
      "name": "list_node_activated_alarms",
      "method": "GET",
      "descr": "List all activated alarm on a node"
    },
    {
      "path": "/alarms/deactivated",
      "name": "list_all_deactivated_alarms",
      "method": "GET",
      "descr": "List all deactivated alarm in the cluster"
    },
    {
      "path": "nodes/:node/alarms/deactivated",
      "name": "list_node_deactivated_alarms",
      "method": "GET",
      "descr": "List all deactivated alarm on a node"
    },
    {
      "path": "/alarms/deactivated",
      "name": "deactivate_alarm",
      "method": "POST",
      "descr": "Delete the special alarm on a node"
    },
    {
      "path": "/alarms/deactivated",
      "name": "delete_all_deactivated_alarms",
      "method": "DELETE",
      "descr": "Delete all deactivated alarm in the cluster"
    },
    {
      "path": "nodes/:node/alarms/deactivated",
      "name": "delete_node_deactivated_alarms",
      "method": "DELETE",
      "descr": "Delete all deactivated alarm on a node"
    },
    {
      "path": "/banned/",
      "name": "list_banned",
      "method": "GET",
      "descr": "List banned"
    },
    {
      "path": "/banned/",
      "name": "create_banned",
      "method": "POST",
      "descr": "Create banned"
    },
    {
      "path": "/banned/:who",
      "name": "delete_banned",
      "method": "DELETE",
      "descr": "Delete banned"
    },
    {
      "path": "/brokers/",
      "name": "list_brokers",
      "method": "GET",
      "descr": "A list of brokers in the cluster"
    },
    {
      "path": "/brokers/:node",
      "name": "get_broker",
      "method": "GET",
      "descr": "Get broker info of a node"
    },
    {
      "path": "/clients/",
      "name": "list_clients",
      "method": "GET",
      "descr": "A list of clients on current node"
    },
    {
      "path": "nodes/:node/clients/",
      "name": "list_node_clients",
      "method": "GET",
      "descr": "A list of clients on specified node"
    },
    {
      "path": "/clients/:clientid",
      "name": "lookup_client",
      "method": "GET",
      "descr": "Lookup a client in the cluster"
    },
    {
      "path": "nodes/:node/clients/:clientid",
      "name": "lookup_node_client",
      "method": "GET",
      "descr": "Lookup a client on the node"
    },
    {
      "path": "/clients/username/:username",
      "name": "lookup_client_via_username",
      "method": "GET",
      "descr": "Lookup a client via username in the cluster"
    },
    {
      "path": "/nodes/:node/clients/username/:username",
      "name": "lookup_node_client_via_username",
      "method": "GET",
      "descr": "Lookup a client via username on the node "
    },
    {
      "path": "/clients/:clientid",
      "name": "kickout_client",
      "method": "DELETE",
      "descr": "Kick out the client in the cluster"
    },
    {
      "path": "/clients/:clientid/acl_cache",
      "name": "clean_acl_cache",
      "method": "DELETE",
      "descr": "Clear the ACL cache of a specified client in the cluster"
    },
    {
      "path": "/clients/:clientid/acl_cache",
      "name": "list_acl_cache",
      "method": "GET",
      "descr": "List the ACL cache of a specified client in the cluster"
    },
    {
      "path": "/clients/:clientid/ratelimit",
      "name": "set_ratelimit_policy",
      "method": "POST",
      "descr": "Set the client ratelimit policy"
    },
    {
      "path": "/clients/:clientid/ratelimit",
      "name": "clean_ratelimit",
      "method": "DELETE",
      "descr": "Clear the ratelimit policy"
    },
    {
      "path": "/clients/:clientid/quota",
      "name": "set_quota_policy",
      "method": "POST",
      "descr": "Set the client quota policy"
    },
    {
      "path": "/clients/:clientid/quota",
      "name": "clean_quota",
      "method": "DELETE",
      "descr": "Clear the quota policy"
    },
    {
      "path": "/data/export",
      "name": "export",
      "method": "POST",
      "descr": "Export data"
    },
    {
      "path": "/data/export",
      "name": "list_exported",
      "method": "GET",
      "descr": "List exported file"
    },
    {
      "path": "/data/import",
      "name": "import",
      "method": "POST",
      "descr": "Import data"
    },
    {
      "path": "/data/file/:filename",
      "name": "download",
      "method": "GET",
      "descr": "Download data file to local"
    },
    {
      "path": "/data/file",
      "name": "upload",
      "method": "POST",
      "descr": "Upload data file from local"
    },
    {
      "path": "/data/file/:filename",
      "name": "delete",
      "method": "DELETE",
      "descr": "Delete data file"
    },
    {
      "path": "/listeners/",
      "name": "list_listeners",
      "method": "GET",
      "descr": "A list of listeners in the cluster"
    },
    {
      "path": "/nodes/:node/listeners",
      "name": "list_node_listeners",
      "method": "GET",
      "descr": "A list of listeners on the node"
    },
    {
      "path": "/listeners/:identifier/restart",
      "name": "restart_listener",
      "method": "PUT",
      "descr": "Restart a listener in the cluster"
    },
    {
      "path": "/nodes/:node/listeners/:identifier/restart",
      "name": "restart_node_listener",
      "method": "PUT",
      "descr": "Restart a listener on a node"
    },
    {
      "path": "/metrics",
      "name": "list_all_metrics",
      "method": "GET",
      "descr": "A list of metrics of all nodes in the cluster"
    },
    {
      "path": "/nodes/:node/metrics",
      "name": "list_node_metrics",
      "method": "GET",
      "descr": "A list of metrics of a node"
    },
    {
      "path": "/nodes/",
      "name": "list_nodes",
      "method": "GET",
      "descr": "A list of nodes in the cluster"
    },
    {
      "path": "/nodes/:node",
      "name": "get_node",
      "method": "GET",
      "descr": "Lookup a node in the cluster"
    },
    {
      "path": "/plugins/",
      "name": "list_all_plugins",
      "method": "GET",
      "descr": "List all plugins in the cluster"
    },
    {
      "path": "/nodes/:node/plugins/",
      "name": "list_node_plugins",
      "method": "GET",
      "descr": "List all plugins on a node"
    },
    {
      "path": "/nodes/:node/plugins/:plugin/load",
      "name": "load_node_plugin",
      "method": "PUT",
      "descr": "Load a plugin"
    },
    {
      "path": "/nodes/:node/plugins/:plugin/unload",
      "name": "unload_node_plugin",
      "method": "PUT",
      "descr": "Unload a plugin"
    },
    {
      "path": "/nodes/:node/plugins/:plugin/reload",
      "name": "reload_node_plugin",
      "method": "PUT",
      "descr": "Reload a plugin"
    },
    {
      "path": "/plugins/:plugin/unload",
      "name": "unload_plugin",
      "method": "PUT",
      "descr": "Unload a plugin in the cluster"
    },
    {
      "path": "/plugins/:plugin/reload",
      "name": "reload_plugin",
      "method": "PUT",
      "descr": "Reload a plugin in the cluster"
    },
    {
      "path": "/mqtt/subscribe",
      "name": "mqtt_subscribe",
      "method": "POST",
      "descr": "Subscribe a topic"
    },
    {
      "path": "/mqtt/publish",
      "name": "mqtt_publish",
      "method": "POST",
      "descr": "Publish a MQTT message"
    },
    {
      "path": "/mqtt/unsubscribe",
      "name": "mqtt_unsubscribe",
      "method": "POST",
      "descr": "Unsubscribe a topic"
    },
    {
      "path": "/mqtt/subscribe_batch",
      "name": "mqtt_subscribe_batch",
      "method": "POST",
      "descr": "Batch subscribes topics"
    },
    {
      "path": "/mqtt/publish_batch",
      "name": "mqtt_publish_batch",
      "method": "POST",
      "descr": "Batch publish MQTT messages"
    },
    {
      "path": "/mqtt/unsubscribe_batch",
      "name": "mqtt_unsubscribe_batch",
      "method": "POST",
      "descr": "Batch unsubscribes topics"
    },
    {
      "path": "/routes/",
      "name": "list_routes",
      "method": "GET",
      "descr": "List routes"
    },
    {
      "path": "/routes/:topic",
      "name": "lookup_routes",
      "method": "GET",
      "descr": "Lookup routes to a topic"
    },
    {
      "path": "/stats/",
      "name": "list_stats",
      "method": "GET",
      "descr": "A list of stats of all nodes in the cluster"
    },
    {
      "path": "/nodes/:node/stats/",
      "name": "lookup_node_stats",
      "method": "GET",
      "descr": "A list of stats of a node"
    },
    {
      "path": "/subscriptions/",
      "name": "list_subscriptions",
      "method": "GET",
      "descr": "A list of subscriptions in the cluster"
    },
    {
      "path": "/nodes/:node/subscriptions/",
      "name": "list_node_subscriptions",
      "method": "GET",
      "descr": "A list of subscriptions on a node"
    },
    {
      "path": "/subscriptions/:clientid",
      "name": "lookup_client_subscriptions",
      "method": "GET",
      "descr": "A list of subscriptions of a client"
    },
    {
      "path": "/nodes/:node/subscriptions/:clientid",
      "name": "lookup_client_subscriptions_with_node",
      "method": "GET",
      "descr": "A list of subscriptions of a client on the node"
    },
    {
      "path": "/emqx_prometheus",
      "name": "stats",
      "method": "GET",
      "descr": "Get emqx all stats info"
    },
    {
      "path": "/rules/",
      "name": "create_rule",
      "method": "POST",
      "descr": "Create a rule"
    },
    {
      "path": "/rules/:id",
      "name": "update_rule",
      "method": "PUT",
      "descr": "Update a rule"
    },
    {
      "path": "/rules/",
      "name": "list_rules",
      "method": "GET",
      "descr": "A list of all rules"
    },
    {
      "path": "/rules/:id",
      "name": "show_rule",
      "method": "GET",
      "descr": "Show a rule"
    },
    {
      "path": "/rules/:id",
      "name": "delete_rule",
      "method": "DELETE",
      "descr": "Delete a rule"
    },
    {
      "path": "/actions/",
      "name": "list_actions",
      "method": "GET",
      "descr": "A list of all actions"
    },
    {
      "path": "/actions/:name",
      "name": "show_action",
      "method": "GET",
      "descr": "Show an action"
    },
    {
      "path": "/resources/",
      "name": "list_resources",
      "method": "GET",
      "descr": "A list of all resources"
    },
    {
      "path": "/resources/",
      "name": "create_resource",
      "method": "POST",
      "descr": "Create a resource"
    },
    {
      "path": "/resources/:id",
      "name": "update_resource",
      "method": "PUT",
      "descr": "Update a resource"
    },
    {
      "path": "/resources/:id",
      "name": "show_resource",
      "method": "GET",
      "descr": "Show a resource"
    },
    {
      "path": "/resource_status/:id",
      "name": "get_resource_status",
      "method": "GET",
      "descr": "Get status of a resource"
    },
    {
      "path": "/resources/:id",
      "name": "start_resource",
      "method": "POST",
      "descr": "Start a resource"
    },
    {
      "path": "/resources/:id",
      "name": "delete_resource",
      "method": "DELETE",
      "descr": "Delete a resource"
    },
    {
      "path": "/resource_types/",
      "name": "list_resource_types",
      "method": "GET",
      "descr": "List all resource types"
    },
    {
      "path": "/resource_types/:name",
      "name": "show_resource_type",
      "method": "GET",
      "descr": "Show a resource type"
    },
    {
      "path": "/resource_types/:type/resources",
      "name": "list_resources_by_type",
      "method": "GET",
      "descr": "List all resources of a resource type"
    },
    {
      "path": "/rule_events/",
      "name": "list_events",
      "method": "GET",
      "descr": "List all events with detailed info"
    },
    {
      "path": "/sasl",
      "name": "add",
      "method": "POST",
      "descr": "Add authentication information"
    },
    {
      "path": "/sasl",
      "name": "delete",
      "method": "DELETE",
      "descr": "Delete authentication information"
    },
    {
      "path": "/sasl",
      "name": "update",
      "method": "PUT",
      "descr": "Update authentication information"
    },
    {
      "path": "/sasl",
      "name": "get",
      "method": "GET",
      "descr": "Get authentication information"
    },
    {
      "path": "/telemetry/status",
      "name": "enable_telemetry",
      "method": "PUT",
      "descr": "Enable or disbale telemetry"
    },
    {
      "path": "/telemetry/status",
      "name": "get_telemetry_status",
      "method": "GET",
      "descr": "Get telemetry status"
    },
    {
      "path": "/telemetry/data",
      "name": "get_telemetry_data",
      "method": "GET",
      "descr": "Get reported telemetry data"
    },
    {
      "path": "/topic-metrics",
      "name": "list_all_topic_metrics",
      "method": "GET",
      "descr": "A list of all topic metrics of all nodes in the cluster"
    },
    {
      "path": "/topic-metrics/:topic",
      "name": "list_topic_metrics",
      "method": "GET",
      "descr": "A list of specfied topic metrics of all nodes in the cluster"
    },
    {
      "path": "/topic-metrics",
      "name": "register_topic_metrics",
      "method": "POST",
      "descr": "Register topic metrics"
    },
    {
      "path": "/topic-metrics",
      "name": "unregister_all_topic_metrics",
      "method": "DELETE",
      "descr": "Unregister all topic metrics"
    },
    {
      "path": "/topic-metrics/:topic",
      "name": "unregister_topic_metrics",
      "method": "DELETE",
      "descr": "Unregister topic metrics"
    },
    {
      "path": "/modules/",
      "name": "list_all_modules",
      "method": "GET",
      "descr": "List all modules in the cluster"
    },
    {
      "path": "/nodes/:node/modules/",
      "name": "list_node_modules",
      "method": "GET",
      "descr": "List all modules on a node"
    },
    {
      "path": "/nodes/:node/modules/:module/load",
      "name": "load_node_module",
      "method": "PUT",
      "descr": "Load a module"
    },
    {
      "path": "/nodes/:node/modules/:module/unload",
      "name": "unload_node_module",
      "method": "PUT",
      "descr": "Unload a module"
    },
    {
      "path": "/nodes/:node/modules/:module/reload",
      "name": "reload_node_module",
      "method": "PUT",
      "descr": "Reload a module"
    },
    {
      "path": "/modules/:module/load",
      "name": "load_module",
      "method": "PUT",
      "descr": "load a module in the cluster"
    },
    {
      "path": "/modules/:module/unload",
      "name": "unload_module",
      "method": "PUT",
      "descr": "Unload a module in the cluster"
    },
    {
      "path": "/modules/:module/reload",
      "name": "reload_module",
      "method": "PUT",
      "descr": "Reload a module in the cluster"
    }
  ],
  "code": 0
}

C:\temp> curl --basic -u admin:public "http://localhost:8081/api/v4/resource_types" 2> nul | json_pp
{
  "data": [
    {
      "title": {
        "zh": "EMQX Bridge",
        "en": "EMQX Bridge"
      },
      "provider": "emqx_bridge_mqtt",
      "params": {
        "reconnect_interval": {
          "type": "string",
          "title": {
            "zh": "重连间隔",
            "en": "Reconnect Interval"
          },
          "required": false,
          "order": 4,
          "description": {
            "zh": "重连间隔",
            "en": "Reconnect Interval of bridge"
          },
          "default": "30s"
        },
        "pool_size": {
          "type": "number",
          "title": {
            "zh": "连接池大小",
            "en": "Pool Size"
          },
          "required": true,
          "order": 3,
          "description": {
            "zh": "连接池大小",
            "en": "MQTT/RPC Connection Pool Size"
          },
          "default": 8
        },
        "mountpoint": {
          "type": "string",
          "title": {
            "zh": "桥接挂载点",
            "en": "Bridge MountPoint"
          },
          "required": false,
          "order": 2,
          "description": {
            "zh": "桥接主题的挂载点
示例: 本地节点向 `topic1` 发消息,远程桥接节点的主题会变换为 `bridge/aws/${node}/topic1`", "en": "MountPoint for bridge topic
Example: The topic of messages sent to `topic1` on local node will be transformed to `bridge/aws/${node}/topic1`" }, "default": "bridge/emqx/${node}/" }, "disk_cache": { "type": "string", "title": { "zh": "磁盘缓存", "en": "Disk Cache" }, "required": false, "order": 6, "enum": [ "on", "off" ], "description": { "zh": "当桥接断开时用于控制是否将消息缓存到本地磁盘队列上", "en": "The flag which determines whether messages can be cached on local disk when bridge is disconnected" }, "default": "off" }, "batch_size": { "type": "number", "title": { "zh": "批处理大小", "en": "Batch Size" }, "required": false, "order": 5, "description": { "zh": "批处理大小", "en": "Batch Size" }, "default": 32 }, "address": { "type": "string", "title": { "zh": "EMQ X 节点名称", "en": "EMQ X Node Name" }, "required": true, "order": 1, "description": { "zh": "远程 EMQ X 节点名称 ", "en": "EMQ X Remote Node Name" }, "default": "emqx2@127.0.0.1" } }, "name": "bridge_rpc", "description": { "zh": "EMQ X RPC 消息桥接", "en": "EMQ X RPC Bridge" } }, { "title": { "zh": "MQTT Bridge", "en": "MQTT Bridge" }, "provider": "emqx_bridge_mqtt", "params": { "username": { "type": "string", "title": { "zh": "用户名", "en": "Username" }, "required": false, "order": 5, "description": { "zh": "连接远程 Broker 的用户名", "en": "Username for connecting to remote MQTT Broker" }, "default": "" }, "ssl": { "type": "boolean", "title": { "zh": "开启SSL链接", "en": "Enable SSL" }, "order": 14, "description": { "zh": "是否开启 SSL", "en": "Enable SSL or not" }, "default": false }, "retry_interval": { "type": "string", "title": { "zh": "重传间隔", "en": "Retry interval" }, "required": false, "order": 12, "description": { "zh": "消息重传间隔", "en": "Retry interval for bridge QoS1 message delivering" }, "default": "20s" }, "reconnect_interval": { "type": "string", "title": { "zh": "重连间隔", "en": "Reconnect Interval" }, "required": false, "order": 11, "description": { "zh": "重连间隔", "en": "Reconnect interval of bridge:
" }, "default": "30s" }, "proto_ver": { "type": "string", "title": { "zh": "协议版本", "en": "Protocol Version" }, "required": false, "order": 9, "enum": [ "mqttv3", "mqttv4", "mqttv5" ], "description": { "zh": "MQTT 协议版本", "en": "MQTTT Protocol version" }, "default": "mqttv4" }, "pool_size": { "type": "number", "title": { "zh": "连接池大小", "en": "Pool Size" }, "required": true, "order": 2, "description": { "zh": "连接池大小", "en": "MQTT Connection Pool Size" }, "default": 8 }, "password": { "type": "password", "title": { "zh": "密码", "en": "Password" }, "required": false, "order": 6, "description": { "zh": "连接远程 Broker 的密码", "en": "Password for connecting to remote MQTT Broker" }, "default": "" }, "mountpoint": { "type": "string", "title": { "zh": "桥接挂载点", "en": "Bridge MountPoint" }, "required": false, "order": 7, "description": { "zh": "桥接主题的挂载点:
示例: 本地节点向 `topic1` 发消息,远程桥接节点的主题会变换为 `bridge/aws/${node}/topic1`", "en": "MountPoint for bridge topic:
Example: The topic of messages sent to `topic1` on local node will be transformed to `bridge/aws/${node}/topic1`" }, "default": "bridge/aws/${node}/" }, "keyfile": { "type": "file", "title": { "zh": "SSL 密钥文件", "en": "SSL Keyfile" }, "required": false, "order": 17, "description": { "zh": "客户端密钥路径", "en": "The file path of the client keyfile" }, "default": "etc/certs/client-key.pem" }, "keepalive": { "type": "string", "title": { "zh": "心跳间隔", "en": "Keepalive" }, "required": false, "order": 10, "description": { "zh": "心跳间隔", "en": "Keepalive" }, "default": "60s" }, "disk_cache": { "type": "string", "title": { "zh": "磁盘缓存", "en": "Disk Cache" }, "required": false, "order": 8, "enum": [ "on", "off" ], "description": { "zh": "当桥接断开时用于控制是否将消息缓存到本地磁盘队列上", "en": "The flag which determines whether messages can be cached on local disk when bridge is disconnected" }, "default": "off" }, "clientid": { "type": "string", "title": { "zh": "客户端 Id", "en": "ClientId" }, "required": true, "order": 3, "description": { "zh": "连接远程 Broker 的 ClientId", "en": "ClientId for connecting to remote MQTT broker" }, "default": "client" }, "ciphers": { "type": "string", "title": { "zh": "SSL 加密算法", "en": "SSL Ciphers" }, "required": false, "order": 18, "description": { "zh": "SSL 加密算法", "en": "SSL Ciphers" }, "default": "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA" }, "certfile": { "type": "file", "title": { "zh": "SSL 客户端证书", "en": "SSL Certfile" }, "required": false, "order": 16, "description": { "zh": "客户端证书路径", "en": "The file path of the client certfile" }, "default": "etc/certs/client-cert.pem" }, "cacertfile": { "type": "file", "title": { "zh": "CA 证书", "en": "CA certificates" }, "required": false, "order": 15, "description": { "zh": "CA 证书路径", "en": "The file path of the CA certificates" }, "default": "etc/certs/cacert.pem" }, "bridge_mode": { "type": "boolean", "title": { "zh": "桥接模式", "en": "Bridge Mode" }, "required": false, "order": 13, "description": { "zh": "MQTT 连接是否为桥接模式", "en": "Bridge mode for MQTT bridge connection" }, "default": false }, "append": { "type": "boolean", "title": { "zh": "附加 GUID", "en": "Append GUID" }, "required": false, "order": 4, "description": { "zh": "是否将GUID附加到 MQTT ClientId 后", "en": "Append GUID to MQTT ClientId?" }, "default": true }, "address": { "type": "string", "title": { "zh": "远程 broker 地址", "en": " Broker Address" }, "required": true, "order": 1, "description": { "zh": "远程 MQTT Broker 的地址", "en": "The MQTT Remote Address" }, "default": "127.0.0.1:1883" } }, "name": "bridge_mqtt", "description": { "zh": "MQTT 消息桥接", "en": "MQTT Message Bridge" } }, { "title": { "zh": "WebHook", "en": "WebHook" }, "provider": "emqx_web_hook", "params": { "verify": { "type": "boolean", "title": { "zh": "校验服务器证书", "en": "Verify Server Certfile" }, "order": 8, "description": { "zh": "是否校验服务器证书。 默认客户端不会去校验服务器的证书,如果需要校验,请设置成true。", "en": "Whether to verify the server certificate. By default, the client will not verify the server's certificate. If verification is required, please set it to true." }, "default": false }, "url": { "type": "string", "title": { "zh": "请求 URL", "en": "Request URL" }, "required": true, "order": 1, "format": "url", "description": { "zh": "用于接收 Webhook 请求的服务器的 URL。", "en": "The URL of the server that will receive the Webhook requests." } }, "server_name_indication": { "type": "string", "title": { "zh": "服务器名称指示", "en": "Server Name Indication" }, "order": 9, "description": { "zh": "指定用于对端证书验证时使用的主机名,或者设置为 disable 以关闭此项验证。", "en": "Specify the hostname used for peer certificate verification, or set to disable to turn off this verification." } }, "request_timeout": { "type": "string", "title": { "zh": "请求超时时间时间", "en": "Request Timeout" }, "order": 3, "description": { "zh": "请求超时时间", "en": "Request Timeout In Seconds" }, "default": "5s" }, "pool_size": { "type": "number", "title": { "zh": "连接池大小", "en": "Pool Size" }, "order": 4, "description": { "zh": "连接池大小", "en": "Connection Pool" }, "default": 8 }, "keyfile": { "type": "file", "title": { "zh": "SSL Key", "en": "SSL Key" }, "order": 6, "description": { "zh": "SSL 私钥", "en": "Your ssl keyfile" }, "default": "" }, "connect_timeout": { "type": "string", "title": { "zh": "连接超时时间", "en": "Connect Timeout" }, "order": 2, "description": { "zh": "连接超时时间", "en": "Connect Timeout In Seconds" }, "default": "5s" }, "certfile": { "type": "file", "title": { "zh": "SSL Cert", "en": "SSL Cert" }, "order": 7, "description": { "zh": "SSL 证书", "en": "Your ssl certfile" }, "default": "" }, "cacertfile": { "type": "file", "title": { "zh": "CA 证书文件", "en": "CA Certificate File" }, "order": 5, "description": { "zh": "CA 证书文件", "en": "CA Certificate file" }, "default": "" } }, "name": "web_hook", "description": { "zh": "WebHook", "en": "WebHook" } } ], "code": 0 }




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/15/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
1756정성태9/23/201427497기타: 48. NVidia 제품의 과다한 디스크 사용 [2]
1755정성태9/22/201434281오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
1754정성태9/22/201424684VC++: 80. 내 컴퓨터에서 C++ AMP 코드가 실행이 될까요? [1]
1753정성태9/22/201420623오류 유형: 240. Lync로 세미나 참여 시 소리만 들리지 않는 경우 [1]
1752정성태9/21/201441073Windows: 100. 윈도우 8 - RDP 연결을 이용해 VNC처럼 사용자 로그온 화면을 공유하는 방법 [5]
1751정성태9/20/201438964.NET Framework: 464. 프로세스 간 통신 시 소켓 필요 없이 간단하게 Pipe를 열어 통신하는 방법 [1]파일 다운로드1
1750정성태9/20/201423837.NET Framework: 463. PInvoke 호출을 이용한 비동기 파일 작업파일 다운로드1
1749정성태9/20/201423738.NET Framework: 462. 커널 객체를 위한 null DACL 생성 방법파일 다운로드1
1748정성태9/19/201425391개발 환경 구성: 238. [Synergy] 여러 컴퓨터에서 키보드, 마우스 공유
1747정성태9/19/201428519오류 유형: 239. psexec 실행 오류 - The system cannot find the file specified.
1746정성태9/18/201426109.NET Framework: 461. .NET EXE 파일을 닷넷 프레임워크 버전에 상관없이 실행할 수 있을까요? - 두 번째 이야기 [6]파일 다운로드1
1745정성태9/17/201423045개발 환경 구성: 237. 리눅스 Integration Services 버전 업그레이드 하는 방법 [1]
1744정성태9/17/201431070.NET Framework: 460. GetTickCount / GetTickCount64와 0x7FFE0000 주솟값 [4]파일 다운로드1
1743정성태9/16/201420986오류 유형: 238. 설치 오류 - Failed to get size of pseudo bundle
1742정성태8/27/201426986개발 환경 구성: 236. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 [2]
1741정성태8/26/201421339.NET Framework: 459. GetModuleHandleEx로 알아보는 .NET 메서드의 DLL 모듈 관계파일 다운로드1
1740정성태8/25/201432528.NET Framework: 458. 닷넷 GC가 순환 참조를 해제할 수 있을까요? [2]파일 다운로드1
1739정성태8/24/201426576.NET Framework: 457. 교착상태(Dead-lock) 해결 방법 - Lock Leveling [2]파일 다운로드1
1738정성태8/23/201422069.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201419775VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201425597VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201418271.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201419934오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201426367.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201434489Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201427089개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...