思路: 通过tornado 框架构建web 服务器,通过执行后台命令程序获取监控目标状态或监控结果,根据状态或结果,通过websocket 发送信息到前端进行相应的展现。
问题: tornado web框架是异步处理的,其核心是将事务都放入到ioloop异步循环中。但通常使用python调用shell脚本或者执行的shell命令,以及python打开文件的操作都是同步阻塞模式,无法加入到ioloop中。在下面的连接中提到了如何将shell命令通过异步方式执行并获取执行结果。
https://www.cnblogs.com/senjougahara/p/6091652.html
根据 tornado 官方文档,tornado.process.
Subprocess
(*args, **kwargs) 对象是 subprocess.popen的装饰器,增加了对IOStream的支持。
stdin,stdout和stderr 可以是tornado.process.Subprocess.STREAM的值,将subproess的结果属性放入到pipeIOStream。如果使用这个选项,调用者将负责steam的关闭。
注意,subprocess.STREAM 和 set_exit_callback 以及wait_for_exit 方法不适用于windows平台,也就是在windows中只能用subprocess.Popen()
上述链接中我主要使用了这个方法:
import tornado.gen
from tornado.process import Subprocess
@tornado.gen.coroutine
def run_command(command):
"""run command"""
process = Subprocess(
[command],
stdout=Subprocess.STREAM,
stderr=Subprocess.STREAM,
shell=True
)
out, err = yield [process.stdout.read_until_close(), process.stderr.read_until_close()]
raise tornado.gen.Return((out, err))
然后定义一个异步函数,在期中调用上述函数执行命令,并接收返回的结果。
async def healthy_check():
GW = "192.168.1.1"
cmd_gw = "ping -c 3 " + GW + " | grep " + GW + " | wc -l"
"""run command"""
while True:
await asyncio.sleep(1)
r_cmd_res,r_cmd_err = await run_command(cmd_gw)
if r_cmd_res.decode() == '5':
websocket_inst.write_message("GW_UP")
else:
websocket_inst.write_message("GW_UP")
在tornado 中:
if __name__ == "__main__":
app = Application()
app.listen(options.port)
io = tornado.ioloop.IOLoop.current()
io.add_callback(healthy_check)
io.start()
将链接中的代码改成异步原生状态也可以:只不过将yield 改成await即可。
async def run_command(command):
"""run command"""
process = Subprocess(
[command],
stdout=Subprocess.STREAM,
stderr=Subprocess.STREAM,
shell=True
)
out = await process.stdout.read_until_close()
err = await process.stderr.read_until_close()
return (out, err)
通过这种方式,将shell命令、websocket 和tornado 结合起来实现对监控目标的监控,并将监控结果实时展现到前段界面。
可以观察到系统调用shell 执行命令的情况,可以看到,系统每次是创建不同的进程来执行命令,在具有大量监控目标时可能会有较大的开销。好处是系统调用完就关闭进程,能够支持的并发数要高,实际使用时,需要在系统新建和关闭进程的开销和并发数之间进行折中考虑。