Мониторинг исключений Gevent в рабочих местах - PullRequest
13 голосов
/ 09 февраля 2012

Я создаю приложение, используя gevent. Мое приложение становится довольно большим, так как множество рабочих мест создаются и уничтожаются. Теперь я заметил, что когда происходит сбой одного из этих заданий, все мое приложение просто продолжает работать (если исключение было получено из неосновного гринлета), и это нормально. Но проблема в том, что я должен посмотреть на свою консоль, чтобы увидеть ошибку. Таким образом, некоторая часть моего приложения может «умереть», и я не сразу осознаю это, и приложение продолжает работать.

Дрожание моего приложения с помощью метода try catch не кажется чистым решением. Может быть, пользовательская функция spawn, которая выдает сообщения об ошибках?

Каким образом следует отслеживать задания / гринлеты? ловить исключения?

В моем случае я слушаю события из нескольких разных источников, и я должен иметь дело с каждым по-разному. Есть 5 очень важных рабочих мест. Вебсервер гринлет, вебсет розетка, база данных greenlet, аварийные сигналы greenlet и zmq greenlet. Если кто-то из этих «умрет», мое заявление должно полностью умереть. Другие работы, которые умирают, не так важны. Например, возможно, что гринлет websocket умирает из-за какого-то исключения, а остальные приложения продолжают работать нормально, как будто ничего не произошло. Теперь это совершенно бесполезно и опасно, и его надо просто сильно разбить.

Ответы [ 4 ]

12 голосов
/ 09 февраля 2012

Я думаю, что самый простой способ - поймать исключение, которое вы считаете фатальным, и выполнить sys.exit() (вам понадобится gevent 1.0 , поскольку до этого SystemExit не выходил из процесса).

Другим способом является использование link_exception, которое вызывалось бы, если бы гринлет умер за исключением.

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

Обратите внимание, что для этого также требуется gevent 1.0.

Если на 0.13.6, сделать что-то подобное, чтобы убить процесс:

gevent.get_hub().parent.throw(SystemExit())
3 голосов
/ 09 февраля 2012

Вы хотите greenlet.link_exception() всех ваших гринлетов для функции a to janitor.

Функция уборщика будет передавать любой умирающий гринлет, из которого она может проверить свои greenlet.exception, чтобы увидеть, что произошло, и, если необходимо, что-то с этим сделать.

2 голосов
/ 17 августа 2015

Как сказали @Denis и @lvo, link_exception - это нормально, но я думаю, что был бы лучший способ для этого, не меняя текущий код на порождающий гринлет.

Обычно, когда исключениеброшенный в гринлет, метод _report_errorgevent.greenlet.Greenlet) будет вызван для этого гринлета.Он будет выполнять некоторые вещи, такие как вызов всех функций связи и, наконец, вызов self.parent.handle_error с exc_info из текущего стека.self.parent здесь - глобальный объект Hub, это означает, что все исключения, возникающие в каждом гринлете, всегда будут централизованы для одного метода обработки.По умолчанию 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 в sentry (система отслеживания исключений), пока она работает довольно хорошо.

0 голосов
/ 02 апреля 2013

Основная проблема с greenlet.link_exception() заключается в том, что он не дает никакой информации о трассировке, которая может быть очень важна для ведения журнала.

Для ведения журнала с помощью трассировки я использую декоратор для spwan-заданий, косвенный вызов заданияв простую функцию регистрации:

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 для управления заданиями (которые мне не нужны)

...