gunicorn的实践经验
mania_yan 2020-03-08 21:42:31 783 收藏 3
分类专栏: python
版权
为什么要上gunicorn?
flask自带的web容器不满足生产环境的要求。生产环境不能直接采用flask自带的web容器。
gunicorn是目前应用较广的支持WSGI的web容器。
gunicorn能否替代flask自带的web容器进行开发调试?
可以,加入- -reload,则代码变更后,gunicorn会自动重启。
gunicorn的worker选择gevent时的注意事项
当没有指定worker类型时,默认为同步类型,线程为native类型。
异步worker gevent有很强的并发性能
当worker指定为gevent或evenlet类型时,线程变成基于greentlet的task(伪线程),这时,线程数量的参数是无效的。
采用gevent worker时,默认并发数量是1000,有很强的并发能力。
gevent的兼容性问题
但是,使用gevent时,系统会使用monkey patch。系统的部分函数会被修改。
有些库的使用要选择兼容gevent的类型。
例如,任务调度的库apscheduler,web socket需要socketio的库等,需要专门选择gevent的函数。
而有些库则直接无法使用,例如多进程multiprocess。
例如,在一个api请求中,如果需要使用多核cpu资源,采用multiprocess进行多进程计算。则会出现卡死的问题。gevent中,不能使用multiprocess库
gunicorn的timeout参数
gunicorn默认的timeout时间是30秒,这是满足web应用的典型参数。
但是,我们是深度学习的领域,任务的执行需要的时间很长,所以,timeout参数是一个常用的参数。
gunicorn的管理进程如果在timeout时间内,没有收到worker进程的消息心跳,则会认为worker进程出现了死亡,从而重启相应的worker进程。
会导致worker进程正在执行的任务被中断。
worker进程处于阻塞或高CPU计算时,会出现不能及时发送心跳给管理进程的问题。
因此,对于深度学习领域,timeout要根据业务场景有余量的进行设置,才能保证时间较久的任务不被管理进程中断。
gunicorn什么时候使用- -preload参数
preload
Load application code before the worker processes are forked
默认情况下,gunicorn的每个进程,会将代码重新加载一次,以保障进程之间是互相隔离的。这样可以做到更好的兼容性。
但是,有些情况,需要多个进程共享同一个资源时,或多个进程只能开启1个任务时,则需要使用- -preload
使用preload后,API函数之外的初始化代码,只会出现在gunicorn的管理进程中,以共享的方式让worker进程访问
(这个需要大家自己写demo代码好好体验一下,尤其是初始化变量打印进程号,感知一下preload的差异
可以参考示例:https://testerhome.com/topics/18306)
思考:gunicorn每个进程在执行log的rotate时的问题。
gunicorn如何集成进程序,不通过命令行进行启动
目前算法采用pyinstaller进行打包,只有一个自启动的执行文件,无法采用传统的gunicorn命令行加载内部代码变量的方式启动。
制作基于gunicorn自启动执行文件,可以参考:http://docs.gunicorn.org/en/latest/custom.html
gunicorn的日志问题
我们平时的日志,只记录业务代码的日志。
第三方库的日志和gunicorn自身的日志没有被正常记录在日志文件中,必须在docker的日志里去查看,这样不方便也容易导致丢失或管理不统一。
简单示例代码:
加入gunicorn日志
去掉默认的,防止内容和gunicorn重复
logger.removeHandler(default_handler)
加入gunicorn的handler
logger.handlers.extend(logging.getLogger('gunicorn.error').handlers)
详细使用参考gunicorn的日志配置部分
或者业务代码的log不变,gunicorn通过配置让其日志单独输出一个文件
web socket的场景
web socket是一个长连接方案,gunicorn默认的同步方式是不支持长连接的。
当使用web socket时,建议独立一个程序,使用异步worker。否则,将它集成到主程序中,会强迫主程序的worker从同步改为异步。
在ops模块中,主程序需要将最新信息推送到web时,可以通过消息队列(ops模块使用的是zeromq)实现进程间的通信,然后通过web socket的独立程序将消息发送到浏览器。
未来,可以尝试ASGI的框架,ASGI是兼容WSGI,同时支持长连接的web socket和http2的推送机制,进一步简化设计。
另外,web socket独立一个程序之后,需要关注其监控问题。
默认的docker启动gunicorn服务,gunicorn服务死亡,docker服务会自动重启。
但是,web socket采用另一个独立的后台进程服务,它的死亡不会导致docker重启。因此,如果web socket进程意外死亡是不会被自动重启的。需要加入监控软件。
官方推荐了很多,目前项目已经使用的有supervisor,使用非常简单。
其他问题
为什么ocr server中,要加入nginx,有什么作用?
正常来说,无需nginx,gunicorn可以正常提供服务。
但是,C++的插件在发送图片给ocr server时,经常会出现witch的错误(目前未知什么原因)。
加入nginx后,无此问题。
(官方推荐使用nginx:Although there are many HTTP proxies available, we strongly advise that you use Nginx.)