共计 1335 个字符,预计需要花费 4 分钟才能阅读完成。
问题描述
- 使用 Flask 开发的 Web 服务,部署在服务器上使用的是
gunicorn manage:app -k gevent -w 4
- 某日告警,说浏览器崩了,当时急急忙忙的重启,搞好了,因为所有的服务都正常运行,后面查看日志,也没有发现什么特别的地方,最终感觉因该是 MongoDB 连接数满了,本地测试发现确实是连接数一直增加,不会释放。
解决过程
关于 Gunicron
-
什么是 Gunicron:是一个 unix 上被广泛使用的高性能的 Python WSGI UNIX HTTP Server。和大多数的 web 框架兼容,并具有实现简单,轻量级,高性能等特点。
- 深入理解 uwsgi 和 gunicorn 网络模型
- 为什么要使用 Gunicron:用于接受 http 请求并转换为 WSGI 协议,以供实现了 WSGI 协议的 flask 使用,并且 gunicorn 得益于 gevent 等技术,大幅度提高了性能,在生产环境以替代框架自带的 WSGI server。
生产环境
- 配置
gevent==1.3.6
greenlet==0.4.14
gunicorn==19.9.0
pymongo==3.7.0 - mongodb 连接代码
def __init__(self):
config_name = os.getenv('FLASK_CONFIG') or 'default'
base_config = config[config_name] # object
self.client = MongoClient(host=base_config.MONGO_HOST, port=base_config.MONGO_PORT)
db_name = base_config.MONGO_NAME
self.db = self.client[db_name]
修改方案
- 参考 pymongo: MongoClient opened before fork 错误排解
- fork 是启动新进程的方法,由于 MongoClient 不是进程安全的,所以不可以将该实例从父进程中复制到子进程当中。在这个 flask 应用中,flask 使用 gunicorn 作为网关接口,在启动的时候会启动一个主进程和多个子进程,也就是 master/workers,这个时候就出现了 MongoClient 实例在进程之间的传递。
- 为了解决这个问题,在实例化 MongoClient 对象的时候要加上 connect=False 参数。
def __init__(self):
config_name = os.getenv('FLASK_CONFIG') or 'default'
base_config = config[config_name] # object
self.client = MongoClient(host=base_config.MONGO_HOST, port=base_config.MONGO_PORT, maxIdleTimeMS=300000, connect=False)
db_name = base_config.MONGO_NAME
self.db = self.client[db_name]
参考
- 用 gevent 来 host wsgi server,mysql 能否长连接
- python – Mongo 连接从未发布 – Django 和 Mongoengine 在 gevent 上使用 gunicorn
正文完