如何在两个节点之间的相互连接期间避免分布式死锁?


11

假设我们有两个对等节点:第一个节点可以向第二个节点发送连接请求,但是第二个节点可以向第一个节点发送连接请求。如何避免两个节点之间的双重连接?要解决此问题,顺序执行为创建入站或出站TCP连接而执行的操作就足够了。

这意味着每个节点都应按顺序处理每个新的连接创建操作,包括传入连接和传出连接。这样,在从节点接受新的传入连接之前或在向节点发送连接请求之前,维护已连接节点的列表,只需检查该节点是否已存在于列表中就足够了。

为了使创建连接的操作顺序进行,对连接的节点列表执行锁定就足够了:实际上,对于每个新连接,新连接的节点的标识符都添加到该列表中。但是,我想知道这种方法是否会导致分布式死锁

  • 第一节点可以向第二节点发送连接请求;
  • 第二节点可以向第一节点发送连接请求;
  • 假设两个连接请求不是异步的,则两个节点都锁定所有传入的连接请求。

我该如何解决这个问题?

更新:但是,每次创建新的(传入或传出的)连接时,我仍然必须锁定列表,因为其他线程可能会访问该列表,因此仍然存在死锁问题。

更新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

Answers:


3

您可以尝试“乐观”的方法:先连接,然后在检测到并发的相互连接时断开连接。这样,您在建立新连接时就不需要保留连接请求:建立传入连接后,锁定列表,并查看是否有到同一主机的传出连接。如果这样做,请检查主机的地址。如果小于您的连接,请断开您的传出连接;否则,请断开连接。否则,断开传入的。您的对等主机将执行相反的操作,因为地址的比较将有所不同,并且两个连接之一将被丢弃。这种方法使您避免重试连接,并有可能帮助您每单位时间接受更多的连接请求。


但是,每次创建新的(传入或传出的)连接时,我仍然必须锁定列表,因为其他线程可能会访问该列表,因此仍然存在死锁问题。
enzom83

@ enzom83不,在这种方案下不可能发生死锁,因为对等方永远不需要等待您完成需要锁定的操作。互斥体是为了保护列表的内部结构。一旦获得它,您将在已知的时间内离开,因为您无需等待关键部分内的任何其他资源。
dasblinkenlight 2012年

好的,但是如果连接请求不是异步的,并且在关键部分内执行,则可能会发生死锁:在这种情况下,除非接受其连接请求,否则节点无法离开互斥锁。否则,我应该仅在列表上执行锁定操作以添加(或删除)节点:在这种情况下,我应该检查重复的连接等。最后,另一种选择是发送异步连接请求。
enzom83

1
@ enzom83,只要您不从关键部分请求连接,就不会得到分布式死锁。这就是乐观方法的想法-您仅在列表上执行锁定以添加或删除节点,并且如果在添加节点时发现对等连接,则在离开关键部分后使用“较小地址”将其断开规则(根据答案)。
dasblinkenlight 2012年

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

据我了解,您要避免的问题是这样的:

  • 节点1请求来自节点2的连接
  • Node1锁定连接列表
  • Node2请求来自节点1的连接
  • Node2锁定连接列表
  • Node2收到来自node1的连接请求,由于列表已锁定而拒绝
  • Node1收到来自Node2的连接请求,由于列表已锁定而拒绝
  • 没有人最终会彼此连接。

我可以想到几种不同的方法来解决这个问题。

  1. 如果尝试连接到节点,并且节点拒绝您的请求并显示“列表已锁定”消息,请等待随机的毫秒数,然后重试。(随机性很关键。这使得双方等待完全相同的时间并无限期地重复相同的问题的可能性大大降低。)
  2. 将两个系统的时钟与时间服务器同步,并发送时间戳和连接请求。如果一个连接请求来自您当前尝试连接到的节点,则两个节点都同意哪个尝试先连接就赢,而另一个连接被关闭。

问题还在于,接收请求的节点不能拒绝连接,但是它将无限期地等待以获取列表上的锁。
enzom83
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.