gevent.monkey.patch_all()后的max_user_connections问题

1 投票
1 回答
710 浏览
提问于 2025-04-18 07:36

我正在使用gevent-socketio v0.13.8来开发一个基于Django的聊天应用。我的数据库是MySql,最大用户连接数设置为1500。我的socket服务器是用Python的守护进程运行的。最开始我没有使用猴子补丁(monkey patching),服务器运行得很好,除了当某个绿色线程(greenlet)出错时,整个系统就会崩溃,出现SystemExit错误,之后就再也无法建立连接了。解决办法就是重启整个服务器。

不过我不想每次都重启服务器。最后我想到了猴子补丁这个方法。我不确定这是否能解决我的问题,但我希望我的socket服务器即使在绿色线程出现未处理异常导致SystemExit时也能继续运行。

于是我在服务器启动的函数中使用了gevent.monkey.patch_all()。现在我遇到的主要问题是:在连接了3到4次后,MySql出现了以下错误:

User xxx already has more than 'max_user_connections' active connections

我的MySql服务器的max_user_connection变量设置为1500。我觉得可能是某些东西在绿色线程中创建了新的数据库连接。

顺便提一下,当我使用:

monkey.patch_all(socket=True, dns=True, time=True, select=True, thread=True, os=True, ssl=True, httplib=True, aggressive=True)

而不是:

monkey.patch_all()

时,max_user_connection错误就不会出现。有没有办法在不出现这个错误的情况下使用猴子补丁?如果我在问题描述中遗漏了什么信息,请告诉我,我会立即更新。

这是我的守护进程服务器代码:

class App():

    def __init__(self):
        self.stdin_path = 'xxx.txt'
        self.stdout_path = 'xxx.txt'
        self.stderr_path = 'xxx.txt'
        self.pidfile_path =  'xxx.pid'
        self.pidfile_timeout = 5

    def run(self):
        from socketio.server import SocketIOServer
        from gevent import monkey
        monkey.patch_all()
        while True:
            try:
                bind = ("xx.xx.xx.xx", xxxx)
                handler = get_handler()
                server = SocketIOServer(bind, handler, resource="socket.io",policy_server=False)
                server.serve_forever()
            except:
                server.kill()
app = App()
logger = logging.getLogger("DaemonLog")
logger.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler = logging.FileHandler("xxx.log")
handler.setFormatter(formatter)
logger.addHandler(handler)

daemon_runner = runner.DaemonRunner(app)
daemon_runner.daemon_context.files_preserve=[handler.stream]
daemon_runner.do_action()

1 个回答

1

我在这里找到了解决我问题的方法:https://github.com/abourget/gevent-socketio/issues/174

其实这个问题和没有关闭的数据库连接有关,这些连接是由绿色线程(greenlets)管理的。在我的命名空间类中添加了一个类似的异常处理装饰器之后,我就没有再看到“最大用户连接数”错误了。

from django.db import close_old_connections  # Django 1.6
class MyNamespace(BaseNamespace):
    ...
    def exception_handler_decorator(self, fun):
        def wrap(*args, **kwargs):
            try:
                return fun(*args, **kwargs)
            finally:
                close_old_connections()
        return wrap
    ...

撰写回答