Ehcacheの自動ピア・ディスカバリ周りのコードを読んでみる(その3)
MulticastKeepaliveHeartbeatSender$MulticastServerThread
ハートビートを送るのは、CacheManager の初期化時に別スレッドとして作成・実行された MulticastServerThread です。run()の内部は2重のループ構造になっていて、外側のループは、ソケット周りの例外時の再接続用。内側のループがハートビート送信の本質的なコードです。以下に、後者のループ処理を示します。
- ハートビート情報を作成。情報の内容は、RMIで公開するローカル・キャッシュ・ピアのURL情報。1500バイトを超えないサイズに分割されたリストとして作成される。
- CachePeerListenerから、全ローカル・キャッシュ・ピア(ローカルのEhcacheインスタンス)の情報をListとして取得。(RmiCacheManagerPeerListener側では、キャッシュピアのリストをこのタイミングで作成して返している。)
- ListのhashCode()を取得。
- ListのhashCode()が、以前に保存された値と同様の場合は、以前に保存された compressedUrlListList を return。(TODO:何故にhashCodeの比較をしているのか分からん。ハッシュが衝突したらどうすんの?)
- ピアのURLを、150毎の単位で文字列連結して、バイト列化し、gzip圧縮し、圧縮後URLリストとしてまとめ、それをreturnする。
- 全パケットを送信する。
- 指定されたインターバル(デフォルトは5秒)スリープする。
- ループの先頭に戻る。
ここで面白いのは、1単位の圧縮後URLリストのバイト列の長さがMTU(1500)を超えると、fatulエラーを出すようになっていること。確かに、パケットレベルで複数に分割されるとすべてのパケットの到達保障をする必要があるからなぁ。納得です。
と思いきや、ん? PayloadUtil.MTU = 1500 という設定になっているのだが、ペイロードのサイズとMTUのサイズ比較してもダメじゃない? IPデータグラムの全体のサイズが1500なんだから、ペイロードのサイズはIPヘッダ20バイトとUDPヘッダ8バイトを除いた1472バイト以内に抑えなければ、IPデータグラムがフラグメント起こしますよね?
素朴な疑問
- RMICacheManagerPeerListener#getBoundCachePeers() では、何故に呼ばれるたびにリストを作成しているのだろうか。スレッドセーフにするために、ローカルピア情報のMapインスタンスをモニタにしてsynchronizedまでかける必要があるのだから、重さ倍増。確かにMap内部の情報をスレッドセーフにするために、Map自身をロックモニタにするのは理にかなっているけど、これだけ関連箇所が多くなってくると、Mapの変更タイミングでListも作成して、Listを使う場合は、Mapでsynchronizedかけてねという形にすれば問題なさそうだが。
- ハートビートという死活確認用の機構に、全ピアの情報を絡めるというのはどうなんでしょうね。Web系の案件で分散キャッシュが使われることが多いと思うのですが、動的にピアの情報が変更されることってほとんどないのではないでしょうか? ハートビートによりクラスタへの参加者が認識できていれば、指名して聞きにいき、TCPベースのプロトコルで情報を確認してもよさそうですよね。ハートビートがうまくいくけどピア・ディスカバリに失敗する場合はどうするのという懸念もあるのかな。でも、割り切っちゃってもいけるような気もする。