두 노드 간의 상호 연결 중에 분산 교착 상태를 피하려면 어떻게해야합니까?


11

두 개의 피어 노드가 있다고 가정합니다. 첫 번째 노드는 두 번째 노드에 연결 요청을 보낼 수 있지만 두 번째 노드는 첫 번째 노드에 연결 요청을 보낼 수 있습니다. 두 노드 간의 이중 연결을 피하는 방법은 무엇입니까? 이 문제를 해결하려면 인바운드 또는 아웃 바운드 TCP 연결을 만들기 위해 수행 된 작업을 순차적으로 수행하면 충분합니다.

즉, 각 노드는 들어오는 연결과 나가는 연결 모두에 대해 각각의 새 연결 작성 작업을 순차적으로 처리해야합니다. 이러한 방식으로, 연결된 노드 목록을 유지 관리하거나, 노드에서 새로운 수신 연결을 수락하기 전에 또는 노드로 연결 요청을 보내기 전에이 노드가 이미 목록에 있는지 확인하면 충분합니다.

연결 작성 조작을 순차적으로 수행 하려면 연결된 노드 목록 에서 잠금 을 수행하는 것으로 충분합니다 . 실제로 각 새 연결마다 새 연결된 노드의 ID가이 목록에 추가됩니다. 그러나이 접근법이 분산 교착 상태를 일으킬 수 있는지 궁금합니다 .

  • 제 1 노드는 제 2 노드에 연결 요청을 전송할 수 있고;
  • 제 2 노드는 제 1 노드에 연결 요청을 전송할 수 있고;
  • 두 개의 연결 요청이 비동기 적이 지 않다고 가정하면 두 노드 모두 들어오는 연결 요청을 잠급니다.

이 문제를 어떻게 해결할 수 있습니까?

업데이트 : 그러나 다른 스레드 가이 목록에 액세스 할 수 있으므로 교착 상태 문제는 여전히 남아 있기 때문에 새로운 (들어오는 또는 나가는) 연결이 생성 될 때마다 여전히 목록을 잠 가야합니다.

업데이트 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의 요청을 수락합니다.

그것에 대해 어떻게 생각하세요?


이러한 종류의 알고리즘은 분석하고 증명하기가 매우 어렵습니다. 그러나 분산 컴퓨팅의 많은 측면에서 전문가 인 연구원이 있습니다. Leslie Lamport의 간행물 페이지를 확인하십시오. research.microsoft.com/en-us/um/people/lamport/pubs/pubs.html
DeveloperDon

답변:


3

"낙관적 인"접근 방식을 시도 할 수 있습니다. 먼저 연결 한 다음 동시 상호 연결이 감지되면 연결을 끊으십시오. 이렇게하면 새 연결을 만드는 동안 연결 요청을 유지할 필요가 없습니다. 들어오는 연결이 설정되면 목록을 잠그고 동일한 호스트에 나가는 연결이 있는지 확인합니다. 그렇다면 호스트 주소를 확인하십시오. 자신보다 작은 경우 나가는 연결을 끊습니다. 그렇지 않으면 들어오는 것을 분리하십시오. 주소가 다르게 비교되고 두 연결 중 하나가 끊어지기 때문에 피어 호스트가 반대 작업을 수행합니다. 이 방법을 사용하면 연결을 다시 시도하지 않아도되고 시간 단위당 더 많은 연결 요청을 수락 할 수 있습니다.


그러나 다른 스레드 가이 목록에 액세스 할 수 있기 때문에 교착 상태 문제는 여전히 남아 있기 때문에 새로운 (들어오는 또는 나가는) 연결이 생성 될 때마다 목록을 잠 가야합니다.
enzom83

@ enzom83 아니요, 피어는 잠금이 필요한 작업을 완료 할 때까지 기다릴 필요가 없기 때문에이 구성에서는 교착 상태가 불가능합니다. 뮤텍스는 목록의 내부를 보호하는 것입니다. 일단 획득 한 후에는 중요한 섹션 내에서 다른 리소스를 기다릴 필요가 없으므로 알려진 시간이 걸립니다.
dasblinkenlight 1

연결 요청이 비동기 적이 지 않고 임계 섹션 내에서 수행되는 경우 교착 상태가 발생할 수 있습니다 .이 경우 연결 요청이 수락 될 때까지 노드가 뮤텍스를 떠날 수 없습니다. 그렇지 않으면 노드를 추가하거나 제거하기 위해서만 목록에서 잠금을 수행해야합니다.이 경우 중복 연결 등을 확인해야합니다. 마지막으로 다른 옵션은 비동기 연결 요청을 보내는 것입니다.
enzom83

1
@ enzom83 중요 섹션 내에서 연결을 요청하지 않으면 분산 교착 상태가 발생하지 않습니다. 이것이 낙관적 접근 방식의 아이디어입니다. 노드를 추가하거나 제거하기 위해서만 목록에서 잠금을 수행하고 노드를 추가 할 때 상호 연결을 찾으면 "작은 주소"를 사용하여 중요 섹션을 떠난 후 연결을 끊습니다. 규칙 (답변에서).
dasblinkenlight

4

한 노드가 다른 노드로 요청을 보내면 임의의 64 비트 정수가 포함될 수 있습니다. 노드가 연결 요청을받을 때 이미 소유 한 노드 중 하나를 보낸 경우 가장 높은 번호를 유지하고 다른 하나는 삭제합니다. 예를 들면 다음과 같습니다.

시간 1 : A가 B에 연결을 시도하고 숫자 123을 보냅니다.

시간 2 : B가 A에 연결을 시도하고 숫자 234를 보냅니다.

시간 3 : B는 A의 요청을받습니다. B 자신의 요청이 더 많으므로 A의 요청을 거부합니다.

시간 4 : A는 B의 요청을받습니다. B의 요청이 더 많으므로 A는 요청을 수락하고 자체 요청을 삭제합니다.

난수를 생성하려면 벽시계 시간을 기반으로하는 난수 생성기의 기본 시딩을 사용하지 않고 / dev / urandom을 사용하여 난수 생성기를 시드해야합니다. 두 노드가 무시할 수없는 가능성이 있습니다. 같은 씨앗을 얻을 것입니다.

난수 대신에 미리 숫자를 미리 분배하거나 (즉, 1에서 n까지 모든 컴퓨터에 번호를 매기거나) MAC 주소를 사용하거나 충돌 가능성이 아주 작은 숫자를 찾는 다른 방법을 사용할 수도 있습니다. 무시할 수있는.


3

이해하면 피하려고하는 문제는 다음과 같습니다.

  • Node1은 노드 2로부터 연결을 요청합니다.
  • Node1 잠금 연결 목록
  • Node2는 노드 1에서 연결을 요청합니다.
  • Node2 잠금 연결 목록
  • Node2는 node1로부터 연결 요청을 수신하고 목록이 잠겨 있기 때문에 거부합니다.
  • Node1은 node2로부터 연결 요청을 받고 목록이 잠겨 있기 때문에 거부합니다.
  • 어느 쪽도 서로 연결되지 않습니다.

나는 이것을 처리하는 몇 가지 다른 방법을 생각할 수 있습니다.

  1. 노드에 연결하려고하는데 "목록 잠금"메시지로 요청을 거부하면 임의의 시간 (밀리 초) 동안 기다렸다가 다시 시도하십시오. (임의의 중요성은 중요합니다. 둘 다 정확히 같은 시간 동안 기다릴 때와 같은 문제를 무한정 반복 할 가능성은 훨씬 줄어 듭니다 .)
  2. 두 시스템의 시계를 시간 서버와 동기화하고 연결 요청과 함께 타임 스탬프를 보냅니다. 현재 연결하려는 노드에서 연결 요청이 들어 오면 두 노드 모두 먼저 연결을 시도한 노드 중 하나가 이기고 다른 연결이 닫히는 것에 동의합니다.

문제는 요청을받는 노드가 연결을 거부 할 수 없지만 목록에서 잠금을 얻기 위해 무기한 대기 상태로 남아 있다는 것입니다.
enzom83
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.