Gunicron + gevent Mongodb數據庫鏈接一直增長不釋放

問題描述

  • 使用Flask開發的Web服務,部署在服務器上使用的是gunicorn manage:app -k gevent -w 4
  • 某日告警,說瀏覽器崩了,當時急急忙忙的重啓,搞好了,由於全部的服務都正常運行,後面查看日誌,也沒有發現什麼特別的地方,最終感受因該是MongoDB鏈接數滿了,本地測試發現確實是鏈接數一直增長,不會釋放。

解決過程

關於Gunicron

  • 什麼是Gunicron:是一個unix上被普遍使用的高性能的Python WSGI UNIX HTTP Server。和大多數的web框架兼容,並具備實現簡單,輕量級,高性能等特色。html

  • 爲何要使用Gunicron:用於接受http請求並轉換爲WSGI協議,以供實現了WSGI協議的flask使用,而且gunicorn得益於gevent等技術,大幅度提升了性能,在生產環境以替代框架自帶的WSGI server。

生產環境

  • 配置

    gevent==1.3.6
    greenlet==0.4.14
    gunicorn==19.9.0
    pymongo==3.7.0python

  • 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]

參考

相關文章
相關標籤/搜索