2017-01-23 7 views
2

Я начал использовать ZeroMQ для IPC и сделал простой echo-client/server, и я удивлен одной штукой. Вот код C++ (с использованием zmq.hpp и zmq_addon.hpp).Как ZeroMQ REQ/REP обрабатывает несколько клиентов?

Сервер:

zmq::context_t context(1); 
zmq::socket_t socket(context, ZMQ_REP); 
socket.bind("ipc:///tmp/machine-1"); 
while (1) { 
    zmq::multipart_t m; 
    m.recv(socket); 
    int i = m.poptyp<int>(); 
    i++; 
    m.addtyp<int>(i); 
    m.send(socket); 
} 

Клиент:

zmq::context_t context(1); 
zmq::socket_t socket(context, ZMQ_REQ); 

socket.connect("ipc:///tmp/machine-1"); 

int i = 0; 
while (1) { 
    int save = i; 
    zmq::multipart_t m; 
    m.addtyp<int>(i); 
    m.send(socket); 
    m.recv(socket); 

    i = m.poptyp<int>(); 

    if (i != (save + 1)) 
     break; 

    if ((i % 100000) == 0) 
     std::cerr << "i : " << i<< "\n"; 
} 

я работает, как ожидалось. Клиент отправляет int, сервер делает плюс один и отправляет его обратно.

Теперь волшебство я не понимаю: я понял, что я могу запускать клиент несколько раз параллельно, и он продолжает работать, для каждого клиента правильно.

Проверка save+1 на i всегда в порядке.

Как ZMQ обрабатывает проблему параллелизма на стороне сервера? Как он узнает, к какому клиенту следует отправить ответ?

Существует этот вопрос на SO, но это не ответ на мой вопрос: ZeroMQ REQ/REP on ipc:// and concurrency

ответ

4

согласно ZeroMQ документы, когда вы звоните REP.recv() на сервере он будет возвращать сообщение от REQ в очереди (клиент). Если подключено несколько клиентов, он будет использовать политику справедливой очереди для ее выбора. Когда вы вызываете REP.send() для ответа, сокет REP всегда отправляет ответ соответствующему клиенту REQ.

Это «магия» - сокет REP заботится о том, чтобы отправить ответ правильному клиенту. Если клиент отключился, он просто отбрасывает ответное сообщение.

docs может быть яснее, чем мое объяснение:

ZMQ_REP: Разъем типа ZMQ_REP используется службой для получения просьбы и посылать ответы на клиента. Этот тип сокета позволяет использовать только чередующуюся последовательность zmq_recv (запрос) и последующие вызовы zzmq_send (ответ). Каждый полученный запрос имеет справедливую очередь из числа всех клиентов, и каждый отправленный ответ направляется клиенту, который выдал последний запрос. Если исходный запросчик больше не существует , ответ молча отбрасывается.

+0

Я только читаю руководство, вы правы в документации 'zmq_connect', это хорошо описано. Ключ в том, что для ZMQ_REQ/ZMQ_RES-соединения также требуется определенный recv/send-pattern. Таким образом, они могут заблокировать доступ к сокету при вызове recv и освободить его при вызове send (наоборот, для другой стороны) –

1

Ответ на вопрос (и не очень полезный): это работает, потому что они написали его таким образом.

Более длинный ответ: что сделала команда ZMQ, реализует свой собственный протокол передачи сообщений (zmtp) поверх поточных соединений (ipc-каналы, сокеты и т. Д.). Помимо передачи и демаркации сообщений, они добавили функции в этот протокол специально для поддержки различных шаблонов, таких как REQ/REP, PUB/SUB, честной очереди и т. Д. Чтобы заставить его работать, существует поток библиотек zmq, который обрабатывает все действия zmtp в фоновом режиме, и вы взаимодействуете с этим потоком через вызовы zmq_send, zmq_poll и т. д. Использование zmtp означает, что программа на другом конце сокета также должна говорить zmtp; ничего полезного не происходит, если один конец использует libzmq, а другой просто открывает исходный сокет для себя.

Это действительно полезная часть кода.

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

Полагаю, что вы даже можете связать сокет zmq с двумя разными транспортными средствами, например, как ipc, так и tcp. Это супер полезно!