13

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

用 try catch 来抖动我的应用程序似乎不是一个干净的解决方案。也许是一个自定义生成函数,它会报告一些错误?

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

在我的情况下,我监听几个不同来源的事件,我应该处理每个不同的事件。有5个工作非常重要。webserver greenlet、websocket greenlet、数据库 greenlet、警报 greenlet 和 zmq greenlet。如果其中任何一个“死”,我的应用程序应该完全死掉。其他死掉的工作并不那么重要。例如,websocket greenlet 有可能由于引发了一些异常而死掉,而其余的应用程序仍然运行良好,就像什么都没发生一样。它现在完全没用而且很危险,应该会严重崩溃。

4

4 回答 4

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())
于 2012-02-09T09:32:04.880 回答
3

你想把你greenlet.link_exception()所有的greenlets变成一个看门人的功能。

看门人函数将被传递给任何死亡的greenlet,它可以从中检查它greenlet.exception以查看发生了什么,并在必要时对其进行处理。

于 2012-02-09T11:23:19.207 回答
2

正如@Denis 和@lvo 所说,没问题link_exception,但我认为会有更好的方法,无需更改您当前的代码来生成greenlet。

通常,每当在 greenlet 中引发异常时,都会为该 greenlet 调用_report_error方法 (in gevent.greenlet.Greenlet)。它会做一些事情,比如调用所有的链接函数,最后,self.parent.handle_error用当前堆栈中的 exc_info 调用。这里self.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 下进行了测试,我们使用它向 sentry(异常跟踪系统)发送 greenlet 错误信息,目前运行良好。

于 2015-08-17T07:36:34.660 回答
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调用来管理作业(我不需要)

于 2013-04-02T16:03:02.900 回答