前回のmemberlistに続き、今回はserfのコードを読んでみました。

Serf Summery

serfは、memberlistを利用してクラスタ内のノードのJoin/Leaveやユーザ定義のイベントをフックし、各イベントにマッピングされたユーザスクリプトを実行します。

serf2_2

Packages

serfは以下の3つのpackageで構成されています。

serf2_1

Full State Sync Message Expansion

serfmemberlistのDelegateを定義しています。このDelegateを使って、memberlistがノード間でstate syncする際にやり取りする情報を拡張しています。具体的には以下の情報を追加で渡すようになっています。

serf2_3

Join/Leave Event

serfmemberlistのEventDelegateを定義しています。このEventDelegateを使って、ノードのJoin/Leave時に以下の事を行っています。

NodeJoin

NodeLeave

UserMessage And SerfEvent

memberlistでは、gossipメッセージ送信時に、送信バッファの空き容量に応じてユーザ定義のメッセージを送信することができます(memberlist/broadcast.goのGetBroadcasts)。serfはこの機構を利用し、gossipメッセージ送信時に以下のメッセージを追加しています。

このうち、messageJoinTypeとmessageLeaveTypeは、後述のコマンドを使って意図的にクラスタにjoin/leaveした場合に送信されます。恐らく、コマンド等で意図的に実行したjoin/leaveと、何らかの障害によって起こったjoin/leaveと区別されて扱われているのだと思います。これらのメッセージが送信されると、recentJoinやrecentLeaveにノード情報が登録されます。

MemberStatus

memberlistでも各ノードの状態を管理していましたが、それとは別にserfでも各ノードの状態を管理しています。serfで管理しているノードの状態と遷移の契機は以下のとおりです。

serf2_4

Coalesce

Coalesceは、個々のイベントを集約して1つに纏めて、メッセージの送信を効率化する為の仕組みです。現時点でdefaultではdisableとなっています。
Coalesceについては、config.goのCoalescePeriodのところに記載されているコメントが分かりやすいので、簡単に訳してみました。

Serfは、EventChに送信されたノイズ量を削減する為に、複数のイベントを1つに纏めることができる。例えば、5つのノードが一気にクラスタにjoinすると、EventChには5つのEventMemberJoinが送信されるのではなく、5ノードの情報を含んだ1つのEventMemberJoinが送信される。これは障害や復帰の通知の氾濫を軽減する。

Snapshot

Snapshotは、現在保持しているノード情報をファイルに出力しておく機能です。これもconfig.goのSnapshotPathのところに記載されているコメントを簡単に訳してみました。

SnapshotPathを設定すると、生存しているノードとLamport clock値を保存したスナップショットが使えるようになる。Serf起動時にスナップショットを用いると、前回認識していたノードをjoinしようとしなくなり、過去のユーザイベントの再発を回避する

Agent

agentはノード上に、これまで見てきたmemberlistserfのserfパッケージの機能を含むプロセスを立ち上げます。つまり、agentが起動して初めてこれらの機能がノード上で有効になります。

また、agentはRPCサーバでもあります。このRPCの受け口(TCP)は、コマンド引数”rpc-addr”で指定します。デフォルトは”127.0.0.1:7373”となっています。

agent起動時に、serfのEventChに対してイベントハンドラを設定します。現時点では固定でScriptEventHandlerが設定されるようになっています。このScriptEventHandlerによって、各イベントにマッピングされたスクリプトが実行されます。

Command

commandはjoinやleave、ユーザイベントを発行するCLIを提供します。これらのcommandを実行するとagentに対してRPCでリクエストを発行し、agentが処理を実行する仕組みになっています。

Conclusion

今回はserfのコードを読んでみました。正直あまり細かいところまでは理解できていませんが、概要は掴めたと思います。

記載している内容について間違っているところがあれば教えてもらえると嬉しいです(golangは一行も書いたことがないので)。

Update