🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
> 最近有个自动化脚本的任务开发,开发完成只有,发现调用有点麻烦。因为脚本也需要提供给其他运维人员使用,所以脚本只能放到运维平台服务器上,而其他人员对这个服务器又不是很熟悉,导致使用麻烦。 > 所以直接想到从运维平台开一个页面进行调用此脚本,如果单纯只是执行,那是很简单的。如果想实时看到脚本执行输出,那就需要想点办法了。 > 从网上查到,基本上有两种方式:①客户端 ajax 轮询 ②服务端 websocket 主动推送 > 本篇为我选择的 websocket 方案的使用记录 # 1. WebSocket 服务端 运维平台使用 django 开发的,所以首先查询了一下 django 中集成 websocket 方法;查到可以使用 [channels](https://channels.readthedocs.io/en/latest/),调研中又发现另一个 [websocketd](http://websocketd.com/),发现后者更加轻量、简单,所以直接使用了后者。 ## 1.1 安装启动 安装和启动都特别简单,直接按照官网的示例。 * 安装:直接下载二进制文件,它就是一个单文件;下载后放到服务器的 `/usr/bin/` 目录即可 * 启动:`$ websocketd --port=8080 my-program` ## 1.2 本地测试 官网示例:https://github.com/joewalnes/websocketd/wiki/Ten-minute-tutorial 下面主要是我自己的示例,与官网比较,添加了参数处理和调用外部脚本 ### 1.2.1 本地启动: ```bash $ websocketd --devconsole --port=9000 --address=127.0.0.1 --dir=./script Sun, 18 Oct 2020 14:40:18 +0800 | INFO | server | | Serving from directory : /home/shengan/popop/script Sun, 18 Oct 2020 14:40:18 +0800 | INFO | server | | Starting WebSocket server : ws://127.0.0.1:9000/ Sun, 18 Oct 2020 14:40:18 +0800 | INFO | server | | Developer console enabled : http://127.0.0.1:9000/ Sun, 18 Oct 2020 14:40:21 +0800 | ACCESS | session | url:'http://127.0.0.1:9000/ws/test.sh?p1=5__1' id:'1603003221372852300' remote:'127.0.0.1' command:'/home/shengan/popop/script/ws/test.sh' origin:'http://127.0.0.1:9000' | CONNECT Sun, 18 Oct 2020 14:40:31 +0800 | ACCESS | session | url:'http://127.0.0.1:9000/ws/test.sh?p1=5__1' id:'1603003221372852300' remote:'127.0.0.1' command:'/home/shengan/popop/script/ws/test.sh' origin:'http://127.0.0.1:9000' pid:'173' | DISCONNECT ``` ### 1.2.2 参数介绍: * --devconsole:开启浏览器测试,可以不用编写客户端代码,直接提供页面进行测试 * --address:绑定地址,默认是 all,上线后记得修改或直接去掉这个参数 * --dir:不用指定一个脚本,此参数指定一个目录,此目录可以放置多个脚本任务 ### 1.2.3 页面测试示例: 使用浏览器登录 locahost:9000 即可进入开发测试页面;填写要执行的脚本和其参数 `ws/test.sh?p1=5__1` 我的测试脚本 `test.sh` 在 `script/ws` 目录,所以我的填写的是 `ws/test.sh`;后面 `?p1=5__1` 是参数,就是正常的 url 地址和参数 正常的参数是 `?p1=5&p2=1` 为了方便处理,我将多个参数都合并成一个参数了,使用双下划线连接。 ![](https://img.kancloud.cn/90/58/905833b936f339b68ab1d003d05c7acf_793x457.gif) 执行完成也可以看到服务端日志的输出,在上面的本地启动中可以看到 ### 1.2.4 脚本内容 ```bash $ cat test.py #!/bin/bash export LC_ALL="en_US.UTF-8" export LC_CTYPE="en_US.UTF-8" echo $QUERY_STRING # 分割参数环境变量,获取参数 param=${QUERY_STRING#*=} cnt=${param%__*} ss=${param#*__} for ((COUNT = 1; COUNT <= $cnt; COUNT++)); do echo $COUNT sleep $ss done pipenv run python -u script/test.py $cnt $ss ``` ```python $ cat test.py import time import sys cnt = sys.argv[1] ss = sys.argv[2] cnt = int(cnt) ss = float(ss) def test(): for i in range(cnt): print(f'python {i}') # sys.stdout.flush() time.sleep(ss) if __name__ == '__main__': test() ``` 重点: * `$QUERY_STRING`:为 websocketd 提供的环境变量,记录参数;更多环境变量:https://github.com/joewalnes/websocketd/wiki/Environment-variables * `python -u xxx.py`:-u 关闭 python 脚本输出缓冲问题,不关闭的话,在调用 test.py 时并不会实时打印 test.py 的输出,而是会在 test.py 执行完成之后一次性打印其输出。参考:https://medium.com/@bramblexu/three-ways-to-close-buffer-for-stdout-stdin-stderr-in-python-8be694bd2737 ## 1.3 Nginx 代理 ``` server { listen 80; server_name 127.0.0.1; location /ws/ { proxy_pass http://127.0.0.1:9000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } ``` 注意后面三行也必不可少,参考:https://github.com/joewalnes/websocketd/wiki/Websocketd-behind-Nginx 启动 nginx 代理之后,上面的访问链接就可以改成 http://localhost/ws/test.sh?p1=5__1 了,效果一样。 ## 1.4 线上环境使用 websocketd 的启动非常适合使用 supervisor 来启动管理,配置文件如下: ``` [program:websocketd] command=websocketd --port=9000 --dir=./script directory=/popop/current user=root numprocs=1 stdout_logfile=/var/log/popop/websocketd.log stderr_logfile=/var/log/popop/websocketd.log autostart=true autorestart=true startsecs=10 ``` 注意点: * 线上服务如果使用 https,则客户端连接地址为 `wss://xxx.com/ws/test.sh?p1=5__1` * 启动方式的 --address 参数已经去掉了