Suponha que tenhamos dois nós de mesmo nível: o primeiro nó pode enviar uma solicitação de conexão para o segundo, mas também o segundo pode enviar uma solicitação de conexão para o primeiro. Como evitar uma conexão dupla entre os dois nós? Para resolver esse problema, seria suficiente tornar sequenciais as operações executadas para criar conexões TCP de entrada ou saída.
Isso significa que cada nó deve processar sequencialmente cada nova operação de criação de conexão, tanto para conexões de entrada quanto para conexões de saída. Dessa maneira, mantendo uma lista de nós conectados, antes de aceitar uma nova conexão de entrada de um nó ou antes de enviar uma solicitação de conexão a um nó, será suficiente verificar se esse nó já está presente na lista.
Para tornar sequenciais as operações de criação de conexões, é suficiente executar um bloqueio na lista de nós conectados: de fato, para cada nova conexão, o identificador do novo nó conectado é adicionado a esta lista. No entanto, gostaria de saber se essa abordagem pode causar um conflito distribuído :
- o primeiro nó pode enviar uma solicitação de conexão para o segundo;
- o segundo nó pode enviar uma solicitação de conexão para o primeiro;
- assumindo que as duas solicitações de conexão não sejam assíncronas, os dois nós bloqueiam todas as solicitações de conexão recebidas.
Como eu poderia resolver esse problema?
ATUALIZAÇÃO: No entanto, ainda tenho que bloquear a lista toda vez que uma nova conexão (de entrada ou saída) é criada, já que outros threads podem acessar essa lista, e o problema de conflito ainda permanece.
ATUALIZAÇÃO 2: Com base no seu conselho, escrevi um algoritmo para impedir a aceitação mútua de uma solicitação de login. Como cada nó é um par, pode haver uma rotina de cliente para enviar novas solicitações de conexão e uma rotina de servidor para aceitar conexões de entrada.
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
}
Exemplo: O IP: porta do nó A é A: 7001 - O IP: porta do nó B é B: 8001.
Suponha que o nó A tenha enviado uma solicitação de login ao nó B: 8001. Nesse caso, o nó A chama a rotina de login enviando enviando seu próprio endereço de escuta (A: 7001). Como conseqüência, a tabela vizinhos_ do nó A contém o endereço do nó remoto (B: 8001): esse endereço está associado ao estado CONNECTING. O nó A está aguardando o nó B aceitar ou negar a solicitação de login.
Enquanto isso, o nó B também pode ter enviado uma solicitação de conexão para o endereço do nó A (A: 7001), e o nó A pode estar processando a solicitação do nó B. Portanto, a tabela de vizinhos do nó B contém o endereço do controle remoto. nó (A: 7001): esse endereço está associado ao estado CONNECTING. O nó B está aguardando o nó A aceitar ou negar a solicitação de login.
Se o lado do servidor do nó A rejeitar a solicitação de B: 8001, devo ter certeza de que o lado do servidor do nó B aceitará a solicitação de A: 7001. Da mesma forma, se o lado do servidor do nó B rejeitar a solicitação de A: 7001, devo ter certeza de que o lado do servidor do nó A aceitará a solicitação do B: 8001.
De acordo com a regra "pequeno endereço" , nesse caso, o nó A rejeitará a solicitação de login pelo nó B, enquanto o nó B aceitará a solicitação do nó A.
O que você acha disso?
fonte
Respostas:
Você pode tentar uma abordagem "otimista": conecte primeiro e depois desconecte se detectar uma conexão mútua simultânea. Dessa forma, você não precisaria manter de fora as solicitações de conexão enquanto estiver fazendo novas conexões: quando uma conexão de entrada for estabelecida, bloqueie a lista e verifique se você possui uma conexão de saída com o mesmo host. Se sim, verifique o endereço do host. Se for menor que o seu, desconecte sua conexão de saída; caso contrário, desconecte o de entrada. Seu host de mesmo nível fará o oposto, porque os endereços serão comparados de maneira diferente e uma das duas conexões será descartada. Essa abordagem permite evitar novas tentativas de conexão e potencialmente ajuda a aceitar mais solicitações de conexão por unidade de tempo.
fonte
Quando um nó envia uma solicitação para outro, ele pode incluir um número inteiro aleatório de 64 bits. Quando um nó recebe uma solicitação de conexão, se já enviou uma, é mantido com o número mais alto e eliminado os outros. Por exemplo:
Tempo 1: A tenta se conectar a B, envia o número 123.
Tempo 2: B tenta se conectar a A, envia o número 234.
Tempo 3: B recebe a solicitação de A. Como a própria solicitação de B tem um número maior, ela nega a solicitação de A.
Tempo 4: A recebe a solicitação de B. Como a solicitação de B tem um número maior, A a aceita e descarta sua própria solicitação.
Para gerar o número aleatório, certifique-se de propagar seu gerador de números aleatórios com / dev / urandom, em vez de usar a propagação padrão do seu gerador de números aleatórios, que geralmente é baseado no tempo do relógio de parede: há uma chance não ignorável de que dois nós receberá a mesma semente.
Em vez de números aleatórios, você também pode distribuir números antecipadamente (ou seja, apenas numerar todas as máquinas de 1 a n), ou usar um endereço MAC, ou alguma outra maneira de encontrar um número em que a probabilidade de colisão seja tão pequena quanto possível. ignorável.
fonte
Se eu entendi, o problema que você está tentando evitar é o seguinte:
Eu posso pensar em algumas maneiras diferentes de lidar com isso.
fonte