Pull to refresh

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

Reading time 10 min
Views 81K
Original author: Faruk Akgul
Всем привет!
Хочу начать вольный перевод книги «ZeroMQ.Use ZeroMQ and learn how to apply different message patterns». Уверен, что многие захотят разобраться с этой интересной библиотекой.

Содержание


Добро пожаловать в ZeroMQ! Эта глава представляет собой введение в ZeroMQ и дает читателю общее представление о том, что такое система очередей сообщений и, самое главное, что такое ZeroMQ. В этой главе мы поговорим о следующих темах:
  • Обзор того, что представляет собой очередь сообщений
  • Зачем использовать ZeroMQ и что отличает ее от других технологий работы с очередями сообщений
  • Основы клиент/серверной архитектуры
  • Рассмотрим первый паттерн: запрос-ответ
  • Как мы можем обрабатывать строки в C
  • Проверка установленных версий ZeroMQ




Начало


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

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


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



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



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



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



Это традиционный способ программирования, после начала процесса мы дожидаемся его завершения. Недостатком этого подхода является то, что он блокирует выполнение программы в то время, когда задача находится в процессе обработки. Но есть и другой AIO подход. В AIO задача, которая не зависит от процесса все еще продолжается. Во второй главе мы более детально рассмотрим AIO и его использовать в ZeroMQ.
Вы можете удивиться почему вы должны использовать очередь сообщений вместо обработки всех потоков для одно-поточного или много-поточного подхода. Давайте рассмотрим ситуацию, когда у вас есть веб-приложение, похожее на Google Images, в котором вы позволяете пользователям вводить некоторые URL. Как только они предоставят форму, ваше приложение выводит все изображения для данного URL. Тем не менее:
  • Если вы используете одно-поточную очередь, то в случае большого числа пользователей программа будет не в состоянии обработать данные для всех URL-адресов
  • Если вы используете для очереди много-поточный подход, то ваше приложение может быть подвержено DDoS-атаке
  • Вы потеряете данные для всех URL-адресов в случае аппаратного сбоя

В данном случае вы знаете, что вам необходимо добавить данные 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 <string.h>
#include <stdio.h>
#include <unistd.h>
#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 <string.h>
#include <stdio.h>
#include <unistd.h>
#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_msg_send() запрос не указывает, если сообщение было отправлено по сети.
  • Параметр флаг может быть либо ZMQ_DONTWAIT либо ZMQ_SNDMORE. ZMQ_DONTWAIT указывает на то, что сообщение должно быть отправлено асинхронно. ZMQ_SNDMORE указывает, что сообщение является составным и остальная часть сообщения находится в пути.

После отправки сообщения клиент ждет, чтобы получить ответ. Это делается с помощью zmq_msg_recv(3).

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_msg_recv принимает три параметра, а именно сообщение, сокет и флаги.
  • Ранее полученные сообщения (если таковые имеются) признаются недействительными
  • Параметром флага может быть 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, <<"world">>)

Предположим, что наш клиент, написанный на 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 <stdio.h>
#include "zmq.h"

int main (int argc, char const *argv[]) 
{
	int major, minor, patch;
	zmq_version(&major, &minor, &patch);
	printf("Installed ZeroMQ version: %d.%d.%d\n", major, minor, patch);
	
	return 0;
}


Резюме


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

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

Всем большое спасибо за внимание!
Tags:
Hubs:
+38
Comments 9
Comments Comments 9

Articles