2016-11-02 1 views
0

Я имею дело с распределенной ситуацией «тупика» в одноранговой системе связи (написанной и запущенной на Python 3.5). В этой системе каждый узел поддерживает 2 так называемых inconn и outconn связи с каждым из своих сверстников. Я использую select.poll() для выполнения мультиплексирования. Поэтому иногда может произойти следующий тупик: если два подключенных партнера, пытающихся отправить их другому через outconn, цикл select.poll() каждого однорангового узла блокирует send(), и поэтому другая сторона не может recv() на inconn подключение.send() через блокирующий сокет вышло из строя, но сообщение прибыло в пункт назначения после этого

Способ, которым я управляю этим тупиком, заключается в установке() на разъеме outconnn, который кажется рабочим. Тем не менее, интересно, что сообщение, похоже, может прибыть в пункт назначения после истечения времени ожидания сокета. Вот пример бревна двух узлов:

Узел (192.168.56.109)

INFO: [2016-11-02 11:08:05,172] [COOP] Sending ASK_COOP [2016-11-02 11:08:05.172643] to 192.168.56.110 for segment 2.

WARNING: [2016-11-02 11:08:06,173] [COOP] Cannot send to 192.168.56.110. Error: timed out

INFO: [2016-11-02 11:08:06,174] [COOP] Message from 192.168.56.110 is available on 10.

INFO: [2016-11-02 11:08:06,174] [COOP] Get HEARTBEAT [2016-11-02 11:08:04.503723] from 192.168.56.110 for segment 2.

Node B (192.168.56.110)

INFO: [2016-11-02 11:08:04,503] [COOP] Sending HEARTBEAT [2016-11-02 11:08:04.503723] to 192.168.56.109 for segment 2.

WARNING: [2016-11-02 11:08:05,505] [COOP] Cannot send to 192.168.56.109. Error: timed out

INFO: [2016-11-02 11:08:05,505] [COOP] Message from 192.168.56.109 is available on 11.

INFO: [2016-11-02 11:08:05,505] [COOP] Get ASK_COOP [2016-11-02 11:08:05.172643] from 192.168.56.109 for segment 2.

Могу ли я узнать, почему в том, что? И, кстати, мой способ справиться с таким тупиком - хорошая практика? Если нет, то какова наилучшая практика, чтобы избежать такого распределенного тупика?

ответ

1

По моему опыту, наилучшая практика для предотвращения этой проблемы - всегда использовать неблокирующий ввод-вывод. Если ваше приложение никогда не блокирует внутри send() или recv(), тогда не может быть тупика (по крайней мере, не того типа, который вы описываете).

Конечно, неблокирующий ввод-вывод приносит свои сложности - в частности, ваш код должен иметь возможность обрабатывать частичные отправки и частичные получать правильно. На практике это означает, что цикл обработки событий вашего приложения может выглядеть примерно так (псевдокод):

while true: 
    block in select() until at least one socket is ready-for-read (or ready-for write, if you have data you want to send on that socket) 

    for each ready-for-read socket:  
     read as many bytes as you can (without blocking) into a FIFO receive buffer that you have associated with that socket 
     parse as many complete messages as you can out of the beginning of the FIFO buffer 
     (pop the parsed bytes out of the FIFO when you're done with them) 

    for each ready-for-write socket: 
     send as many bytes as you can (without blocking) from a FIFO send buffer that you have associated with that socket 
     (pop the sent bytes out of the FIFO when you're done with them) 

В этой конструкции, когда ваше приложение породившее новые данные, которые он хочет отправить на сокете, он не должен call send() напрямую; вместо этого он должен добавить эти данные в конец буфера отправки FIFO, который вы связали с этим сокетом, и вышеуказанный цикл событий позволит отправлять данные как можно скорее (после любой отправки любых данных, которые уже присутствовали в FIFO , конечно), , не блокируя цикл событий от выполнения каких-либо других обязанностей, он может иметь.

В худшем случае (очень медленное TCP-соединение, на которое вы хотите отправить много данных), FIFO может стать большим (с использованием дополнительной памяти), но он никогда не будет «тупиком».

Смежные вопросы