design\project\Node-Red 读书笔记(IOT)(四)

728 阅读9分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

压力机和测试机在同一台服务器上,结果仅供参考

Node-Red 压力测试

我们都知道通过拖拽,就能配置 http 端口,就能订阅 mqtt 主题,那么其性能到底怎么样呢?博主进行了简单的压测

http 服务器

image.png

服务器负载

image.png

其中一个结果

博主安装了 node-red-redis 模块

image.png

接收 login 的 post 请求,从 redis 中读取一个 token 返回给客户端

脚本:

[{"id":"a7367301.a0ecd","type":"tab","label":"流程 1","disabled":false,"info":""},{"id":"69a64e3d.84da3","type":"http in","z":"a7367301.a0ecd","name":"","url":"/login","method":"get","upload":false,"swaggerDoc":"","x":160,"y":140,"wires":[["38d357a7.b69d08"]]},{"id":"43e49c36.dc7624","type":"http response","z":"a7367301.a0ecd","name":"","statusCode":"","headers":{},"x":490,"y":140,"wires":[]},{"id":"8cb4b74e.602c28","type":"comment","z":"a7367301.a0ecd","name":"登录表单","info":"The `HTTP In` node can listen for POST requests.  It returns the posted data as `msg.payload`.\n\nSee Node-RED cookbook items(\n[post raw data(https://cookbook.nodered.org/http/post-raw-data-to-a-flow), [post form data](https://cookbook.nodered.org/http/post-form-data-to-a-flow), [post JSON data](https://cookbook.nodered.org/http/post-json-data-to-a-flow)\n) for details.","x":120,"y":100,"wires":[]},{"id":"38d357a7.b69d08","type":"template","z":"a7367301.a0ecd","name":"page","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<html>  \n   <head>\n   </head>\n   <body>\n      <form action=\"/process_login\" method=\"POST\">\n         <label for=\"username\">Login</label>\n         <input name=\"username\" type=\"text\" />\n         <br />\n         <label for=\"password\">Password</label>\n         <input name=\"password\" type=\"password\" />\n         <br />\n         <input type=\"submit\" />\n      </form>\n   </body>\n</html>  ","output":"str","x":350,"y":140,"wires":[["43e49c36.dc7624"]]},{"id":"bd12b53350231a15","type":"http in","z":"a7367301.a0ecd","name":"login 登录表单","url":"/login","method":"post","upload":false,"swaggerDoc":"","x":170,"y":220,"wires":[["e68d13f08734d337"]]},{"id":"e68d13f08734d337","type":"function","z":"a7367301.a0ecd","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":220,"wires":[["6ec8a900d2b626d8"]]},{"id":"e0facb1e529832ba","type":"debug","z":"a7367301.a0ecd","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":550,"y":280,"wires":[]},{"id":"efeb12614478ecca","type":"http response","z":"a7367301.a0ecd","name":"","statusCode":"","headers":{},"x":1050,"y":220,"wires":[]},{"id":"6ec8a900d2b626d8","type":"change","z":"a7367301.a0ecd","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"HTTPTOKENID069e9378c3974665b9605e864f64f5c6","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":220,"wires":[["fc0daf896511a9ec"]]},{"id":"fc0daf896511a9ec","type":"redis-command","z":"a7367301.a0ecd","server":"319f929161f928c2","command":"GET","name":"","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":760,"y":220,"wires":[["ab4f1661eb205102"]]},{"id":"80433c4d.36b5a8","type":"redis-command","z":"a7367301.a0ecd","server":"319f929161f928c2","command":"hget","name":"","topic":"IMWhash","params":"[\"imwAccessToken\"]","paramsType":"json","payloadType":"json","block":false,"x":460,"y":1120,"wires":[["ce325093.9c43a8"]]},{"id":"ce325093.9c43a8","type":"debug","z":"a7367301.a0ecd","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":750,"y":1120,"wires":[]},{"id":"88687136.c00c38","type":"inject","z":"a7367301.a0ecd","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":210,"y":1120,"wires":[["80433c4d.36b5a8"]]},{"id":"ab4f1661eb205102","type":"function","z":"a7367301.a0ecd","name":"","func":"msg.payload = Object.assign({success:true,code:200},{msg:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":220,"wires":[["efeb12614478ecca"]]},{"id":"319f929161f928c2","type":"redis-config","name":"本地 redis","options":"{\"port\":6379,\"host\":\"127.0.0.1\"}","cluster":false,"optionsType":"json"}]

image.png

Mqtt 客户端

这个,直接说结论

  1. 收到消息后,直接写入 redis,向 mqtt broker 推送消息,Node-Red 假死,过了 10 分钟后恢复

image.png

image.png

  1. 添加限流节点,Node-Red 不再假死
    • 大量请求压在 限流节点,可以看到其 mqtt-out 频率在博主的笔记本上 大概 300~500 请求/秒,和消息大小相关

5431d562bd7173ea5c1e17e2f0ce124.png

  1. 消息重复,Qos = 2 无效,流程中会收到重复消息
    • Node-Red 的 mqtt 客户端回复 PUBCOMP 和 PUBACK 都是异步回复的,服务端在收到 PUBACK 之前会重发该消息
    • 而 Node-Red 在大并发下处理不过来
    • 那么这个大并发是多少呢?经过测试 -n 1000 -c 1 是没有问题的,加到 -n 1000 -c 10 就已经大并发了

5431d562bd7173ea5c1e17e2f0ce124.png 所用脚本:

[{"id":"e8e45cde31fa8e1f","type":"tab","label":"流程 1","disabled":false,"info":""},{"id":"3de9573a5bc33d37","type":"group","z":"e8e45cde31fa8e1f","name":"测试 MQTT 消息吞吐率","style":{"label":true,"stroke":"#ffff3f","label-position":"n","color":"#ff0000","fill":"#ffffbf"},"nodes":["d6a064173cdee4c2","ce512c0aa71eac51","f7d1b383be5ac29c","ed1ca259ab35453d","0671b8a36c2065e0","0a9a1df8abe43f83","7fa7a4d3e6bc0fba","e461186d3bb5085a"],"x":14,"y":259,"w":932,"h":142},{"id":"bbd9331fc4742078","type":"http in","z":"e8e45cde31fa8e1f","name":"","url":"/login","method":"get","upload":false,"swaggerDoc":"","x":160,"y":140,"wires":[["533f17269e1854fe"]]},{"id":"52d328f6610803fc","type":"http response","z":"e8e45cde31fa8e1f","name":"","statusCode":"","headers":{},"x":490,"y":140,"wires":[]},{"id":"4e9dce3de3a52e2b","type":"comment","z":"e8e45cde31fa8e1f","name":"登录表单","info":"The `HTTP In` node can listen for POST requests.  It returns the posted data as `msg.payload`.\n\nSee Node-RED cookbook items(\n[post raw data(https://cookbook.nodered.org/http/post-raw-data-to-a-flow), [post form data](https://cookbook.nodered.org/http/post-form-data-to-a-flow), [post JSON data](https://cookbook.nodered.org/http/post-json-data-to-a-flow)\n) for details.","x":120,"y":100,"wires":[]},{"id":"533f17269e1854fe","type":"template","z":"e8e45cde31fa8e1f","name":"page","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<html>  \n   <head>\n   </head>\n   <body>\n      <form action=\"/process_login\" method=\"POST\">\n         <label for=\"username\">Login</label>\n         <input name=\"username\" type=\"text\" />\n         <br />\n         <label for=\"password\">Password</label>\n         <input name=\"password\" type=\"password\" />\n         <br />\n         <input type=\"submit\" />\n      </form>\n   </body>\n</html>  ","output":"str","x":350,"y":140,"wires":[["52d328f6610803fc"]]},{"id":"5c81dc92952dc1c2","type":"http in","z":"e8e45cde31fa8e1f","name":"login 登录表单","url":"/login","method":"post","upload":false,"swaggerDoc":"","x":630,"y":140,"wires":[["2e9372dd54d2c3ef"]]},{"id":"7fa7a4d3e6bc0fba","type":"debug","z":"e8e45cde31fa8e1f","g":"3de9573a5bc33d37","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":470,"y":360,"wires":[]},{"id":"2e9372dd54d2c3ef","type":"http response","z":"e8e45cde31fa8e1f","name":"","statusCode":"","headers":{},"x":790,"y":140,"wires":[]},{"id":"7b97081ab4c87056","type":"redis-command","z":"e8e45cde31fa8e1f","server":"319f929161f928c2","command":"hget","name":"","topic":"IMWhash","params":"[\"imwAccessToken\"]","paramsType":"json","payloadType":"json","block":false,"x":460,"y":1120,"wires":[["a544ab373d09b36e"]]},{"id":"a544ab373d09b36e","type":"debug","z":"e8e45cde31fa8e1f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":750,"y":1120,"wires":[]},{"id":"fdd8936c5b909cda","type":"inject","z":"e8e45cde31fa8e1f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":210,"y":1120,"wires":[["7b97081ab4c87056"]]},{"id":"494686e5d3f0e5dd","type":"function","z":"e8e45cde31fa8e1f","name":"","func":"msg.payload = Object.assign({success:true,code:200},{msg:msg.payload});\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":930,"y":140,"wires":[[]]},{"id":"d6a064173cdee4c2","type":"mqtt in","z":"e8e45cde31fa8e1f","g":"3de9573a5bc33d37","name":"","topic":"test","qos":"2","datatype":"auto","broker":"465afaab07409795","nl":false,"rap":true,"rh":0,"inputs":0,"x":90,"y":300,"wires":[["0a9a1df8abe43f83"]]},{"id":"ce512c0aa71eac51","type":"mqtt out","z":"e8e45cde31fa8e1f","g":"3de9573a5bc33d37","name":"","topic":"test2","qos":"0","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"465afaab07409795","x":870,"y":360,"wires":[]},{"id":"f7d1b383be5ac29c","type":"redis-command","z":"e8e45cde31fa8e1f","g":"3de9573a5bc33d37","server":"319f929161f928c2","command":"INCR","name":"","topic":"counter","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":490,"y":300,"wires":[["ed1ca259ab35453d","7fa7a4d3e6bc0fba"]]},{"id":"ed1ca259ab35453d","type":"function","z":"e8e45cde31fa8e1f","g":"3de9573a5bc33d37","name":"","func":"msg.payload = [msg.payload, '11111111111111111111111111111111111111']\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":670,"y":300,"wires":[["e461186d3bb5085a"]]},{"id":"0671b8a36c2065e0","type":"change","z":"e8e45cde31fa8e1f","g":"3de9573a5bc33d37","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{     \"success\": true,     \"code\": 200,     \"msg\": \"rO0ABXNyAB5jb20uZGhjYy5iYXNpYy50b2tlbi5IdHRwVG9rZW4AAAAAAAAAAQIAGEoAC2lkbGVTZWNvbmRzWgAKaW5uZXJUb2tlbkkACHVzZXJ0eXBlTAAIY2xpZW50SXB0ABJMamF2YS9sYW5nL1N0cmluZztMAApjbGllbnROYW1lcQB+AAFMAApleHBpcnlUaW1ldAAQTGphdmEvdXRpbC9EYXRlO0wABmV4dHlwZXEAfgABTAACaWRxAH4AAUwADmxhc3RBY2Nlc3NUaW1lcQB+AAJMAAZsb2NhbGV0ABJMamF2YS91dGlsL0xvY2FsZTtMAAlsb2dpbk5hbWVxAH4AAUwABm1lbW9yeXQAD0xqYXZhL3V0aWwvTWFwO0wACW9yZ0FnZW5jeXEAfgABTAALb3JnQXJlYWNvZGVxAH4AAUwABW9yZ0lkcQB+AAFMAAtvcmdOb2RldHlwZXEAfgABTAAKb3JnU3VidHlwZXEAfgABTAAGcGFya0lkcQB+AAFMAAlzdGFydFRpbWVxAH4AAkwAB3N5c1R5cGV0ABNMamF2YS9sYW5nL0ludGVnZXI7TAAIdGVuYW50aWRxAH4AAUwACHVpdGlja2V0cQB+AAFMAAZ1c2VySWRxAH4AAUwACHVzZXJOYW1lcQB+AAF4cAAAAAAAAA4QAAAAAAR0AA0xMTQuODQuNzIuMjAzcQB+AAdzcgAOamF2YS51dGlsLkRhdGVoaoEBS1l0GQMAAHhwdwgAAAGApsaggnhwdAAgMDY5ZTkzNzhjMzk3NDY2NWI5NjA1ZTg2NGY2NGY1YzZzcQB+AAh3CAAAAYCmj7ICeHNyABBqYXZhLnV0aWwuTG9jYWxlfvgRYJww+ewDAAZJAAhoYXNoY29kZUwAB2NvdW50cnlxAH4AAUwACmV4dGVuc2lvbnNxAH4AAUwACGxhbmd1YWdlcQB+AAFMAAZzY3JpcHRxAH4AAUwAB3ZhcmlhbnRxAH4AAXhw/////3QAAkNOdAAAdAACemhxAH4AD3EAfgAPeHQABm55bmgwMXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHBwdAAgNDAyODgxYzg3ZmRlOTY0ODAxN2ZkZWJiZDQyMDAwMjdwdAABNHQAIGZmODA4MDgxN2ZmZmMzYjEwMTgwMDI5YTVhOGUwYTJmc3EAfgAIdwgAAAGApo+xunhwdAAgZmY4MDgwODE3YjlhMDdkNjAxN2I5ZjYwZDRmMzBkZDd0ACA0MzcyY2NjYWQxYWI0YjIxYjUwNTU0Yzk4MTI4ZThhZHQAIDQwMjg4MWM4N2ZkZTk2NDgwMTdmZGViZDA2ODkwMDMxdAAO6IO95rqQ6IO96ICXMDE=\" }","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":690,"y":360,"wires":[["ce512c0aa71eac51"]]},{"id":"0a9a1df8abe43f83","type":"change","z":"e8e45cde31fa8e1f","g":"3de9573a5bc33d37","name":"","rules":[{"t":"delete","p":"topic","pt":"msg"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":260,"y":300,"wires":[["f7d1b383be5ac29c"]]},{"id":"e461186d3bb5085a","type":"redis-command","z":"e8e45cde31fa8e1f","g":"3de9573a5bc33d37","server":"319f929161f928c2","command":"set","name":"","topic":"","params":"[\"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\"]","paramsType":"json","payloadType":"json","block":false,"x":840,"y":300,"wires":[["0671b8a36c2065e0"]]},{"id":"319f929161f928c2","type":"redis-config","name":"本地 redis","options":"{\"port\":6379,\"host\":\"127.0.0.1\"}","cluster":false,"optionsType":"json"},{"id":"465afaab07409795","type":"mqtt-broker","name":"","broker":"localhost","port":"11883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]
其中一个结果

image.png

image.png