Załóżmy, że mamy dwa węzły równorzędne: pierwszy węzeł może wysłać żądanie połączenia do drugiego, ale także drugi może wysłać żądanie połączenia do pierwszego. Jak uniknąć podwójnego połączenia między dwoma węzłami? Aby rozwiązać ten problem, wystarczy wykonać sekwencyjne operacje wykonywane w celu utworzenia przychodzących lub wychodzących połączeń TCP.
Oznacza to, że każdy węzeł powinien przetwarzać sekwencyjnie każdą nową operację tworzenia połączenia, zarówno dla połączeń przychodzących, jak i wychodzących. W ten sposób, utrzymując listę połączonych węzłów, przed zaakceptowaniem nowego połączenia przychodzącego z węzła lub przed wysłaniem żądania połączenia do węzła wystarczy sprawdzić, czy ten węzeł jest już na liście.
Aby sekwencyjnie wykonywać operacje tworzenia połączeń, wystarczy wykonać blokadę na liście połączonych węzłów: w rzeczywistości dla każdego nowego połączenia do tej listy dodawany jest identyfikator nowego podłączonego węzła. Zastanawiam się jednak, czy to podejście może spowodować rozproszenie rozproszone :
- pierwszy węzeł może wysłać żądanie połączenia do drugiego;
- drugi węzeł mógłby wysłać żądanie połączenia do pierwszego;
- zakładając, że dwa żądania połączenia nie są asynchroniczne, oba węzły blokują wszelkie przychodzące żądania połączenia.
Jak mogę rozwiązać ten problem?
AKTUALIZACJA: Jednak nadal muszę blokować listę za każdym razem, gdy tworzone jest nowe (przychodzące lub wychodzące) połączenie, ponieważ inne wątki mogą uzyskiwać dostęp do tej listy, problem zakleszczenia nadal pozostaje.
AKTUALIZACJA 2: W oparciu o twoją radę napisałem algorytm zapobiegający wzajemnej akceptacji żądania logowania. Ponieważ każdy węzeł jest równorzędny, może mieć procedurę klienta do wysyłania nowych żądań połączenia oraz procedurę serwera do akceptowania połączeń przychodzących.
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
}
Przykład: Port IP: węzeł A to A: 7001 - Port IP: węzeł B to B: 8001.
Załóżmy, że węzeł A wysłał żądanie logowania do węzła B: 8001. W takim przypadku węzeł A wywołuje procedurę logowania, wysyłając, wysyłając własny adres nasłuchiwania (A: 7001). W konsekwencji tabela sąsiadów A węzła A zawiera adres zdalnego węzła (B: 8001): adres ten jest powiązany ze stanem PODŁĄCZANIE. Węzeł A czeka na zaakceptowanie lub odrzucenie żądania logowania przez węzeł B.
W międzyczasie węzeł B mógł również wysłać żądanie połączenia na adres węzła A (A: 7001), a następnie węzeł A może przetwarzać żądanie węzła B. Tak więc tabela sąsiadów B zawiera adres zdalnego węzeł (A: 7001): ten adres jest powiązany ze stanem PODŁĄCZANIE. Węzeł B czeka na węzeł A zaakceptuj lub odrzuć żądanie logowania.
Jeśli po stronie serwera węzła A odrzuca żądanie z B: 8001, to muszę się upewnić, że po stronie serwera węzła B zaakceptuje żądanie z A: 7001. Podobnie, jeśli strona serwera węzła B odrzuci żądanie od A: 7001, to muszę się upewnić, że strona serwera węzła A zaakceptuje żądanie od B: 8001.
Zgodnie z zasadą „małego adresu” w tym przypadku węzeł A odrzuci żądanie logowania przez węzeł B, podczas gdy węzeł B zaakceptuje żądanie z węzła A.
Co myślicie o tym?