Оглавление

  • Что такое ZMQ?
  • Установка ZMQ
  • Использование основных методов для обмена данными: REP / REQ и PUSH / PULL

Изучение Python - отличное начало для открытия чудесного мира программирования. Как выразился наш дорогой Харрисон Кинсли:

Программирование - это суперсила.

Верно ! Python интересен не только для новичков, но и для довольно продвинутых приложений, таких как веб-разработка, робототехника, научный анализ и так далее. Великолепие Python в его библиотеках. Вам нужна функциональность, просто установите ее, импортируйте, и все готово.

Сегодня мы рассмотрим ZeroMQ или ZMQ, что делает эта библиотека, как ее реализовать.

Если у вас есть какие-либо вопросы или комментарии, вы можете написать мне в любое время.

Что такое ZMQ?

Цитата с их собственного сайта (zeromq.org):

ZeroMQ выглядит как встраиваемая сетевая библиотека, но действует как среда параллелизма. Он предоставляет вам сокеты, которые переносят атомарные сообщения через различные транспорты, такие как внутрипроцессный, межпроцессный, TCP и многоадресный.

Другими словами, это библиотека обмена сообщениями, которая позволяет нескольким процессам взаимодействовать друг с другом, будь то в локальной сети, но также и через Интернет. Он довольно гибкий и легкий, а также не зависит от платформы. Это означает, что вы можете писать код на многих разных языках, и разные системы будут взаимодействовать друг с другом в разных операционных системах.

Вот фрагмент совместимых языков: C ++ | C # | Clojure | CL | Delphi | Эрланг | F # | Феликс | Вперед | Haskell | Haxe | Java | Lua | Node.js | Objective-C | Perl | PHP | Python | Q | Ракетка | Рубин | Scala | Tcl | Ада | Базовый | ooc. Здесь мы, конечно, сосредоточимся только на языке Python.

Установка ZMQ и импорт

ZMQ прост в установке, вам нужно только открыть свой терминал и ввести

pip install pyzmq

Чтобы начать использовать его, откройте новый скрипт Python и импортируйте ZMQ. Затем необходимо объявить контекст, который позволит вам создать соответствующий объект для дальнейшего использования.

import zmq
context = zmq.Context()

Теперь, когда контекст объявлен, мы можем увидеть, какой метод мы хотим использовать. Действительно, первое, что нужно понять при реализации ZMQ, - это какой тип коммуникации ему нужен. Я не говорю здесь о протоколе, а о том, как два или более процесса могут взаимодействовать друг с другом. В следующем разделе мы рассмотрим только

  • REP / REQ
  • ТЯНИ-ТОЛКАЙ

Существует еще несколько, но, насколько я согласен, это самый минимум, который поможет вам начать использовать ZMQ.

REP / REQ

REP / REQ - простейшее общение.

Просите, и вы получите

Здесь у вас есть два процесса: сервер и клиент. Клиент запрашивает пакет на сервере, и последний отвечает. Теперь давайте посмотрим на код. У нас будет два файла Python: client.py и server.py.

#client.py
import zmq
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect(“tcp://localhost:5555”)
for request in range(10):
 
    print(“Sending request {} …”.format(request))
    socket.send(b”Hello”)
    message = socket.recv()
    print(“Received reply {} [ {} ]”. format(request, message))
socket.close()
context.term()
#server.py
import time
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind(“tcp://*:5555”)
while True:
    message = socket.recv() # Wait for next request from client
    print(“Received request: {}”.format(message))
    # Do some ‘work’
    time.sleep(1)
    message = b”World”
    socket.send(message) # Send reply back to client

Хорошо, давайте по очереди разберемся, что происходит. В обоих файлах объявляется контекст, а затем создается объект сокета с соответствующим типом ZMQ (REQ или REP).

context = zmq.Context()
socket = context.socket(zmq.REP) #server.py
socket = context.socket(zmq.REQ) #client.py

Затем скрипты связываются с TCP-адресом, в нашем случае он использует localhost. В этом разница между двумя сценариями. Сервер привязывается к порту 5555, а клиент подключается к этому порту. Порт можно изменить на то, что вы хотите, только будьте осторожны, чтобы не использовать стандартные порты, такие как 80 или 22 и т. Д., Поскольку другие протоколы используют их по умолчанию. Различия между коннектами и связыванием выходят за рамки этой статьи, для получения более подробной информации просмотрите исчерпывающее руководство по zeroMQ (zguide.zeromq.org).

Теперь, когда сокеты подключены к сети, мы можем общаться. В методе REQ / REP клиенту сначала нужно запросить или запросить пакет. Итак, в цикле for он отправляет «привет». Вы заметите букву «b» перед строкой, потому что ZMQ передает только байты. Позже мы увидим, как кодировать переменные с помощью json для отправки практически всего.

socket.send(b”Hello”) #client.py

На стороне сервера мы входим в цикл while, который ожидает сообщения запроса от клиента.

message = socket.recv() #server.py

Важно отметить, что эта функция «блокирует», что означает, что скрипт python будет ждать в этой строке, пока не получит сообщение. Другими словами, вы не можете ожидать выполнения кода без некоторых уловок, таких как многопоточность или многопроцессорность.

Как только сообщение получено на стороне сервера, мы можем работать с ним, time.sleep (1) иллюстрирует это, хотя он может запрашивать базу данных для получения пользовательских данных или даже общаться с другим zmq socket и т. д. По завершении мы отправляем ответ клиенту с переменной message, который принимается на стороне клиента и печатается.

После завершения мы закрываем сокет и завершаем контекст. Это полезно для предотвращения конфликтов, которые могут возникнуть, когда эти линии закрывают порт.

Запуск кода дает

Как видите, клиент печатает запрос, сервер читает запрос и печатает сообщение, а затем вы можете видеть, что клиент читает ответ от сервера в квадратных скобках.

И вуаля ! Наш первый код zmq. Скорость обмена данными довольно высока и допускает некоторую параллельную обработку, если задуматься. Оба сценария могут заниматься своими делами, пока им не понадобится общаться. В этом примере нет тайм-аутов, поэтому клиент может отправить сообщение, которое автоматически буферизуется библиотекой ZMQ, и когда сервер переходит на строку recv, оно обрабатывается. Буфер даже позволяет запускать клиентский сценарий перед серверным сценарием. Вам не нужно запускать каждый процесс в заданном порядке.

Однако, как я уже упоминал, это метод запроса и ответа, то есть, если сервер отправляет сообщение без запроса или если клиент получает сообщение, не спрашивая, все идет не так!

Принцип работы Python гласит, что это

Проще просить прощения, чем разрешения

Таким образом, вы можете легко встроить функции send и recv в try и except. Это может привести к потере пакета, но это решение. Хотя я бы порекомендовал исправить проблему должным образом, если она возникнет.

НАЖАТЬ / ТЯНУТЬ

Теперь представьте, что у вас есть несколько клиентов, которые все хотят отправлять данные на сервер. Например, у вас есть несколько датчиков, которые хотят передавать данные на сервер. На самом деле вы не хотите устанавливать связь REQ / REP между всеми процессами, поскольку это было бы громоздко и потребовало бы изменения сервера каждый раз, когда вы хотите добавить новый ввод. В этом случае вы можете использовать метод PUSH / PULL, при котором клиенты будут отправлять сообщение серверу, а последний будет искать или извлекать сообщения, когда это возможно.

Чтобы проиллюстрировать это, мы сначала начнем с двух клиентов, отправляющих сообщения на сервер, и тем временем представим новые функции, хотя многие клиенты могут быть автоматически сгенерированы, например, с использованием класса.

Давайте посмотрим на код. Сначала мы определяем двух клиентов.

#client1.py
import zmq
import time
import json
context = zmq.Context()
socket = context.socket(zmq.PUSH)
socket.connect(“tcp://localhost:5555”)
for request in range(5):
    print(“Sending push message: {}”.format(request))
    time.sleep(1)
    message = json.dumps(request)
    message = message.encode()
    socket.send_multipart([b”client1", message])
socket.close()
context.term()
#client2.py
import zmq
import time
import json
context = zmq.Context()
socket = context.socket(zmq.PUSH)
socket.connect(“tcp://localhost:5555”)
for request in range(5):
    print(“Sending push message: {}”.format(request))
    time.sleep(3)
    message = json.dumps(request)
    message = message.encode()
    socket.send_multipart([b”client2", message])
socket.close()
context.term()

Единственное различие между двумя клиентами - это time.sleep (), первый клиент ждет 1 с, а второй клиент ждет 3 с. Тем не менее, в этих отрывках я ввел две важные концепции. Использование json и составных сообщений. Как упоминалось ранее, ZMQ отправляет только байты, а не строки, или список и т. Д. Для того, чтобы отправить список, например, вы должны сериализовать данные. Это делается путем выгрузки данных в json и последующего кодирования сообщения:

message = json.dumps(request)
message = message.encode()

Вторая новинка - использование multipart. При отправке сообщений через ZMQ можно выбрать множество типов. Простой send (), который мы видели ранее, отправляет только байт, но можно отправлять строки и даже объекты (хотя с разными результатами). Насколько мне известно, я использую только send_multipart (). Это позволяет мне стандартизировать способ взаимодействия моих скриптов и всегда знать происхождение сообщения. send_multipart () принимает список в качестве аргумента, где первый элемент - это идентификатор клиента, а второй - закодированное сообщение. Так не должно быть, но я так делаю.

Теперь посмотрим на серверную часть.

import zmq
context = zmq.Context()
socket = context.socket(zmq.PULL)
socket.bind(“tcp://*:5555”)
poller = zmq.Poller()
poller.register(socket, zmq.POLLIN)
while True:
    socks = dict(poller.poll())
    if socket in socks:
         message = socket.recv_multipart()
         print(message)

Опять же, мы объявляем сокет, теперь определенный как zmq.PULL, но затем у нас есть объект poller. Это используется для опроса сокетов, с которыми мы можем взаимодействовать в основном цикле. Кстати, в цикле while мы определяем socks как словарь poller. Затем мы смотрим, находится ли сокет в этом словаре, если True, мы разрешаем recv_multipart () и все. Полученное сообщение представляет собой список, в котором первое значение представляет собой идентификатор клиента, а второе - само сообщение, которое можно легко декодировать с помощью json.loads (message).

Запустим код. Я открыл три терминала и сначала запустил server.py, поскольку он может ждать, а затем быстро запустил client1.py и client2.py в двух терминалах.

Как видите, оба клиента отправили свои составные сообщения, включающие их id и message. Они прибыли на сервер в разное время, и каждое сообщение отображалось по мере их поступления.

Заключительные слова

Есть много приложений для zmq, но я думаю, что это действительно просто реализовать, и в конечном итоге я часто использую его в любое время, когда мне нужно разделить переменные между скриптами. В будущей статье я покажу часть своего кода и то, как я использую ZMQ для роботизированных приложений.