gevent WSGIServer实现优雅退出
一、背景
项目中其中一个服务原来是基于tornado开发的,虽然tornado是一个异步框架,但是由于业务逻辑中存在大量的同步操作,例如:查数据、查es等;导致服务性能很低,于是经过调研最终决定将底层框架切换到gevent,但是该优化上线后发现每次更新时耗时很长需要十几秒,因此抽时间排查了一下这里的问题做了一个优化。
二、代码
2.1、优化前
def sig_handler(signum, frame):
MyLogger().getlogger().info('ENTRANCE: signum:%s, frame:%s', signum, frame)
tornado.ioloop.IOLoop.instance().add_callback(grace_shutdown)
MyLogger().getlogger().info('add callback finish')
signal.signal(signal.SIGINT, sig_handler)
signal.signal(signal.SIGTERM, sig_handler)
2.2、优化后
def sig_handler(signum, frame):
MyLogger().getlogger().info('stop service')
g_wsgi_server.stop()
gevent.signal.signal(signal.SIGTERM, sig_handler)
gevent.signal.signal(signal.SIGINT, sig_handler)
这里是由于底层框架切换到gevent之后,服务启动之后的循环其实已经是由gevent在负责了,因此在sig_handler中调用tornado的add_callback是不会被调度到的。
三、效果
优化前重启服务:
优化后重启服务:
3.1、结论
从前端对比可以很明确的看到停止服务的速度从平均10s减少到不到1s。
四、supervisor的机制
虽然问题解决了,但是这里有一个疑问,当我手动使用kill -s TERM pid给进程发送信号时进程一直运行着,但是supervisor是如何使进程强制关闭的呢?
翻了一下supervisor的官方文档,并没有关于stop机制的实现介绍。
但是却意外从supervisor的日志中翻到一下这些记录:
从这个日志推测,supervisor停止服务的应该是:
- 发送SIGTERM信号到进程。
- 每隔2秒查询一下进程是否还在。
- 如果等待超过10s,则发送SIGKILL信号强制关闭服务进程。
- 再次查询进程是否还在,此时进程应该肯定被关闭了,毕竟SIGKILL无法被捕获。
到此就理清了supervisor停止服务的机制,也和效果对比中的实验相呼应:为什么优化前停止服务需要将近10s中。