web-dev-qa-db-ja.com

RabbitMQに再接続する方法は?

私のpythonスクリプトは、別のデータソースからメッセージを受信すると、常にRabbitMQにメッセージを送信する必要があります。pythonスクリプトがメッセージを送信する頻度は、 、1分〜30分。

RabbitMQへの接続を確立する方法は次のとおりです。

  rabt_conn = pika.BlockingConnection(pika.ConnectionParameters("some_Host"))
  channel = rbt_conn.channel()

例外が発生しました

pika.exceptions.ConnectionClosed

どうすれば再接続できますか?最良の方法は何ですか? 「戦略」はありますか? pingを送信して接続を維持したり、タイムアウトを設定したりできますか?

どんなポインタでも大歓迎です。

14
Alan Coromano

RabbitMQはheartbeatsを使用して、「デッド」接続を検出して閉じ、ネットワークデバイス(ファイアウォールなど)が「アイドル」接続を終了しないようにします。バージョン3.5.5以降では、デフォルトのタイムアウトは60秒に設定されています(以前は〜10分でした)。 docs から:

ハートビートフレームは、約2秒ごとに送信されます。 2つのハートビートが失われた後、ピアは到達不能と見なされます。

PikaのBlockingConnectionの問題は、API呼び出しが行われるまでハートビートに応答できないことです(たとえば、channel.basic_publish()connection.sleep()、等)。

これまでに見つけたアプローチ:

タイムアウトを増やすか無効にする

RabbitMQは、接続を確立するときにクライアントとタイムアウトをネゴシエートします。理論的には、_heartbeat_interval_引数を使用してサーバーのデフォルト値をより大きな値で上書きすることは可能ですが、現在のPikaバージョン(0.10.0)はminサーバーとクライアントが提供する値の間の値。この問題は現在の master で修正されています。

一方、_heartbeat_interval_引数を_0_に設定することにより、ハートビート機能を完全に非アクティブ化することができます。これにより、新しい問題(ファイアウォールによる接続のドロップなど)が発生する可能性があります。

再接続しています

@itsafireの答えを拡張して、独自のpublisherクラスを記述して、必要に応じて再接続することができます。単純な実装の例:

_import logging
import json
import pika

class Publisher:
    EXCHANGE='my_exchange'
    TYPE='topic'
    ROUTING_KEY = 'some_routing_key'

    def __init__(self, Host, virtual_Host, username, password):
        self._params = pika.connection.ConnectionParameters(
            Host=host,
            virtual_Host=virtual_Host,
            credentials=pika.credentials.PlainCredentials(username, password))
        self._conn = None
        self._channel = None

    def connect(self):
        if not self._conn or self._conn.is_closed:
            self._conn = pika.BlockingConnection(self._params)
            self._channel = self._conn.channel()
            self._channel.exchange_declare(exchange=self.EXCHANGE,
                                           type=self.TYPE)

    def _publish(self, msg):
        self._channel.basic_publish(exchange=self.EXCHANGE,
                                    routing_key=self.ROUTING_KEY,
                                    body=json.dumps(msg).encode())
        logging.debug('message sent: %s', msg)

    def publish(self, msg):
        """Publish msg, reconnecting if necessary."""

        try:
            self._publish(msg)
        except pika.exceptions.ConnectionClosed:
            logging.debug('reconnecting to queue')
            self.connect()
            self._publish(msg)

    def close(self):
        if self._conn and self._conn.is_open:
            logging.debug('closing queue connection')
            self._conn.close()
_

その他の可能性

まだ検討していなかった他の可能性:

  • 発行用の非同期アダプターの使用
  • RabbitMQ接続と「パブリッシュ」コードをバックグラウンドスレッドに維持します。バックグラウンドスレッドは、定期的にconnection.sleep()を呼び出してサーバーのハートビートに応答します。
13
el.atomo

非常にシンプル:このようなパターン。

import time

while True:
    try:
        communication_handles = connect_pika()
        do_your_stuff(communication_handles)
    except pika.exceptions.ConnectionClosed:
        print 'oops. lost connection. trying to reconnect.'
        # avoid rapid reconnection on longer RMQ server outage
        time.sleep(0.5) 

おそらく、コードをリファクタリングする必要がありますが、基本的には、例外をキャッチし、問題を軽減して作業を続行することです。 communication_handlesチャネル、キューなど、pikaを介してRabbitMQと通信するために必要なすべてのpika要素を含みます。

8
itsafire