Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 577. MQTT - emqx.io 서비스 소개 [링크 복사], [링크+제목 복사]
조회: 9999
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




1  [2]  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13576정성태3/8/20241543닷넷: 2228. .NET Profiler - IMetaDataEmit2::DefineMethodSpec 사용법
13575정성태3/7/20241676닷넷: 2227. 최신 C# 문법을 .NET Framework 프로젝트에 쓸 수 있을까요?
13574정성태3/6/20241557닷넷: 2226. C# - "Docker Desktop for Windows" Container 환경에서의 IPv6 DualMode 소켓
13573정성태3/5/20241563닷넷: 2225. Windbg - dumasync로 분석하는 async/await 호출
13572정성태3/4/20241639닷넷: 2224. C# - WPF의 Dispatcher Queue로 알아보는 await 호출의 hang 현상파일 다운로드1
13571정성태3/1/20241600닷넷: 2223. C# - await 호출과 WPF의 Dispatcher Queue 동작 확인파일 다운로드1
13570정성태2/29/20241627닷넷: 2222. C# - WPF의 Dispatcher Queue 동작 확인파일 다운로드1
13569정성태2/28/20241539닷넷: 2221. C# - LoadContext, LoadFromContext 그리고 GAC파일 다운로드1
13568정성태2/27/20241602닷넷: 2220. C# - .NET Framework 프로세스의 LoaderOptimization 설정을 확인하는 방법파일 다운로드1
13567정성태2/27/20241613오류 유형: 898. .NET Framework 3.5 이하에서 mscoree.tlb 참조 시 System.BadImageFormatException파일 다운로드1
13566정성태2/27/20241626오류 유형: 897. Windows 7 SDK 설치 시 ".NET Development" 옵션이 비활성으로 선택이 안 되는 경우
13565정성태2/23/20241464닷넷: 2219. .NET CLR2 보안 모델에서의 개별 System.Security.Permissions 제어
13564정성태2/22/20241614Windows: 259. Hyper-V Generation 1 유형의 VM을 Generation 2 유형으로 바꾸는 방법
13563정성태2/21/20241644디버깅 기술: 196. windbg - async/await 비동기인 경우 메모리 덤프 분석의 어려움
13562정성태2/21/20241643오류 유형: 896. ASP.NET - .NET Framework 기본 예제에서 System.Web에 대한 System.IO.FileNotFoundException 예외 발생
13561정성태2/20/20241741닷넷: 2218. C# - (예를 들어, Socket) 비동기 I/O에 대한 await 호출 시 CancellationToken을 이용한 취소파일 다운로드1
13560정성태2/19/20241744디버깅 기술: 195. windbg 분석 사례 - Semaphore 잠금으로 인한 Hang 현상 (닷넷)
13559정성태2/19/20242623오류 유형: 895. ASP.NET - System.Security.SecurityException: 'Requested registry access is not allowed.'
13558정성태2/18/20241819닷넷: 2217. C# - 최댓값이 1인 SemaphoreSlim 보다 Mutex 또는 lock(obj)를 선택하는 것이 나은 이유
13557정성태2/18/20241572Windows: 258. Task Scheduler의 Author 속성 값을 변경하는 방법
13556정성태2/17/20241653Windows: 257. Windows - Symbolic (hard/soft) Link 및 Junction 차이점
13555정성태2/15/20241951닷넷: 2216. C# - SemaphoreSlim 사용 시 주의점
13554정성태2/15/20241707VS.NET IDE: 189. Visual Studio - 닷넷 소스코드 디컴파일 찾기가 안 될 때
13553정성태2/14/20241734닷넷: 2215. windbg - thin/fat lock 없이 동작하는 Monitor.Wait + Pulse
13552정성태2/13/20241684닷넷: 2214. windbg - Monitor.Enter의 thin lock과 fat lock
13551정성태2/12/20242016닷넷: 2213. ASP.NET/Core 웹 응용 프로그램 - 2차 스레드의 예외로 인한 비정상 종료
1  [2]  3  4  5  6  7  8  9  10  11  12  13  14  15  ...