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停止服务的应该是:

  1. 发送SIGTERM信号到进程。
  2. 每隔2秒查询一下进程是否还在。
  3. 如果等待超过10s,则发送SIGKILL信号强制关闭服务进程。
  4. 再次查询进程是否还在,此时进程应该肯定被关闭了,毕竟SIGKILL无法被捕获。

到此就理清了supervisor停止服务的机制,也和效果对比中的实验相呼应:为什么优化前停止服务需要将近10s中。

posted @ 2020-12-23 19:04  冷冰若水  阅读(740)  评论(0编辑  收藏  举报