2つのピアノードがあるとします。最初のノードは2番目のノードに接続要求を送信できますが、2番目のノードは最初のノードに接続要求を送信できます。2つのノード間の二重接続を回避する方法は?この問題を解決するには、インバウンドまたはアウトバウンドTCP接続を作成するために実行される操作を連続して行うだけで十分です。
つまり、各ノードは、着信接続と発信接続の両方で、新しい接続作成操作を順番に処理する必要があります。この方法では、接続されたノードのリストを維持し、ノードからの新しい着信接続を受け入れる前、またはノードに接続要求を送信する前に、このノードがリストに既に存在するかどうかを確認するだけで十分です。
接続を作成する操作を順番に行うには、接続ノードのリストをロックするだけで十分です。実際、新しい接続ごとに、新しい接続ノードの識別子がこのリストに追加されます。このアプローチは、原因となることができればしかし、私は疑問に思う分散デッドロックを:
- 最初のノードは、2番目のノードに接続要求を送信できます。
- 2番目のノードは最初のノードに接続要求を送信できます。
- 2つの接続要求が非同期でないと仮定すると、両方のノードが着信接続要求をロックします。
どうすればこの問題を解決できますか?
更新:ただし、新しい(着信または発信)接続が作成されるたびにリストをロックする必要があります。他のスレッドがこのリストにアクセスする可能性があるため、デッドロックの問題が残ることになります。
更新2:あなたのアドバイスに基づいて、ログイン要求の相互受け入れを防ぐアルゴリズムを書きました。各ノードはピアであるため、新しい接続要求を送信するクライアントルーチンと、着信接続を受け入れるサーバールーチンを持つことができます。
ClientSideLoginRoutine() {
for each (address in cache) {
lock (neighbors_table) {
if (neighbors_table.contains(address)) {
// there is already a neighbor with the same address
continue;
}
neighbors_table.add(address, status: CONNECTING);
} // end lock
// ...
// The node tries to establish a TCP connection with the remote address
// and perform the login procedure by sending its listening address (IP and port).
boolean login_result = // ...
// ...
if (login_result)
lock (neighbors_table)
neighbors_table.add(address, status: CONNECTED);
} // end for
}
ServerSideLoginRoutine(remoteListeningAddress) {
// ...
// initialization of data structures needed for communication (queues, etc)
// ...
lock(neighbors_table) {
if(neighbors_table.contains(remoteAddress) && its status is CONNECTING) {
// In this case, the client-side on the same node has already
// initiated the procedure of logging in to the remote node.
if (myListeningAddress < remoteListeningAddress) {
refusesLogin();
return;
}
}
neighbors_table.add(remoteListeningAddress, status: CONNECTED);
} // end lock
}
例: ノードAのIP:ポートはA:7001-ノードBのIP:ポートはB:8001です。
ノードAがログイン要求をノードB:8001に送信したとします。この場合、ノードAは、独自のリスニングアドレス(A:7001)を送信して送信することにより、ログインルーチンを呼び出します。結果として、ノードAのneighbors_tableにはリモートノードのアドレス(B:8001)が含まれます。このアドレスはCONNECTING状態に関連付けられています。ノードAは、ノードBがログイン要求を受け入れるか拒否するのを待っています。
一方、ノードBもノードAのアドレスに接続要求を送信した可能性があり(A:7001)、ノードAはノードBの要求を処理している可能性があります。したがって、ノードBのneighbors_tableにはリモートのアドレスが含まれますノード(A:7001):このアドレスはCONNECTING状態に関連付けられています。ノードBは、ノードAがログイン要求を受け入れるか拒否するのを待っています。
ノードAのサーバー側がB:8001からの要求を拒否する場合、ノードBのサーバー側がA:7001からの要求を受け入れることを確認する必要があります。同様に、ノードBのサーバー側がA:7001からの要求を拒否する場合、ノードAのサーバー側がB:8001からの要求を受け入れることを確認する必要があります。
「小さなアドレス」ルールに従って、この場合、ノードAはノードBによるログイン要求を拒否しますが、ノードBはノードAからの要求を受け入れます。
あれについてどう思う?