성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>MQTT - emqx.io 서비스 소개</h1> <p> winget에서도 볼 수 있는 emqx.mqttx 서비스가 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:temp> <span style='color: blue; font-weight: bold'>winget search --name mqtt</span> Name Id Version --------------------------------------------------- MQTT Explorer thomasnordquist.MQTT-Explorer 0.3.5 mqttx emqx.mqttx 1.5.2 </pre> <br /> 해당 사이트에서 직접 zip 파일을 다운로드해,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > EMQ X Broker ; <a target='tab' href='https://www.emqx.io/downloads#broker'>https://www.emqx.io/downloads#broker</a> emqx-windows-4.3.2.zip ; <a target='tab' href='https://www.emqx.io/downloads/broker/v4.3.2/emqx-windows-4.3.2.zip'>https://www.emqx.io/downloads/broker/v4.3.2/emqx-windows-4.3.2.zip</a> </pre> <br /> 압축 해제 후 실행해 볼 수도 있습니다. 관련한 명령행 옵션은 다음의 문서에서 확인할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Start EMQ X ; <a target='tab' href='https://docs.emqx.io/en/broker/v4.3/getting-started/start.html'>https://docs.emqx.io/en/broker/v4.3/getting-started/start.html</a> </pre> <br /> 따라서 명령행에서 단순히 ".\bin\emqx start"를 관리자 권한으로 실행하면 erl.exe가 다음의 명령행으로 실행되고,<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> 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<br /> </div><br /> <br /> 서비스 상태는 이렇게 조회할 수 있으며,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\tmp\emqx> <span style='color: blue; font-weight: bold'>.\bin\emqx_ctl status</span> Node 'emqx@127.0.0.1' 4.3.2 is started </pre> <br /> 종료하려면 ".\bin\emqx stop" 명령어를 내리면 됩니다. <br /> <br /> <hr style='width: 50%' /><br /> <br /> EMQ X Broker는 REST API를 제공하기 때문에 아래의 문서에 따라 쉽게 테스트하는 것이 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HTTP API ; <a target='tab' href='https://docs.emqx.io/en/broker/v4.3/advanced/http-api.html#api-endpoints'>https://docs.emqx.io/en/broker/v4.3/advanced/http-api.html#api-endpoints</a> </pre> <br /> 몇 개를 추려서 한 번 실습해 볼까요? 우선 MQTT broker가 정상적으로 실행되었는지를 알 수 있습니다. (<a target='tab' href='https://www.sysnet.pe.kr/2/0/12311#json_pp'>json_pp 명령어</a>)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/brokers" 2> nul | json_ppM</span> { "data": [ { "version": "4.3.2", "uptime": "1 hours, 6 minutes, 17 seconds", "sysdescr": "EMQ X", "otp_release": "23.0/11.0", "node_status": "Running", <span style='color: blue; font-weight: bold'>"node": "emqx@127.0.0.1",</span> "datetime": "2021-06-04 23:08:26" } ], "code": 0 } </pre> <br /> 보는 바와 같이 "emqx@127.0.0.1"로 식별되는 노드 1개가 실행 중임을 알 수 있고, 해당 broker만 지정해 조회할 수도 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/brokers/emqx@127.0.0.1" </pre> <br /> node 개념이 있는 것을 보니 클러스터 구성이 가능한 것 같은데요, 그래서 nodes 조회로도 환경을 검색할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes" 2> nul | json_pp</span> { "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", <span style='color: blue; font-weight: bold'>"node": "emqx@127.0.0.1",</span> "memory_used": "54.37M", "memory_total": "71.69M", "max_fds": 16384, "load5": "0.00", "load15": "0.00", "load1": "0.00", "connections": 1 } ], "code": 0 } </pre> <br /> 이것 역시 node를 지정해 조회할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1" </pre> <br /> <hr style='width: 50%' /><br /> <br /> MQTT broker에 대한 서비스를 제공하는 listener를 다음의 명령어로 조회할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/listeners" 2> nul | json_pp</span> { "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 }, <span style='color: blue; font-weight: bold'>{ "shutdown_count": [], "protocol": "mqtt:tcp", "max_conns": 1024000, "listen_on": "0.0.0.0:1883", "identifier": "mqtt:tcp:external", "current_conns": 0, "acceptors": 8 },</span> { "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 }, <span style='color: blue; font-weight: bold'>{ "shutdown_count": [], "protocol": "http:management", "max_conns": 512, "listen_on": "8081", "current_conns": 1, "acceptors": 2 },</span> { "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 } </pre> <br /> 특정 리스너의 재시작도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X PUT "http://localhost:8081/api/v4/listeners/mqtt:tcp:external/restart"</span> {"code":0} </pre> <br /> 여기서 mqtt:management는 우리가 지금 REST API를 수행할 수 있도록 하는 listener입니다. 그리고, mqtt:tcp가 바로 MQTT broker의 endpoint인데요, 실제로 이 포트로 지난 글에서 만든 MqttClient를,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - MQTT를 이용한 클라이언트/서버(Broker) 통신 예제 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12661'>https://www.sysnet.pe.kr/2/0/12661</a> </pre> <br /> 포트만 mqtt:tcp가 대기하는 1883으로 바꾸면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ManagedMqttClientOptions options = new ManagedMqttClientOptions(); options.ClientOptions = new MqttClientOptions() { ClientId = "MyMqttClient", ChannelOptions = new MqttClientTcpOptions { Server = "localhost", <span style='color: blue; font-weight: bold'>Port = 1883,</span> } }; </pre> <br /> 접속이 됩니다. 위와 같이 변경하고 실행하면 EMQ X Broker에 연결된 MQTT client를 다음의 API로 조회할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients?_page=1&_limit=10" </pre> <br /> 실습할 때는 클라이언트가 몇 개 없을 테니 그냥 전체 조회를 해도 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients" 2> nul | json_pp</span> { "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, <span style='color: blue; font-weight: bold'>"clientid": "MyMqttClient",</span> "username": "undefined", "max_awaiting_rel": 100, "clean_start": true, "connected": true, "send_cnt": 3, "ip_address": "127.0.0.1" } ], "code": 0 } </pre> <br /> 보는 바와 같이 우리가 MQTTnet을 이용해 만든 "MyMqttClient"가 보입니다. 전체 조회뿐만 아니라, 특정 clientid를 지정해 조회할 수도 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients/MyMqttClient" </pre> <br /> 해당 클라이언트를 강제로 접속 해제하는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > curl -i --basic -u admin:public -X DELETE "http://localhost:8081/api/v4/clients/MyMqttClient" </pre> <br /> 그런데 위의 명령어를 내린 후 다시 "GET /api/v4/clients"로 알아 보면 해당 클라이언트가 여전히 붙어 있습니다. 하지만 분명히 연결 종료는 되었는데, 이것은 Process Explorer를 통해 TCP 연결이 삭제된 것으로 확인할 수 있습니다. 연결이 여전히 붙어있는 이유는, 우리가 만든 MqttClient의 코드 중에 재접속을 자동으로 하는 옵션이 있었기 때문입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > options.AutoReconnectDelay = TimeSpan.FromSeconds(1); </pre> <br /> 이 설정으로 인해 1초 만에 재차 연결을 했기 때문에 "GET /api/v4/clients"를 했을 때 여전히 연결이 붙은 것처럼 보인 것입니다.<br /> <br /> 이하, 다음의 명령들은 직관적으로 어떤 결과를 나타낼지 상상할 수 있을 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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 <span style='color: blue; font-weight: bold'>DELETE</span> "http://localhost:8081/api/v4/nodes/emqx@127.0.0.1/clients/MyMqttClient" </pre> <br /> (참고로, 위에서 nodes/.../clients/...로 DELETE하는 명령어는 제가 실습했을 때 정상 동작을 하지 않았습니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 재미있는 것은, Broker에 등록된 topic과 그 클라이언트 목록을 조회하는 것도 가능해서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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" </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/subscriptions" 2> nul | json_pp</span> { "meta": { "page": 1, "limit": 10000, "hasnext": false, "count": 2 }, "data": [ { <span style='color: blue; font-weight: bold'>"topic": "xyz",</span> "qos": 0, "node": "emqx@127.0.0.1", <span style='color: blue; font-weight: bold'>"clientid": "MyMqttClient"</span> }, { <span style='color: blue; font-weight: bold'>"topic": "abc",</span> "qos": 0, "node": "emqx@127.0.0.1", <span style='color: blue; font-weight: bold'>"clientid": "MyMqttClient"</span> } ], "code": 0 } </pre> <br /> 개발 시 테스트 용도로 편리하게 사용할 수 있습니다. (위에서 "xyz", "abc"가 나온 것은 MqttClient의 코드에서 해당 토픽을 구독하도록 했기 때문입니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > await managedClient.SubscribeAsync(new MqttTopicFilter { <span style='color: blue; font-weight: bold'>Topic = "xyz"</span>, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); await managedClient.SubscribeAsync(new MqttTopicFilter { <span style='color: blue; font-weight: bold'>Topic = "abc"</span>, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }); </pre> <br /> 아울러 토픽에 메시지를 전송할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>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\"}"</span> {"code":0} </pre> <br /> 특정 클라이언트에 토픽을 구독시키는 것도 가능합니다. 아래는 "test1", "test2"라는 새로운 토픽을 현재 연결된 MyMqttClient에 구독시키고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/subscribe" -d "{\"topics\":\"test1,test2\",\"qos\":1,\"clientid\":\"MyMqttClient\"}"</span> {"code":0} </pre> <br /> 확인은 publish API로 실행하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> curl --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/publish" -d "{<span style='color: blue; font-weight: bold'>\"topic\":\"test1\"</span>,\"payload\":\"Hello World\",\"qos\":1,\"retain\":false,\"clientid\":\"MyMqttClient3\"}" {"code":0} </pre> <br /> MqttClient 화면에 ">> RECEIVED: test1"의 출력으로 알 수 있습니다.<br /> <br /> 당연히, 토픽 구독을 해제하는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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\"}" </pre> <br /> <hr style='width: 50%' /><br /> <br /> REST API를 보면 사용자 계정으로 조회하는 것도 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients/username/{username}" </pre> <br /> 위의 명령어는 해당 사용자 계정으로 실행 중인 모든 클라이언트를 열거하는데요, 이를 실습하기 위해서는 MqttClient의 코드에서 "Credentials" 속성을 다음과 같이 부여해 실행해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > options.ClientOptions = new MqttClientOptions() { ClientId = "MyMqttClient", <span style='color: blue; font-weight: bold'>Credentials = new RandomPassword(),</span> ChannelOptions = new MqttClientTcpOptions { Server = "localhost", Port = 1883, } }; public class RandomPassword : IMqttClientCredentials { public byte[] Password => Guid.NewGuid().ToByteArray(); public string Username => "the_test_user"; } </pre> <br /> 위와 같이 변경한 MqttClient를 실행해 broker 측으로 연결하면 이제 다음과 같은 결과를 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/clients/username/the_test_user"</span> {"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} </pre> <br /> acl_cache도 조회 및 삭제할 수 있다고 하는데, 아직 이에 대한 MqttClient 측에서의 사용법은 모르겠습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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" </pre> <br /> route도 있다는데 아직 이것도 개념을 모르니 실행만 해봤습니다. (route 조회와 topic 조회의 차이점을 모르겠습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/routes"</span> {"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> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/routes/xyz"</span> {"data":[{"topic":"xyz","node":"emqx@127.0.0.1"}],"code":0} </pre> <br /> <hr style='width: 50%' /><br /> <br /> 알아본 김에 기타 여러 가지 명령어도 보겠습니다. 보니까, plugins도 지원하는 것 같고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/plugins"</span> {"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} </pre> <br /> "name" 필드로 특정 plugin을 로드/언로드하는 것이 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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} </pre> <br /> 클러스터 내의 통계 수치와 상태 정보를 각각 metrics와 stats로 구할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/metrics"</span> {"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> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/stats"</span> {"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} </pre> <br /> 알람 기능도 있는 듯하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/alarms/present"</span> Not found. C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/alarms/history"</span> Not found. </pre> <br /> blacklist 등의 관리 기능도 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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" </pre> <br /> 대단하군요. ^^ 이런 것들을 일일이 MQTTnet 라이브러리를 이용해 구현하는 것이 귀찮을 수 있겠다는 생각이 들 정도로 잘해놨습니다. 게다가 web_hook까지 제공하는 것 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public -X GET "http://localhost:8081/api/v4/resource_types/web_hook"</span> {"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} </pre> <br /> 뜬금없이 ^^ 리소스 이름에 중국어 문자가 나오는데요, EMQX라는 회사 자체가 중국계 인물들로 이뤄진 스타트업으로 보입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > About / Company ; <a target='tab' href='https://www.emqx.io/about'>https://www.emqx.io/about</a> </pre> <br /> 암튼, 결국 MQTT broker를 혹시나 저런 규모로 만들 생각이라면 그냥 이미 저렇게 만들어 둔 서비스를 사용하는 것도 나쁘지 않은 선택으로 보입니다. 그 외에, 자신만의 소프트웨어에 간단하게 구현할 목적이라면 MQTTnet 라이브러리를 통해 만드는 것이 좋겠고!<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이 글을 실습하면서, "Bad Request" 오류가 발생한다면?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> url -i --basic -u admin:public -X POST "http://localhost:8081/api/v4/mqtt/publish" -d <span style='color: blue; font-weight: bold'>'{"topic":"abc","payload":"Hello World","qos":1,"retain":false,"clientid":"MyMqttClient"}'</span> 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 </pre> <br /> 다음의 글에 정리한,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > curl로 호출할 때 발생하는 오류 정리 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11426'>https://www.sysnet.pe.kr/2/0/11426</a> </pre> <br /> 내용에 따라 윈도우에서는 json 텍스트를 다음과 같이 다뤄야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > '{"topic":"abc","payload":"Hello World","qos":1,"retain":false,"clientid":"MyMqttClient"}' ==> "{\"topic\":\"abc\",\"payload\":\"Hello World\",\"qos\":1,\"retain\":false,\"clientid\":\"MyMqttClient\"}" </pre> <br /> 마지막으로, 아래는 EMQ X Broker가 제공하는 모든 API들의 endpoint와, 리소스 타입들입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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 } </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\temp> <span style='color: blue; font-weight: bold'>curl --basic -u admin:public "http://localhost:8081/api/v4/resource_types" 2> nul | json_pp</span> { "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": "桥接主题的挂载点<br/>示例: 本地节点向 `topic1` 发消息,远程桥接节点的主题会变换为 `bridge/aws/${node}/topic1`", "en": "MountPoint for bridge topic<br/>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:<br/>" }, "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": "桥接主题的挂载点:<br/>示例: 本地节点向 `topic1` 发消息,远程桥接节点的主题会变换为 `bridge/aws/${node}/topic1`", "en": "MountPoint for bridge topic:<br/>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 } </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1583
(왼쪽의 숫자를 입력해야 합니다.)