代码之家  ›  专栏  ›  技术社区  ›  Nick Gotch

带peewee连接池的django mysql disconnect

  •  1
  • Nick Gotch  · 技术社区  · 6 年前

    我正在用python 3.6中的peewee运行一个django项目,并试图跟踪连接池的问题所在。我在开发服务器上一直遇到以下错误(出于某种原因,我从未在本地计算机上遇到过此问题):

    查询过程中与mysql服务器的连接丢失

    repro步骤是可靠的,并且是:

    1. 在实例上重新启动Apache。
    2. 转到我的django页面,然后按一个按钮触发数据库操作。
    3. 工作良好。
    4. 精确地等待10分钟(我已经做了足够的测试,可以得到确切的数字)。
    5. 按另一个按钮触发另一个数据库操作。
    6. 获取上面的丢失连接错误。

    代码的结构使得我可以在一个独立的python模块中执行所有的db操作,这个模块被导入到django模块中。

    在主类构造函数中,我将设置db,如下所示:

    from playhouse.pool import PooledMySQLDatabase
    
    def __init__(self, host, database, user, password, stale_timeout=300):
        self.mysql_db = PooledMySQLDatabase(host=host, database=database, user=user, password=password, stale_timeout=stale_timeout)
        db_proxy.initialize(self.mysql_db)
    

    每个需要呼叫数据库的呼叫都是这样进行的:

    def get_user_by_id(self, user_id):
        db_proxy.connect(reuse_if_open=True)
        user = (User.get(User.user_id == user_id))
        db_proxy.close()
        return {'id': user.user_id, 'first_name': user.first_name, 'last_name': user.last_name, 'email': user.email }
    

    我看着 wait_timeout MySQL实例上的值为3600,因此这似乎不是问题所在(我尝试更改它只是为了查看)。

    我在这里可能做错什么有什么想法吗?

    更新:

    我发现 /etc/my.cnf mysql的配置文件具有 wait-timeout 值设置为600,与我的体验相匹配。我不知道为什么我跑步时这个值不显示 SHOW VARIABLES LIKE 'wait_timeout'; 在MySQLdb上(返回3600),但似乎问题来自等待超时。

    考虑到这一点,我尝试将过时超时设置为60,假设它小于等待超时,它可能会解决该问题,但不会有任何影响。

    2 回复  |  直到 6 年前
        1
  •  1
  •   coleifer    6 年前

    您需要确保正确地回收连接——这意味着当请求开始时,您打开连接,当响应被传递时,您关闭连接。水池并没有循环利用连接件,很可能是因为你从来没有把它放回水池中,所以看起来它仍然在“使用中”。这可以很容易地通过中间件完成,并在这里描述:

    http://docs.peewee-orm.com/en/latest/peewee/database.html#django

        2
  •  0
  •   Nick Gotch    6 年前

    在尝试之后,我终于想出了一个适合我案件的解决方案。 很多的 思想。这不太理想,但很有效。 This post on Connection pooling 把我指向正确的方向。

    我创建了一个django中间件类,并将其配置为django中间件列表中的第一个。

    from peewee import OperationalError
    from playhouse.pool import PooledMySQLDatabase
    
    database = PooledMySQLDatabase(None)
    
    class PeeweeConnectionMiddleware(object):
    
        CONN_FAILURE_CODES = [ 2006, 2013, ]
    
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            if database.database: # Is DB initialized?
                response = None
                try:
                    database.connect(reuse_if_open=True)
                    with database.atomic() as transaction:
                        try:
                            response = self.get_response(request)
                        except:
                            transaction.rollback()
                            raise
                except OperationalError as exception:
                    if exception.args[0] in self.CONN_FAILURE_CODES:
                        database.close_all()
                        database.connect()
                        response = None
                        with database.atomic() as transaction:
                            try:
                                response = self.get_response(request)
                            except:
                                transaction.rollback()
                                raise
                    else:
                        raise
                finally:
                    if not database.is_closed():
                        database.close()
                return response
            else:
                return self.get_response(request)