监视作业中的gevent异常

时间:2012-02-09 08:55:41

标签: python exception-handling gevent

我正在使用gevent构建应用程序。我的应用程序现在变得相当大,因为有很多工作正在产生和销毁。现在我注意到,当其中一个作业崩溃时,我的整个应用程序只是继续运行(如果异常来自非主要的greenlet),这很好。但问题是我必须查看我的控制台才能看到错误。所以我的应用程序的某些部分可能会“死”,我不会立即意识到这一点并且应用程序会继续运行。

使用try catch东西来抖动我的应用程序似乎不是一个干净的解决方案。 也许是一个自定义的spawn函数,可以进行一些错误报告?

监控gevent jobs / greenlets的正确方法是什么?捕捉异常?

在我的情况下,我会听取几个不同来源的事件,我应该处理每个不同的事件。 有5个工作非常重要。 webserver greenlet,websocket greenlet, 数据库greenlet,alarm greenlet和zmq greenlet。如果任何“死”,我的申请应该完全死亡。其他死亡的工作并不重要。例如,由于引发了一些异常,websocket greenlet可能会死亡,其余的应用程序一直运行良好,就像没有发生任何事情一样。它现在完全无用且危险,应该很难崩溃。

4 个答案:

答案 0 :(得分:12)

我认为最干净的方法是捕获您认为致命的异常并执行sys.exit()(您需要gevent 1.0,因为SystemExit之前没有退出流程)。

另一种方法是使用link_exception,如果greenlet因异常而死亡,则会调用它。

spawn(important_greenlet).link_exception(lambda *args: sys.exit("important_greenlet died"))

请注意,您还需要gevent 1.0来实现此目的。

如果在0.13.6上,请执行以下操作以终止该过程:

gevent.get_hub().parent.throw(SystemExit())

答案 1 :(得分:3)

您希望将greenlet.link_exception()所有greenlet发送到清洁工具。

janitor函数将被传递给任何已死的greenlet,它可以从中检查greenlet.exception以查看发生了什么,并在必要时对其进行一些操作。

答案 2 :(得分:2)

正如@Denis和@lvo所说,link_exception没问题,但我认为有更好的方法可以做到这一点,而不用更改当前代码来生成greenlet。

通常,只要在greenlet中抛出异常,就会为该greenlet调用_report_error方法(在gevent.greenlet.Greenlet中)。它将执行一些调用所有链接函数的操作,最后使用当前堆栈中的exc_info调用self.parent.handle_errorself.parent这里是全局Hub对象,这意味着,每个greenlet中发生的所有异常将始终集中在一个处理方法上。默认情况下Hub.handle_error区分异常类型,忽略某些类型并打印其他类型(这是我们在控制台中总是看到的)。

通过修补Hub.handle_error方法,我们可以轻松注册我们自己的错误处理程序,不再丢失错误。我写了一个辅助函数来实现它:

from gevent.hub import Hub


IGNORE_ERROR = Hub.SYSTEM_ERROR + Hub.NOT_ERROR


def register_error_handler(error_handler):

    Hub._origin_handle_error = Hub.handle_error

    def custom_handle_error(self, context, type, value, tb):
        if not issubclass(type, IGNORE_ERROR):
            # print 'Got error from greenlet:', context, type, value, tb
            error_handler(context, (type, value, tb))

        self._origin_handle_error(context, type, value, tb)

    Hub.handle_error = custom_handle_error

要使用它,只需在初始化事件循环之前调用它:

def gevent_error_handler(context, exc_info):
    """Here goes your custom error handling logics"""
    e = exc_info[1]
    if isinstance(e, SomeError):
        # do some notify things
        pass
    sentry_client.captureException(exc_info=exc_info)

register_error_handler(gevent_error_handler)

此解决方案已经在gevent 1.0.2和1.1b3下测试过,我们使用它来向哨兵(异常跟踪系统)发送greenlet错误信息,到目前为止它运行良好。

答案 3 :(得分:0)

greenlet.link_exception()的主要问题是它没有提供有关回溯的任何信息,这对于记录非常重要。

对于带回溯的日志记录,我使用装饰器将间接作业调用的作业转换为简单的日志记录功能:

from functools import wraps    

import gevent

def async(wrapped):

    def log_exc(func):

        @wraps(wrapped)
        def wrapper(*args, **kwargs):
            try:
                func(*args, **kwargs)
            except Exception:
                log.exception('%s', func)
        return wrapper

    @wraps(wrapped)
    def wrapper(*args, **kwargs):
        greenlet = gevent.spawn(log_exc(wrapped), *args, **kwargs)

    return wrapper

当然,你可以添加link_exception电话来管理工作(我不需要)