Supposons que nous ayons deux nœuds homologues: le premier nœud peut envoyer une demande de connexion au second, mais également le second peut envoyer une demande de connexion au premier. Comment éviter une double connexion entre les deux nœuds? Pour résoudre ce problème, il suffirait de rendre séquentielles les opérations effectuées pour créer des connexions TCP entrantes ou sortantes.
Cela signifie que chaque nœud doit traiter séquentiellement chaque nouvelle opération de création de connexion, à la fois pour les connexions entrantes et pour les connexions sortantes. De cette façon, en conservant une liste de nœuds connectés, avant d'accepter une nouvelle connexion entrante d'un nœud ou avant d'envoyer une demande de connexion à un nœud, il suffira de vérifier si ce nœud est déjà présent dans la liste.
Pour rendre séquentielles les opérations de création de connexions, il suffit d'effectuer un verrouillage sur la liste des nœuds connectés: en effet, pour chaque nouvelle connexion, l'identifiant du nouveau nœud connecté est ajouté à cette liste. Cependant, je me demande si cette approche peut provoquer un blocage distribué :
- le premier nœud pourrait envoyer une demande de connexion au second;
- le deuxième nœud pourrait envoyer une demande de connexion au premier;
- en supposant que les deux demandes de connexion ne sont pas asynchrones, les deux nœuds verrouillent toutes les demandes de connexion entrantes.
Comment pourrais-je résoudre ce problème?
MISE À JOUR: Cependant, je dois toujours verrouiller la liste chaque fois qu'une nouvelle connexion (entrante ou sortante) est créée, car d'autres threads peuvent accéder à cette liste, alors le problème de blocage resterait.
MISE À JOUR 2: Sur la base de vos conseils, j'ai écrit un algorithme pour empêcher l'acceptation mutuelle d'une demande de connexion. Étant donné que chaque nœud est un homologue, il pourrait avoir une routine client pour envoyer de nouvelles demandes de connexion et une routine serveur pour accepter les connexions entrantes.
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
}
Exemple: Le port IP: du nœud A est A: 7001 - Le port IP: du nœud B est B: 8001.
Supposons que le nœud A a envoyé une demande de connexion au nœud B: 8001. Dans ce cas, le nœud A appelle la routine de connexion en envoyant en envoyant sa propre adresse d'écoute (A: 7001). En conséquence, la table voisins_ du nœud A contient l'adresse du nœud distant (B: 8001): cette adresse est associée à l'état CONNECTING. Le nœud A attend que le nœud B accepte ou refuse la demande de connexion.
Pendant ce temps, le nœud B peut également avoir envoyé une demande de connexion à l'adresse du nœud A (A: 7001), puis le nœud A peut traiter la demande du nœud B. Ainsi, la table_voisins du nœud B contient l'adresse de la télécommande nœud (A: 7001): cette adresse est associée à l'état CONNECTING. Le nœud B attend que le nœud A accepte ou refuse la demande de connexion.
Si le côté serveur du nœud A rejette la demande de B: 8001, je dois être sûr que le côté serveur du nœud B acceptera la demande de A: 7001. De même, si le côté serveur du nœud B rejette la demande de A: 7001, je dois être sûr que le côté serveur du nœud A acceptera la demande de B: 8001.
Selon la règle de la "petite adresse" , dans ce cas, le nœud A rejettera la demande de connexion par le nœud B, tandis que le nœud B acceptera la demande du nœud A.
Qu'est ce que tu penses de ça?