问题描述
- 使用Flask开发的Web服务,部署在服务器上使用的是
gunicorn manage:app -k gevent -w 4
- 某日告警,说浏览器崩了,当时急急忙忙的重启,搞好了,因为所有的服务都正常运行,后面查看日志,也没有发现什么特别的地方,最终感觉因该是MongoDB连接数满了,本地测试发现确实是连接数一直增加,不会释放。
解决过程
关于Gunicron
什么是Gunicron:是一个unix上被广泛使用的高性能的Python WSGI UNIX HTTP Server。和大多数的web框架兼容,并具有实现简单,轻量级,高性能等特点。
- 为什么要使用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]