ZeroMQのheartbeatを試してみた
ZeroMQのversion 4.2からheartbeat機能が追加されました。 元々コネクションの切断は検知してzmq_socket_monitor(3)で通知してくれたのですが、 TCPエラーを吐かずにコネクションが死んだり、Out of Memory等でプロセスが止まったりした場合は検知できないので heartbeat機能が追加されたようです。(参考:ZeroMQのRFC)
heartbeatに関するソケットのオプションは以下の3つです。
ZMQ_HEARTBEAT_IVL
ZMQ_HEARTBEAT_TIMEOUT
ZMQ_HEARTBEAT_TTL
ZMQ_HEARTBEAT_IVL
ミリ秒の間隔でpingを送り、ZMQ_HEARTBEAT_TIMEOUT
ミリ秒経っても
pongが返ってこなかったらコネクションが死んだとみなすという感じです。また、pongを返す側では
ZMQ_HEARTBEAT_TTL
ミリ秒経っても次のpingが来なかったらコネクションが死んだとみなします。
ZMQ_HEARTBEAT_IVL
を設定することで初めてheartbeatが動作します。
試しに以下のような設定をしてみます。基本的にどちらか片方(connect側もしくはbind側)のソケットにだけ設定しておけばOKだと思います。
int interval = 100, timeout = 1000, ttl = 1000; zmq_setsockopt(sock, ZMQ_HEARTBEAT_IVL, &interval, sizeof(int)); zmq_setsockopt(sock, ZMQ_HEARTBEAT_TIMEOUT, &timeout, sizeof(int)); zmq_setsockopt(sock, ZMQ_HEARTBEAT_TTL, &ttl, sizeof(int)); zmq_setsockopt(sock, ZMQ_HANDSHAKE_IVL, &timeout, sizeof(int));
ZMQ_HEARTBEAT_TTL
はZMQ_HEARTBEAT_IVL
よりも十分大きい値にしておく必要があります。
そうしないと接続が頻繁に切れてしまいます。ZMQ_HEARTBEAT_TIMEOUT
も短すぎると
偽陽性が増えてしまうので注意です。
また、ZMQ_HANDSHAKE_IVL
を設定しています。ZeroMQではソケットが接続した後に
ソケットの設定情報を交換する(これをhandshakeと呼ぶ)そうなのですが、止まっているソケットに再接続した際に
ZMQ_HEARTBEAT_TIMEOUT
よりもZMQ_HANDSHAKE_IVL
が優先されるため、両方共同じ値にしておきます。
ZMQ_HANDSHAKE_IVL
のデフォルト値は30秒なので、これを設定しないと
ZMQ_HEARTBEAT_TIMEOUT
をどれだけ短くしても再接続時に30秒待ってしまいます。
実際にこんな感じのコードを書いて動かします。
bind/connectが完了した時点で、connect側のプロセスにSIGTSTPを送るとbind側のzmq_socket_monitor(3)
から
ZMQ_EVENT_DISCONNECTED
が通知されます。逆に、bind側のプロセスにSIGTSTPを送ると、connect側の
zmq_socket_monitor(3)
からZMQ_EVENT_DISCONNECTED
が通知された後に、再接続を試みます。
bind側のlistenが残っているからか、再接続は成功しますが、handshakeが失敗して再びZMQ_EVENT_DISCONNECTED
が
通知されるというような挙動になります。
また、heartbeatを有効にしても前回の調査同様、unbind/disconnect時には 相手側のソケットには何も通知されません。disconnectしても再接続しようとするし、unbind/disconnectとは何なのだろう…。 とりあえずソケットを切断するときは必ずcloseした方が良さそうです。
※ZeroMQ v4.2.1を使用