我正在使用gevent构建应用程序。我的应用程序现在变得相当大,因为有很多工作正在产生和销毁。现在我注意到,当其中一个作业崩溃时,我的整个应用程序只是继续运行(如果异常来自非主要的greenlet),这很好。但问题是我必须查看我的控制台才能看到错误。所以我的应用程序的某些部分可能会“死”,我不会立即意识到这一点并且应用程序会继续运行。
使用try catch东西来抖动我的应用程序似乎不是一个干净的解决方案。 也许是一个自定义的spawn函数,可以进行一些错误报告?
监控gevent jobs / greenlets的正确方法是什么?捕捉异常?
在我的情况下,我会听取几个不同来源的事件,我应该处理每个不同的事件。 有5个工作非常重要。 webserver greenlet,websocket greenlet, 数据库greenlet,alarm greenlet和zmq greenlet。如果任何“死”,我的申请应该完全死亡。其他死亡的工作并不重要。例如,由于引发了一些异常,websocket greenlet可能会死亡,其余的应用程序一直运行良好,就像没有发生任何事情一样。它现在完全无用且危险,应该很难崩溃。
答案 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_error
。 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下测试过,我们使用它来向哨兵(异常跟踪系统)发送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
电话来管理工作(我不需要)