13

gevent を使用してアプリケーションを作成しています。多くのジョブが生成および破棄されているため、私のアプリはかなり大きくなっています。これらのジョブの 1 つがクラッシュしても、アプリケーション全体が実行され続けることに気付きました (例外がメイン以外のグリーンレットから発生した場合)。これは問題ありません。しかし問題は、エラーを確認するためにコンソールを見なければならないことです。そのため、アプリケーションの一部が「死ぬ」可能性があり、すぐにそれを認識せず、アプリは実行され続けます。

私のアプリをtry catchのもので揺さぶるのは、きれいな解決策ではないようです。たぶん、いくつかのエラー報告を行うカスタム spawn 関数でしょうか?

geventジョブ/グリーンレットを監視する適切な方法は何ですか? 例外をキャッチしますか?

私の場合、いくつかの異なるソースのイベントをリッスンし、それぞれに対処する必要があります。非常に重要な仕事が5つほどあります。Web サーバー グリーンレット、Websocket グリーンレット、データベース グリーンレット、アラーム グリーンレット、および zmq グリーンレット。それらのいずれかが「死ぬ」場合、私のアプリケーションは完全に死ぬはずです。死ぬ他の仕事はそれほど重要ではありません。たとえば、何らかの例外が発生したために websocket greenlet が停止し、残りのアプリケーションが何事もなかったかのように正常に動作し続ける可能性があります。今では完全に役に立たず危険であり、激しくクラッシュするはずです.

4

4 に答える 4

12

最もクリーンな方法は、致命的であると見なして実行する例外をキャッチすることだと思います(それがプロセスを終了しなかったため、sys.exit()gevent 1.0が必要になります)。SystemExit

もう 1 つの方法は、greenlet が例外で終了した場合に呼び出される 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())
于 2012-02-09T09:32:04.880 に答える
3

greenlet.link_exception()すべての greenlet を to janitor 関数にしたいとします。

用務員関数には、死亡した greenlet が渡され、そこからgreenlet.exception何が起こったのかを調べ、必要に応じて何かを行うことができます。

于 2012-02-09T11:23:19.207 に答える
2

@Denis と @lvolink_exceptionが言ったように、問題ありませんが、現在のコードを変更してグリーンレットを生成することなく、より良い方法があると思います。

通常、greenlet で例外がスローされると、その greenlet に対して_report_error( の ) メソッドgevent.greenlet.Greenletが呼び出されます。すべてのリンク関数を呼び出し、最後にself.parent.handle_error現在のスタックから exc_info を呼び出します。これself.parentはグローバルHubオブジェクトです。つまり、各 greenlet で発生したすべての例外は、常に 1 つのメソッドに集中して処理されます。デフォルトで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 (例外追跡システム) に送信するために使用されています。

于 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 に答える