Zeromq что это
Перейти к содержимому

Zeromq что это

  • автор:

«ZeroMQ».Глава 1: Приступая к работе

Всем привет!
Хочу начать вольный перевод книги «ZeroMQ.Use ZeroMQ and learn how to apply different message patterns». Уверен, что многие захотят разобраться с этой интересной библиотекой.

  • Глава 1: Приступая к работе
  • Глава 2: Знакомство с сокетами
  • Глава 3: Использование топологии сокетов
  • Глава 4: Дополнительные паттерны
  • Обзор того, что представляет собой очередь сообщений
  • Зачем использовать ZeroMQ и что отличает ее от других технологий работы с очередями сообщений
  • Основы клиент/серверной архитектуры
  • Рассмотрим первый паттерн: запрос-ответ
  • Как мы можем обрабатывать строки в C
  • Проверка установленных версий ZeroMQ
Начало

Люди на протяжении всей своей жизни социально взаимодействуют между собой. Программы ведут себя подобным образом. Программа должна общаться с другой программой, так как мы живем в мире интернета. Мы располагаем UDP, TCP, HTTP, IPX, WebSocket и другими протоколами для соединения приложений.
Тем не менее, подходы такого низкого уровня усложняют нам жизнь, нужно что-то проще и быстрее. Абстракции высокого уровня жертвуют скоростью и гибкостью, в то время как иметь дело непосредственно с низкоуровневыми деталями довольно не просто. ZeroMQ показывает, какой есть выход, давая нам удобство использования высоко-уровневых методов со скоростью низко-уровневого подхода.
Прежде чем начать разбираться с ZeroMQ, давайте сначала посмотрим на общую концепцию очереди сообщений.

Очередь сообщений

Очередь сообщений, или, технически, FIFO(First In First Out), является одной из основных и хорошо изученных структур данных. Существуют различные реализации очереди, такие как очередь приоритетов или двусторонняя очередь, которые имеют различные свойства, но общая идея в том, что данные добавляются в очередь когда они появляются или вызывающий абонент будет готов.
Вместе с тем очередь сообщений предоставляет гарантии, что сообщение будет доставлено независимо от того, что происходит. Очередь сообщений позволяет асинхронно взаимодействовать между слабо связанными компонентами, а также обеспечивает строгую последовательность очереди. В случае недостатка ресурсов, что мешает вам сразу же обработать посылаемые данные, вы можете поставить их в очередь сообщений на сервере, который будет хранить данные до тех пор, пока клиент не будет готов.

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

Мы также можем внедрить в эту систему потоки. В этом случае, процесс обработки каждой задачи выполнялся бы параллельно.

В много-поточной модели потоки управляются самой операционной системой на одном процессоре или нескольких процессорах/ядрах.
Асинхронный ввод/вывод (AIO) позволяет программе продолжить выполнение при обработке I/O запросов. AIO является обязательным в приложениях реального времени. С помощью AIO мы можем обработать несколько задач в одном потоке.

  • Если вы используете одно-поточную очередь, то в случае большого числа пользователей программа будет не в состоянии обработать данные для всех URL-адресов
  • Если вы используете для очереди много-поточный подход, то ваше приложение может быть подвержено DDoS-атаке
  • Вы потеряете данные для всех URL-адресов в случае аппаратного сбоя
Введение в ZeroMQ

До этого момента мы рассмотрели, что такое очередь сообщений, что привело нас к цели данной книги, то есть к ZeroMQ. Обществом ZeroMQ определено как «сокеты на стероидах». Формально ZeroMQ определятся как библиотека сообщений, которая помогает разработчикам создавать распределенные и параллельные приложения.
Первое, что мы должны узнать о ZeroMQ это то, что она не является традиционной системой очередей сообщений, таких как ActiveMQ, WebSphereMQ, или RabbitMQ. ZeroMQ отличается. Она дает нам инструменты для создания собственной системы очередей сообщений. Это библиотека.
Она работает на различных архитектурах от ARM до Itanium и поддерживается более чем в 20 языках программирования.

Простота

ZeroMQ простая. Мы можем делать некоторые асинхронные операции ввода/вывода, также ZeroMQ может ставить в очередь сообщений сообщения из потока ввода/вывода. Потоки ввода/вывода в ZeroMQ работают асинхронно при общении с сетевым трафиком, поэтому они могут делать для вас и остальную работу. Если вы прежде работали с сокетами, то вы должны знать, что это весьма не просто. Тем не менее, ZeroMQ позволяет гораздо упростить работу с ними.

Производительность

ZeroMQ быстрая. Веб-сайту Second Life удалось получить 13,4 микросекунды непрерывного времени ожидания и до 4 100 000 сообщений в секунду. ZeroMQ может использовать многоадресный транспортный протокол, который является эффективным методом для передачи данных в различных направлениях.

Hello world

Итак, пришло время начать писать код, после того как мы разобрали что такое очередь сообщений и что из себя представляет ZeroMQ. Ну и конечно же, мы начнем со знаменитой программы «Hello World».
Давайте рассмотрим ситуацию, когда у нас есть сервер и клиент. Сервер отвечает world всякий раз, когда он получит сообщение hello от клиента. Сервер работает на порте 4040, ну а клиент, соответственно, отправляет сообщения на этот же порт.
Ниже приведен код сервера, который посылает сообщение world клиенту:

#include #include #include #include "zmq.h" int main (int argc, char const *argv[]) < void* context = zmq_ctx_new(); void* respond = zmq_socket(context, ZMQ_REP); zmq_bind(respond, "tcp://*:4040"); printf("Starting…\n"); for(;;) < zmq_msg_t request; zmq_msg_init(&request); zmq_msg_recv(&request, respond, 0); printf("Received: hello\n"); zmq_msg_close(&request); sleep(1); // sleep one second zmq_msg_t reply; zmq_msg_init_size(&reply, strlen("world")); memcpy(zmq_msg_data(&reply), "world", 5); zmq_msg_send(&reply, respond, 0); zmq_msg_close(&reply); >zmq_close(respond); zmq_ctx_destroy(context); return 0; > 

Ниже приведен код клиента, который посылает сообщение hello на сервер:

#include #include #include #include "zmq.h" int main (int argc, char const *argv[]) < void* context = zmq_ctx_new(); printf("Client Starting….\n"); void* request = zmq_socket(context, ZMQ_REQ); zmq_connect(request, "tcp://localhost:4040"); int count = 0; for(;;) < zmq_msg_t req; zmq_msg_init_size(&req, strlen("hello")); memcpy(zmq_msg_data(&req), "hello", 5); printf("Sending: hello - %d\n", count); zmq_msg_send(&req, request, 0); zmq_msg_close(&req); zmq_msg_t reply; zmq_msg_init(&reply); zmq_msg_recv(&reply, request, 0); printf("Received: hello - %d\n", count); zmq_msg_close(&reply); count++; >// We never get here though. zmq_close(request); zmq_ctx_destroy(context); return 0; > 

У нас есть первая основная архитектура: запрос-ответ, как показано на следующей диаграмме:

Давайте более подробно разберемся с кодом, чтобы понять, как он работает.
Сначала мы создаем контекст и сокет. Метод zmq_ctx_new() создает новый контекст. Он является потоко-безопасным, так что контекст можно использовать в нескольких потоках.
zmq_socket(2) создает новый сокет в определенном контексте. Сокеты ZeroMQ не являются потоко-безопасными, поэтому они должны использоваться только в том потоке, в котором они были созданы. Традиционные сокеты синхронные, в то время как у сокетов ZeroMQ есть возможность создавать одну очередь на стороне клиента, а другую на стороне сервера, асинхронно управляя паттерном запрос-ответ. ZeroMQ автоматически организует настройку соединения, повторное подключение, отключение и доставку контента. В 3 главе мы рассмотрим более детально разницу между традиционными сокетами и сокетами ZeroMQ.
Сервер связывает сокет ZMQ_REP и 4040-ой порт и начинает ожидать запросы и отвечает каждый раз, когда получает сообщение.
Эта простая программа «hello world» показывает нам пример использования первого паттерна запрос-ответ.

Паттерн запрос-ответ

Мы используем шаблон (паттерн) запрос-ответ для отправки сообщения от клиента одному или нескольким серверам и получаем ответ на каждое отправленное сообщение. Скорее всего, это самый простой способ использования ZeroMQ. Запросы на ответы должны быть строго по порядку.

Ответ

Ниже приводится ответная часть паттерна запрос-ответ:

void* context = zmq_ctx_new(); void* respond = zmq_socket(context, ZMQ_REP); zmq_bind(respond, "tcp://*:4040"); 

Сервер использует сокет ZMQ_REP для получения сообщений из Интернета и отправки ответов клиентам. Для маршрутизации входящих сообщений от ZMQ_REP справедлива стратегия fair-queue , а для исходящих — last-peer .

Стратегия fair-queue

Эта книга полностью посвящен очередям. Вы можете удивиться, когда узнаете, что мы имеем ввиду, когда говорим о стратегии fair-queue . Это алгоритм планирования и выделения ресурсов справедливый по определению.

Чтобы понять как она работает, скажем, что потоки на предыдущем рисунке отправляют 16, 2, 6 и 8 пакетов в секунду соответственно, но на выходе может обрабатываться только 12 пакетов в секунду. В этом случае мы могли бы передавать 4 пакета в секунду, но Поток 2 передает только 2 пакета в секунду. Правила справедливой очереди (fair-queue) в том, что не может быть свободных выходов, если только все выходы свободны одновременно. Таким образом, можно позволить Потоку 2 передать свои 2 пакета в секунду и разделить оставшиеся 10 между остальными потоками.
Эта стратегия маршрутизации входящих сообщений использует ZMQ_REP . Циклическое планирование является самым простым способом реализации стратегии справедливой очереди, которая также используется в ZeroMQ.

Запрос

Ниже приведена запросная часть паттерна запрос-ответ:

void* context = zmq_ctx_new(); printf("Client Starting….\n"); void* request = zmq_socket(context, ZMQ_REQ); zmq_connect(request, "tcp://localhost:4040"); 

Клиент использует ZMQ_REQ для отправки сообщений и получения ответов от сервера. Все сообщения отправляются по стратегии циклической ( round-robin ) маршрутизации. Стратегия маршрутизации входящих сообщений это last-peer .
ZMQ_REQ не выбрасывает никаких сообщений. Если нет никаких доступных служб для отправки сообщения или все службы заняты, то все посылается операции zmq_send(3) , которая будет заблокирована до тех пор, пока один из серверов не станет доступным для отправки сообщения. ZMQ_REQ совместим с типами ZMQ_REP и ZMQ_ROUTER . В 4 главе мы рассмотрим ZMQ_ROUTER .

Отправка сообщения

Эта часть совмещает разделы запрос и ответ и показывает, как происходит ответ на чей-либо запрос и как на него ответить.

printf("Sending: hello - %d\n", count); zmq_msg_send(&req, request, 0); zmq_msg_close(&req); 

Клиент посылает сообщение на сервер с помощью zmq_msg_send(3) . Это очередное сообщение и отправляет его в сокет.

int zmq_send_msg(zmq_msg_t *msg, void *socket, int flags) 
  • Параметр сообщения обнуляется во время запроса, так что если вы хотите отправить сообщение нескольким сокетам, то его необходимо скопировать.
  • Успешный zmq_msg_send() запрос не указывает, если сообщение было отправлено по сети.
  • Параметр флаг может быть либо ZMQ_DONTWAIT либо ZMQ_SNDMORE. ZMQ_DONTWAIT указывает на то, что сообщение должно быть отправлено асинхронно. ZMQ_SNDMORE указывает, что сообщение является составным и остальная часть сообщения находится в пути.
zmq_msg_recv(&reply, request, 0); printf("Received: hello - %d\n", count); zmq_msg_close(&reply); 

zmq_msg_recv(3) получает часть сообщения из сокета, как указано в параметре socket и сохраняет ответ в параметр message .

int zmq_msg_recv (zmq_msg_t *msg, void *socket, int flags) 
  • Ранее полученные сообщения (если таковые имеются) признаются недействительными
  • Параметром флага может быть ZMQ_DONTWAIT , который указывает, что операция должна быть выполнена асинхронно
Работа со строками в C

Каждый язык программирования имеет свой подход для обработки строк. В Erlang вообще нет строк как таковых (там они представлены в виде списков символов). В языке программирования C, строки с нулем в конце. Строки в C, в основном, это символьные массивы, где ‘\0’ означает конец строки. Ошибки работы со строками являются общими и результатом многих уязвимостей.
По словам Миллера и др.(1995), 65 процентов уязвимостей в Unix объясняются ошибками при работе со строками, такими как нулевой байт или переполнение буфера, поэтому обработка строк в C должна быть сделана аккуратно.
При использовании ZeroMQ, это ложится на ваши плечи, чтобы сообщение было надежно отформатировано, так, чтобы другие приложения смогли его прочитать. ZeroMQ знает только размер сообщения и только.
Использование различных языков программирования в одном приложении это обычно дело. Если приложения, написанные на языке программирования, который не добавляет нулевой байт в конце строки, должны, так или иначе, общаться с приложениями, написанными на C, то вы получите странные результаты.
Вы можете отправить сообщение, такое как world, как в нашем примере с нулевым байтом, а именно:

zmq_msg_init_data_(&request, "world", 6, NULL, NULL); 

А вот в Erlang вы бы отправили то же самое сообщение следующим образом:

erlzmq:send(Request, >) 

Предположим, что наш клиент, написанный на C, подключается к ZeroMQ –службе, написанной на Erlang и мы отправляем сообщение world этой службе. В этом случае Erlang будет выводить world . Если мы посылаем сообщение с нулевым байтом, то Erlang выведет следующее [119,111,114,108,100,0] . Вместо желаемой строки, мы получили список, содержащий некоторые цифры, это ASCII-коды. Тем не менее, это больше не интерпретируется как строка.
Строки в ZeroMQ фиксируются в длину и отправляются без нулевого байта. Таким образом, ZeroMQ строка передается как несколько байт (сама строка в этом примере), а также длина.

Проверка версии ZeroMQ

Весьма полезно знать, какую версию ZeroMQ вы используете. В некоторых случаях, чтобы избежать нежелательных сюрпризов, необходимо знать точную версию. Например, есть некоторые различия между ZeroMQ 2.x и ZeroMQ 3.x, такие как использование устаревших методов; поэтому, если вы знаете точную версию ZeroMQ, которая установлена на вашем компьютере, то вы можете избежать использования устаревших методов.

#include #include "zmq.h" int main (int argc, char const *argv[])
Резюме

В этой главе мы посмотрели что такое очередь сообщений, а также сделали небольшое введение в ZeroMQ. Рассмотрели как ZeroMQ обрабатывает строки и первый из паттернов (запрос-ответ). А также написали первое приложение “hello world”.

Ресурсы к данной статье вы можете скачать по ссылке

Всем большое спасибо за внимание!

тут блог

тут блог

Общественные обязательства интроверта.
Сообщения на ИТ тематику, но не обязательно.

О ZeroMQ

Ну вот я и добрался до мимимишной ØMQ.

Сразу отмазываюсь. Хороший наезд на ØMQ на CodeFest 2014 был вполне справедлив. ØMQ — это лишь транспорт, она не способна решить вопросы вашей сервисно-ориентированной архитектуры в одиночку. Мартин Сустрик, один из первоначальных авторов ØMQ, действительно форкнул её, и не один раз. Но мы все же взялись использовать эту чудесную библиотеку, ибо она хороша, и нам подходит.

ØMQ — это сокеты на стероидах. ØMQ — это очередь сообщений без очереди сообщений (ну ладно, с очередями в памяти, но точно без брокера). Позволю себе перевести «Сотню слов о ØMQ» из официального руководства.

ØMQ (также известная как ZeroMQ, 0MQ или zmq) выглядит как встраиваемая сетевая библиотека, но работает как фреймворк для многопоточного программирования. Она дает вам сокеты, которые атомарно передают сообщения через различные транспорты внутри процесса, между процессами, по TCP или мультикастом. Вы можете соединять N к N сокетов согласно таким паттернам как распределение сообщений, публикация и подписка, распределение задач, запрос-ответ. Она достаточно быстра, чтобы строить на ней кластерные продукты. Её асинхронная модель ввода-вывода дает вам масштабируемые многоядерные приложения, построенные из асинхронных задач, обрабатывающих сообщения. Она имеет API на многих языках и работает на большинстве операционных систем. ØMQ вышла из iMatix и она открыта под LGPLv3.

Ну и невозможно не перевести «Как оно начиналось» из того же руководства. (Это руководство полно подобного юмора, замечательное чтиво).

Мы взяли обычные TCP сокеты, примешали радиоактивные изотопы, украденные из секретного советского атомного исследовательского проекта, обработали космическими лучами из 1950-х годов, дали в руки обкуренному автору комиксов, откровенно дико помешанному покрывать вздутые мускулы спандексом. Да, ØMQ сокеты — это мироспасательные супергерои мира сетей.

BOOM!

Меня пока не сильно интересует асинхронная многопоточная модель ØMQ. Хотя обещания строить сложные системы из компонент, обменивающихся сообщениями, сначала из потоков одного процесса, потом разных процессов, потом процессов на разных узлах, объединенных сетью, я где-то уже слышал. Кажется, в Erlang. Может, это и есть главный недостаток ØMQ? В смешении сетевых вещей и работы с потоками.

Ну так чем отличается ØMQ сокет от TCP сокета?

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

Client-server

ØMQ сама заботится о TCP подключении и переподключении. Даже можно «подключиться» и начать посылать сообщения, когда TCP связности между узлами еще нет. Тут у нас возникают таки очереди в памяти. Впрочем, ØMQ предпочитает в случаях невозможности доставки сообщений (или переполнения очереди) молча уничтожать эти самые сообщения. Об этом нужно помнить. И, в общем, тут есть своя логика.

Кстати, сообщения. Если TCP «доставляет» потоки, то ØMQ доставляет сообщения. Это особенность и ограничение любой очереди сообщений. Сообщения доставляются атомарно. Это значит, что если на принимающей стороне API сообщило вам, что пришло сообщение, то это сообщение уже полностью присутствует в памяти этой самой принимающей стороны. А если в процессе передачи сообщения произошли какие-то проблемы, то принимающая сторона никогда об этом не узнает. Сообщение просто не дойдет. Гарантированной доставки нет. О гарантированной доставке нужно заботиться самостоятельно с помощью нумерации сообщений, дополнительных запросов, таймаутов и т.д и т.п.

ØMQ сообщения — просто наборы байт. Но ØMQ сообщения могут еще делиться на фрагменты. Фрагмент — это часть сообщения, как её выделила отправляющая сторона. Фрагменты — это просто удобный способ разбиения сообщения на части. В HTTP для разделения заголовков от тела запроса используется пустая строка. В ØMQ для подобного разделения не нужно изобретать специальных разделителей, достаточно использовать фрагменты. Внутри себя ØMQ использует фрагменты для добавления адреса отправителя к сообщению в ROUTER сокетах. Также на фрагментах можно строить протоколы.

Frames

Почему собственно «нулевая очередь сообщений»? Потому что «обычные» очереди сообщений (на том же «проклятом» создателями AMQP) требуют выделенного брокера. Это такой компонент, который отвечает за хранение очередей и передачу сообщений. Который, для пущей надежности, еще и хранит все сообщения. Который имеет свои собственные проблемы с масштабируемостью. Который еще надо развернуть и сопровождать.

Dealer

Если в том же AMQP есть и сообщения, и очереди, и точки обмена, все как отдельные явные сущности, то в ØMQ есть только сообщения и сокеты. Сокеты бывают разных типов и эти типы должны использоваться совместно, чтобы получились определенные паттерны передачи сообщений.

Простейший и тупейший вариант — пара сокетов REQ (request) и REP (reply). Клиент посылает сообщение через REQ сокет и блокируется в ожидании ответа. Сервер блокируется в ожидании поступлении сообщения через REP сокет, получает сообщение и отсылает ответ через тот же сокет. Как уже упоминалось ранее, слушания и коннекты ортогональны к направлению этого диалога. А тупейший это вариант, потому что это единственный синхронный паттерн в ØMQ. Блокировка, завязанная на порядок поступления сообщений, может в грустных случаях длиться вечно.

REQ-REP

Сокеты PUSH и PULL предназначены для параллельного пропихивания сообщений. Можно из одного PUSH слать сообщения нескольким PULL (поочередно, round-robin). А можно из нескольких PUSH собирать сообщения в один PULL.

PUSH-PULL

Если нужно слать сообщения одновременно нескольким получателям нужны PUB (publish) и SUB (subscribe) сокеты. На SUB стороне нужно обязательно подписаться на интересующие сообщения. Подписаться можно на префикс сообщения. Т.е. указать некую последовательность байт, и на этот сокет будут приходить только сообщения, начинающиеся с этой последовательности байт. Можно подписаться на несколько префиксов. В частном случае можно указать пустой префикс и получать все сообщения. Ну а в PUB сообщения просто посылаются. И если нет ни одного подключенного и подписанного получателя, сообщение просто уничтожается. Но зато по сети пересылаются только те сообщения, в которых заинтересован данный подписчик.

PUB-SUB

Пара сокетов DEALER и ROUTER — это такой асинхронный вариант REQ и REP. Без тупых блокировок. Еще их можно соединять один ко многим. А ROUTER еще и может отсылать ответы конкретному получателю. Когда мы читаем сообщение из ROUTER сокета, к началу сообщения добавляется еще один фрейм, содержащий идентификатор (identity) отправителя сообщения. Этот идентификатор либо назначается ØMQ самостоятельно (но будет уникальным для каждого подключившегося к данному ROUTER сокету), либо отправитель (REQ или DEALER) может установить его явно. Идентификатор — обычный фрейм, т.е. произвольная последовательность байт.

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

На роутере и дилере можно построить брокер, аналогичный выделенному брокеру «классических» очередей сообщений. Даже, при желании, нарисовать и сохранение сообщений.

Broker

Вот на роутерах и дилерах мы и построили одну маленькую подсистему. Есть несколько роутеров, которые перенаправляют сообщения. Типа масштабируемое ядро маршрутизации сообщений. Есть различные компоненты, которые одновременно подключаются ко всем этим роутерам. Эти компоненты выполняют определенные роли (о которых знают роутеры) и устанавливают явный идентификатор (identity), одинаковый для всех подключений к роутерам. Ну а роутеры, зная все идентификаторы и роли, и руководствуясь некоторыми правилами, маршрутизируют сообщения.

PlantUML diagram

Получилось вполне удобно и надежно. Еще раз, у нас уже были компоненты и выделены роли. Нам не хватало удобного транспорта. У REST слишком много накладных расходов, и через него плохо передавать уведомления. C голым TCP слишком много морок при асинхронном обмене, да и адресная доставка и переподключения требуют много ручной работы. В общем, пока ØMQ в роли универсального и мощного транспорта мне нравится.

ZeroMQ

ZeroMQ (также обозначаемый как ØMQ , 0MQ или ZMQ ) — это библиотека асинхронного обмена сообщениями , предназначенная для использования в распределенных или параллельных приложениях. Он предоставляет очередь сообщений , но в отличие от промежуточного программного обеспечения , ориентированного на сообщения , система ZeroMQ может работать без специального брокера сообщений . API библиотеки спроектирован так, чтобы напоминать сокеты Беркли .

4.3.4 [1] / 17 января 2021 г . ; 3 месяца назад ( 17 января 2021 г. )

ZeroMQ разработан большим сообществом участников, основанным iMatix, которому принадлежат доменное имя и товарные знаки. Для многих популярных языков программирования существуют сторонние привязки.

Технология

ZeroMQ API предоставляет сокеты (своего рода обобщение традиционных IP и сокетов домена Unix ), каждый из которых может представлять соединение « многие ко многим» между конечными точками . Работая с детализацией по сообщениям, они требуют использования шаблона обмена сообщениями и особенно оптимизированы для этого типа шаблона.

Основные шаблоны ZeroMQ:

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

Каждый шаблон определяет определенную топологию сети. Запрос-ответ определяет так называемую «служебную шину», публикация-подписка определяет «дерево распределения данных», а push-pull определяет «параллельный конвейер». Все шаблоны намеренно разработаны таким образом, чтобы их можно было бесконечно масштабировать и, таким образом, можно было использовать в масштабе Интернета. [2]

Любое сообщение через сокет обрабатывается как непрозрачный двоичный объект данных. Доставка подписчику может быть автоматически отфильтрована по ведущей строке большого двоичного объекта. Доступные виды транспорта сообщений включают TCP , PGM (надежную многоадресную рассылку), межпроцессное взаимодействие ( IPC ) и межпотоковое взаимодействие (ITC).

Базовая библиотека ZeroMQ работает очень хорошо благодаря своей внутренней потоковой модели и может превзойти обычные приложения TCP с точки зрения пропускной способности за счет использования метода автоматической пакетной обработки сообщений. [3] [4]

ZeroMQ реализует ZMTP, протокол передачи сообщений ZeroMQ. [5] ZMTP определяет правила обратной совместимости, расширяемые механизмы безопасности, кадрирование команд и сообщений, метаданные соединения и другие функции транспортного уровня. Все больше проектов реализуют ZMTP напрямую в качестве альтернативы использованию полной реализации ZeroMQ. [6]

Генеральный директор iMatix Питер Хинтьенс зарегистрировал домен zeromq.org в мае 2007 года и начал проект ZeroMQ вместе с Мартином Сустриком, который был его архитектором и ведущим разработчиком до декабря 2011 года.

30 марта 2010 года Hintjens объявил, что iMatix (первоначальный разработчик протокола Advanced Message Queuing Protocol ) покинет рабочую группу AMQP и не планирует поддерживать AMQP / 1.0 в пользу значительно более простого и быстрого ZeroMQ. [7] [8]

В 2011 году ЦЕРН исследовал способы унификации решений промежуточного программного обеспечения, используемых для работы ускорителей ЦЕРН. В исследовании CERN сравнивались две реализации с открытым исходным кодом: CORBA , Ice , Thrift , ZeroMQ, YAMI4, [9] RTI и Qpid (AMQP), и ZeroMQ получил наивысший балл, в частности, за его универсальность, включая легкую адаптируемость к LynxOS . [4]

В начале 2012 года два первоначальных разработчика разделили ZeroMQ как Crossroads I / O. [10] [11] Мартин Сустрик запустил nanomsg, [12] переработку базовой библиотеки ZeroMQ. [13]

В августе 2012 года Донмин Ю объявил о своей чистой Java-конверсии ZeroMQ, JeroMQ. [14] Это послужило поводом для создания новых полнофункциональных портов ZeroMQ, таких как NetMQ для C # [15] и zmq.rs для Rust. [16]

В марте 2013 года Питер Хинтьенс объявил о новом проекте протокола ZMTP на уровне проводов, который привносит в ZeroMQ расширяемые механизмы безопасности. [17] Вскоре после этого Мартин Хертон реализовал механизм аутентификации и шифрования CurveZMQ [18] в основной библиотеке.

Процесс разработки

Сообщество ZeroMQ в основном использует Контракт на строительство Коллективного кодекса (C4) [19] в качестве контракта на разработку. C4 вдохновлен процессами Википедии и моделью GitHub fork + pull request . Он фокусируется на упрощении участия новых участников и уменьшении зависимости от старых участников.

Смотрите также

  • Портал бесплатного программного обеспечения с открытым исходным кодом
  • Xitami
  • Gearman
  • Эндуро / X
  • MQTT

Рекомендации

  1. ^ «Выпуск 4.3.4» . 17 января 2021 . Проверено 2 февраля 2021 года .
  2. ^ Уровень масштабируемости попадает в интернет-стек
  3. ^
  4. «Почему ØMQ имеет более высокую пропускную способность, чем TCP, хотя он построен на основе TCP?» . ZeroMQ FAQ . Проверено 8 июня 2013 года .
  5. ^ а б
  6. «Тенденции в области промежуточного программного обеспечения и лидеры рынка 2011» . Проверено 8 июня 2013 года .
  7. ^
  8. «Протокол передачи сообщений ZeroMQ» . Проверено 8 июня 2013 года .
  9. ^
  10. «Поиск на GitHub: ZMTP» . Проверено 8 июня 2013 года .
  11. ^
  12. «iMatix прекратит поддержку OpenAMQ к 2011 году» . почтовый ящик openamq-dev. Архивировано из оригинала на 2016-03-05 . Проверено 5 сентября 2018 года .
  13. ^
  14. «Что не так с AMQP (и как это исправить)» . iMatix Corporation . Проверено 14 июля 2012 года .
  15. ^
  16. «Вдохновение ЯМИ4» . Проверено 14 июля 2012 года .
  17. ^
  18. «ZeroMQ и Crossroads I / O: раздвоение торговых марок» . LWN.net . Проверено 14 июля 2012 года .
  19. ^
  20. «Перекресток ввода / вывода» . Проверено 14 июля 2012 года .
  21. ^
  22. «наномсг» . Проверено 8 июня 2013 года .
  23. ^
  24. «Почему я должен [sic] писать ZeroMQ на C, а не на C ++» .
  25. ^
  26. «jeromq — java pojo zeromq» . Список рассылки zeromq-dev . Проверено 23 мая 2013 года .
  27. ^
  28. «NetMQ» . GitHub . Проверено 23 мая 2013 года .
  29. ^
  30. «zmq.rs» . GitHub . Дата обращения 24 октября 2020 .
  31. ^
  32. «Защита ZeroMQ: проект протокола ZMTP v3.0» . Hintjens.com . Проверено 23 мая 2013 года .
  33. ^ curvezmq.org
  34. ^
  35. «Коллективный кодекс строительного договора (C4.1)» . ZeroMQ RFC . Проверено 23 мая 2013 года .

Внешние ссылки

  • Официальный веб-сайт
  • Сообщество ØMQ на GitHub
  • Мартин Сустрик, Мартин Лучина (20 января 2010 г.). 0MQ: новый подход к обмену сообщениями — LWN.net
  • ZeroMQ is the Answer (доклад на конференции PHP UK 2012)
  • ZeroMQ — введение
  • Почему ZeroMQ? (вступительное видео)
  • ZeroMQ: Modern & Fast Networking Stack (обзор с примерами Ruby)
  • ØMQ: Теоретическая основа

Распределенные системы с ZeroMQ

Отойдя немного от моей текущей серии статей о Gevent и Python, сегодня я хочу взглянуть на другую сетевую технологию, которая набирает обороты: ZeroMQ . Так что без дальнейших церемоний, давайте прыгнем прямо в …

Принципы дизайна ZeroMQ

Во-первых, ZeroMQ не является брокером сообщений. Люди иногда принимают его за одно из-за его имени. На самом деле ZeroMQ — это библиотека, которая поддерживает определенные шаблоны сетевого взаимодействия с использованием сокетов . Входит часть «MQ», потому что ZeroMQ использует очереди для буферизации сообщений, чтобы вы не блокировали приложение при отправке данных. Когда вы говорите socket.send (…), ZeroMQ фактически ставит в очередь сообщение, которое будет отправлено позже выделенным потоком связи. (Этот коммуникационный поток и его состояние инкапсулированы в объекте контекста ZeroMQ, который используется ниже; у большинства программ будет один контекст.)

ZeroMQ привязка / подключение по сравнению с «обычными» сокетами

Далее, имейте в виду, что ZeroMQ отделяет понятие клиентов и серверов от базового шаблона связи. Например, вы можете использовать для создания сокета для приема запросов с шаблоном, подобным следующему:

from socket import socket sock = socket() sock.bind(('', 8080)) sock.listen(256) while True: cli = sock.accept() # The following code would probably be handled in a 'worker' thread or # greenlet. It's included here only for example purposes. message = cli.recv(. ) response = handle_message(message) cli.send(response)

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

Затем клиент подключится () к серверу и отправит запрос:

from socket import socket sock = socket() sock.connect(('localhost', 8080)) sock.send(message) response = sock.recv(. )

В ZeroMQ любой конец шаблона запроса / ответа может связываться, и любой конец может подключаться. Например, используя библиотеку pyzmq , вы можете подключить свой «сервер» (тот, кто обрабатывает запросы) к «клиенту» (тому, кто отправляет запросы). Код «сервера» тогда выглядит так:

import zmq context = zmq.Context.instance() sock = context.socket(zmq.REP) sock.connect('tcp://localhost:8080') while True: message = sock.recv() response = handle_message(message) sock.send(response)

Код «клиента» будет выглядеть так:

import zmq context = zmq.Context.instance() sock = context.socket(zmq.REQ) sock.bind('tcp://*:8080') sock.send(message) response = sock.recv()

Несколько вещей заслуживают внимания здесь. Во-первых, как отмечено выше, «сервер» выполняет подключение, а «клиент» выполняет привязку. Еще одна вещь, которую нужно отметить, это используемый адрес. Вместо передачи имени хоста / порта мы передаем URI.

Виды транспорта ZeroMQ

ZeroMQ поддерживает несколько различных стилей URI для своего транспортного уровня, каждый из которых поддерживает полную гамму функциональных возможностей ZeroMQ:

  • tcp: // имя хоста: портовые сокеты позволяют нам делать «обычные» TCP-сети
  • inproc: // имя сокета позволяет нам выполнять внутрипроцессное сетевое взаимодействие (inter-thread / greenlet) с тем же кодом, который мы использовали бы для TCP-сети
  • ipc: /// сокеты tmp / filename используют сокеты домена UNIX для межпроцессного взаимодействия
  • pgm: // interface: address: port и epgm: // interface: address: port использует библиотеку OpenPGM для поддержки многоадресной передачи по IP (pgm) и по UDP (epgm). Из-за многоадресной передачи транспорты pgm и epgm могут использоваться только с типами сокетов PUB / SUB (подробнее об этом ниже).

ZeroMQ отключил операцию

Одна особенность, которая иногда ловит новичков в ZeroMQ, заключается в том, что она поддерживает отключенную работу. Например, в приведенном выше коде мы могли бы сначала запустить сервер, а затем клиента. С сокетами TCP это не будет работать, потому что сервер пытается соединиться () с клиентом. В ZeroMQ метод connect () будет проходить «оптимистично», предполагая, что кто-то будет связываться с этим портом позже.

Более того, вы можете запустить клиент, подключиться к порту 8080, выполнить транзакцию с сервером и затем завершить работу. Затем другой клиент может запустить, подключиться к порту 8080 и выполнить другую транзакцию. Сервер просто продолжает обрабатывать запросы, с радостью «подключенный» к тому, что происходит с портом 8080.

Инкапсуляция сообщений ZeroMQ

Последний аспект ZeroMQ заключается в том, что он инкапсулирует связь в сообщения, которые могут состоять из нескольких частей . Вместо того, чтобы просить ZeroMQ получить определенное количество байтов из сокета, вы просите ZeroMQ получить одно сообщение . Вы также можете отправлять и получать многокомпонентные сообщения , используя опцию zmq.SNDMORE и zmq.RECVMORE. Чтобы отправить составное сообщение, просто используйте zmq.SNDMORE в качестве второго аргумента для каждой части send (), кроме последней:

sock.send(part1, zmq.SNDMORE) sock.send(part2, zmq.SNDMORE) sock.send(part3, zmq.SNDMORE) sock.send(final)

Затем клиент может спросить, есть ли еще что получить:

more = True parts = [] while more: parts.append(sock.recv()) more = sock.getsockopt(zmq.RCVMORE) 

Шаблоны связи ZeroMQ

Основной концепцией ZeroMQ, на которую я ссылался выше, но не сделал ее явной, являются шаблоны связи, поддерживаемые ZeroMQ. Из-за некоторых функций быстрого доступа, таких как асинхронная связь и отключенная работа, необходимо применять шаблоны более высокого уровня, чем просто пересылать байты из одной конечной точки в другую. ZeroMQ реализует это, заставляя указывать тип socket_type при вызове zmq.Context.socket (). Каждый тип сокета имеет набор «совместимых» типов сокетов, с которыми он может взаимодействовать, и ZeroMQ вызовет исключение, если вы попытаетесь установить связь между несовместимыми сокетами. Здесь я опишу некоторые из основных моделей:

Шаблон запроса / ответа ZeroMQ

This pattern is fairly classic; one end (with socket_type=zmq.REQ) sends a request and receives a response. The other end (with socket_type=zmq.REP) receives a request and sends a response. A simple echo server might use this pattern. The server would be the following:

import sys import zmq context = zmq.Context() sock = context.socket(zmq.REP) sock.bind(sys.argv[1]) while True: message = sock.recv() sock.send('Echoing: ' + message)

Your client then looks like this:

import sys import zmq context = zmq.Context() sock = context.socket(zmq.REQ) sock.connect(sys.argv[1]) sock.send(' '.join(sys.argv[2:])) print sock.recv()

Note that in this pattern the zmq.REQ socket must communicate with a series of send(), recv() pairs, and the zmq.REP socket must communicate with a series of recv(), send() pairs. If you try to send or recv two messages in a row, ZeroMQ will raise an exception. This can cause problems if you have a server that crashes, for instance, because you’d leave your client in a «dangling send» state. To recover, you need some other mechanism for timing out requests, closing the socket, and retrying with a new, fresh zmq.REQ socket.

ZeroMQ publish/subscribe pattern

In the publish/subscribe pattern, you have a single socket of type zmq.PUB and zero or more connected zmq.SUB sockets. The zmq.PUB socket broadcasts messages using send() that the zmq.SUB sockets recv(). Each subscriber must explicitly say what messages it’s interested in using the setsockopt method. A subscription is a string specifying a prefix of messages the subscriber is interested in. Thus to subscribe to all messages, the subscriber would use the call sub_sock.setsockopt(zmq.SUBSCRIBE, »). Subscribers can also explicitly unsubscribe from a topic using setsockopt(zmq.UNSUBSCRIBE, … as well.

Одним интересным аспектом сокетов zmq.SUB является то, что они могут подключаться к нескольким конечным точкам, чтобы они получали сообщения от всех издателей. Например, предположим, что у вас есть сервер, периодически отправляющий сообщения:

import sys import time import zmq context = zmq.Context() sock = context.socket(zmq.PUB) sock.bind(sys.argv[1]) while True: time.sleep(1) sock.send(sys.argv[1] + ':' + time.ctime())

Вы можете подключить клиент к нескольким серверам с помощью следующего кода:

import sys import zmq context = zmq.Context() sock = context.socket(zmq.SUB) sock.setsockopt(zmq.SUBSCRIBE, '') for arg in sys.argv[1:]: sock.connect(arg) while True: message= sock.recv() print message

Чтобы увидеть множественную подписку в действии, вы можете запустить эти программы следующим образом:

$ python publisher.py tcp://*:8080 & python publisher.py tcp://*:8081 & $ python subscriber.py tcp://localhost:8080 tcp://localhost:8081

ZeroMQ двухтактный

Подобно шаблону pub / sub в шаблоне push / pull у вас есть одна сторона (сокет zmq.PUSH), которая выполняет всю отправку, а другая сторона (zmq.PULL) выполняет всю передачу. Разница между push / pull и pub / sub заключается в том, что в push / pull каждое сообщение направляется в один сокет zmq.PULL, тогда как в pub / sub каждое сообщение транслируется на все сокеты zmq.SUB. Шаблон push / pull полезен для конвейерных рабочих нагрузок, когда рабочий процесс выполняет некоторые операции, а затем отправляет результаты для дальнейшей обработки. Это также полезно для реализации традиционных очередей сообщений.

Мы можем видеть маршрутизацию сообщений, подключив несколько клиентов к одному серверу. В этом примере мы можем просто изменить наш тип сокета в коде издателя на тип zmq.PUSH:

import sys import time import zmq context = zmq.Context() sock = context.socket(zmq.PUSH) sock.bind(sys.argv[1]) while True: time.sleep(1) sock.send(sys.argv[1] + ':' + time.ctime())

Наш клиент также похож на код подписчика:

import sys import zmq context = zmq.Context() sock = context.socket(zmq.PULL) for arg in sys.argv[1:]: sock.connect(arg) while True: message= sock.recv() print message

(Обратите внимание, что мы можем сделать тот же трюк с множественным соединением, что и с pub / sub.) Теперь, чтобы увидеть multi-push, multi-pull, мы можем запустить два «толкателя» и два «съемника»:

$ # Start the pushers in one window $ python pusher.py tcp://*:8080 & python pusher.py tcp://*:8081 & $ # Start a puller in another window $ python puller.py tcp://localhost:8080 tcp://localhost:8081 $ # Start another puller in a third window $ python puller.py tcp://localhost:8080 tcp://localhost:8081

Вывод

ZeroMQ предоставляет удобную абстракцию для нескольких шаблонов сетевого взаимодействия, которые мы можем довольно легко использовать из Python. Если вы думаете о создании высокопроизводительной распределенной системы, безусловно, стоит рассмотреть ZeroMQ в качестве возможного транспортного уровня. Здесь я только немного рассказал о том, что возможно с ZeroMQ в Python. В будущих постах я пойду немного глубже, охватывая такие темы, как:

  • управление потоком с ZeroMQ
  • передовые шаблоны и устройства связи
  • используя ZeroMQ с Gevent

Мне бы хотелось услышать, как вы используете (или думаете об использовании) ZeroMQ для создания приложений на Python. В частности, есть ли у вас какие-либо вопросы о ZeroMQ, на которые я мог бы ответить в последующих постах? Вы уже используете ZeroMQ, и если так, столкнулись ли вы с какими-либо проблемами? Расскажите мне об этом в комментариях ниже!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *