2012-02-09 4 views
13

私はgeventを使用してアプリケーションを構築しています。多くの仕事が産んで破壊されているので、私のアプリはかなり大きくなっています。今私は、これらの仕事のうちの1つがクラッシュしたときに、アプリケーション全体が実行されていることに気がつきました(例外は非メインのグリーンレットから来たものです)。しかし問題は、エラーを見るために私のコンソールを見なければならないということです。だから私のアプリケーションのいくつかの部分は "死ぬ"ことができますし、私は即座にそれを認識していないとアプリは実行し続けます。ジョブのジエント例外の監視

私のアプリを試してみると、きれいな解決策ではないようです。 エラー報告を行うカスタムスポーン関数ですか?

gevent jobs/greenletsを監視する適切な方法は何ですか?例外をキャッチしますか?

私のケースでは、いくつかの異なるソースのイベントを待ち受けています。私はそれぞれ異なるイベントを処理する必要があります。 非常に重要な5つの仕事があります。ウェブサーバーのグリーンレット、ウェブソケットグリーンレット、 データベースのグリーンレット、アラームグリーンレット、およびzmqグリーンレット。それらのどれかが「死ぬ」なら、私のアプリケーションは完全に死ぬべきです。死ぬ他の仕事はそれほど重要ではありません。たとえば、何らかの例外が発生してwebsocket greenletが死んで、アプリケーションの残りの部分が何も起こらないようにうまく動作し続ける可能性があります。それは今ではまったく役に立たず危険です。

+0

ミッションクリティカルなグリーンレットを非常に小さくしました(コード8行)。 – Stephan

答えて

12

私はクリーンな方法は、(あなたがSystemExitは、プロセスを終了していないことを前からgevent 1.0が必要です)あなたは致命的な検討し、例外をキャッチし、sys.exit()を行うことであろうと思います。

もう一つの方法は、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()) 
+0

あなたの新しい将来のオフィスbtwは素敵です;) – Stephan

+0

0.13.6で 'gevent.get_hub()'はありません。私は 'gevent.getcurrent()'を持っていますが、親属性は 'None'です。 – scottm

+0

@scottm gevent.hub.get_hub()を試してください。 –

3

あなたは用務員の機能ににgreenlet.link_exception()にごgreenletsのすべてをしたいです。

janitor関数は、死んだ未処理のグリーンレットを渡します。そこから、greenlet.exceptionを調べて、何が起こったかを確認し、必要に応じて何かを実行します。

+0

プロセス全体を殺すよりも賢明です。 –

0

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呼び出しを追加することができます(私はしませんでした

2

@Denisと@lvoが言っているように、link_exceptionはOKですが、私は緑の子を生み出すためにあなたの現在のコードを変更することなく、もっと良い方法があると思います。

一般的に、グリーンレットに例外がスローされると、そのグリーンレットに対して_report_errorメソッド(gevent.greenlet.Greenlet)が呼び出されます。それはすべてのリンク関数を呼び出すようないくつかのものを行い、最終的に現在のスタックからexc_infoでself.parent.handle_errorを呼び出します。ここでself.parentはグローバルHubオブジェクトです。つまり、各グリーンレットで発生したすべての例外は、常に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.1の下でテストされていますb3、我々はセンチリー(例外追跡システム)にグリーンレットのエラー情報を送信するためにそれを使用していますが、これまでのところうまくいきます。

関連する問題