Делаем прием данных более гибким
Не всегда мы имеем представление для конкретного процесса о том какой длины придут данные, для определения существует как раз выше упомянутая структура MPI_Status и некоторые процедуры помогающие эту информацию оттуда извлечь.
Первая процедура которую мы обсудим следующая:
По структуре status процедура определяет сколько данных типа datatype передано соответствующим сообщением и записывает результат по адресу count.
То есть буквально, если мы получаем сообщение от какого-либо процесса и не знаем сколько точно там передано данных, то можем вызвать процедуру MPI_Get_count и узнать какое количество ячеек памяти мы можем считать заполненными корректными данными(если конечно их отправляющий процесс корректно формирует).
Также есть еще одна процедура MPI_Get_elements. По синтаксису они отличаются лишь названиями, но назначение слегка разное. Если в сообщении передаются данные не базового типа, а типа который является производным от базовых(то есть составлен с помощью базовых типов), то нам вернет не количество этих данных, а именно количество данных базового типа. Однако в случае если передаются данные базового типа, то функции вернут одинаковые значения.
Также иногда случается так, что нам надо просто пропустить отправку сообщения, но саму процедуру из кода исключать не хочется, либо делать лишние условия в коде не рационально. В таких случаях процесс может отправить сообщение не существующему процессу, номер такого процесса определен константой MPI_PROC_NULL. В случае если мы передаем сообщение такому процессу, то процедура сразу завершается с кодом возврата SUCCESS.
Хорошо, мы можем принимать на вход какие либо данные и не знать сколько их точно поступает. В таком случае нужно рационально выделять какой-то объем памяти для их сохранения(буферизации). Возникает логичный вопрос о том какой объем выделять, в этом нам поможет процедура MPI_Probe. Она позволяет получить информацию о сообщении которое ожидает в очереди на прием не получая самого сообщения. Синтаксис ее выглядит следующим образом:
Тут мы также определяем от какого процесса получаемое сообщение, с каким тэгом, какой коммуникатор связывает эти процессы и передаем структуру которая запишет необходимую информацию.
Теперь на очень простом примере соединим эти процедуры вместе:
Что тут происходит?В данной программе первый процесс создает массив размером равным количеству процессов и заполняет его номерами процессов по очереди. Потом соответствующему процессу он отправляет такое число элементов этого массива, какой номер у этого процесса. Напрмер: процесс 1 получит 1 элемент, процесс 2 получит 2 элемента этого массива и так далее.
Следующие же процессы должны принять это сообщение и для этого смотрят в очередь сообщений, видят это сообщение, собирают информацию в структуру status, после чего получают длину переданного сообщения и создают буфер для его сохранения, после чего просто выводят то, что получили. Для 5 запущенных процессов результат будет вот таким:
Собственно 4 результата потому что нулевой процесс занимается отправкой этих сообщений.
Предисловие
Практически все программы с применением технологии MPI используют не только средства для порождения процессов и их завершения, но и одну из самых важных частей, заложенных в названии самой технологии (Message Passing Interface), конечно же явная посылка сообщений между процессами. Описание этих процедур начнем, пожалуй, с операций типа точка-точка.
Давайте представим себе ту самую картинку где много компьютеров соединены линиями, стандартная иллюстрация сети(локальной), только на месте узлов будут стоять отдельные процессы, процессоры, системы и т.п. Собственно отсюда становится понятно к чему клонит название «операции типа точка-точка», два процесса общаются друг с другом, пересылают какие-либо данные между собой.
Работает это так: один из процессов, назовем его P1, какого-то коммуникатора C должен указать явный номер процесса P2, который также должен быть под коммуникатором С, и с помощью одной из процедур передать ему данные D, но на самом деле не обязательно нужно знать номер процесса, но это мы обсудим далее.
Процедуры в свою очередь разделены на 2 типа: с блокировкой и без блокировки. 1. Процедуры обмена с блокировкой просто останавливают процесс который должен принять сообщение до момента выполнения какого-либо условия. 2. Процедуры без блокировки, как их ещё называют асинхронные, они возвращаются сразу после осуществления коммуникации, немедленно.
Оба типа процедур мы затронем подробно, а пока начнем с процедур с блокировкой.
Коррелирование сообщений типа «запрос-ответ»
Хотя очереди обычно являются однонаправленными, в некоторых сценариях может потребоваться корреляция полученного ответа с ранее переданным запросом. Если требуется такая корреляция, рекомендуется применять свой собственный заголовок сообщения SOAP, содержащий корреляционные сведения для сообщения. Обычно отправитель добавляет этот заголовок в сообщение, а получатель, после обработки сообщения и передачи нового ответного сообщения в очередь ответов, добавляет заголовок сообщения отправителя, содержащий корреляционную информацию, чтобы отправитель мог связать ответное сообщение с сообщением запроса.
Синхронное представление в быту
У нас есть некая занятая девушка, которая запланировала на вечер познакомить родителей со своим молодым человеком. Чтобы все прошло идеально, ей нужно:
-
доделать дела на работе;
-
подготовить вечерний наряд;
-
сделать прическу, маникюр и накраситься;
-
попросить маму накрыть на стол.
Без законченных дел на работе, подготовки вечернего наряда, приукрашивания себя самой и накрытого стола знакомство с родителями точно не состоится! Девушка живет в таком мире, что, пока не доделана одна работа, она не может приступить к следующей.
Девушка берет телефон в руки и начинает звонить. Набрала маму и попросила на вечер накрыть стол. Мама принялась готовить. Девушка заказала красивое вечернее платье и согласовала адрес его доставки. Съездила сделала себе маникюр и прическу. Наступил вечер, знакомство не состоялось. Почему? Девушка была так занята подготовкой, что не успела доделать дела на работе, потому что целый день провела с телефоном, отдавая команды и ожидая подтверждения, что все готово.
Если бы девушка жила в асинхронном мире, то знакомство с родителями не зависело бы от накрытого стола или выглаженного платья. Отдав команды по телефону на выполнение необходимых задач, ей не пришлось бы постоянно перезванивать и спрашивать, все ли готово. Она бы спокойно выполняла свою основную работу, а все остальные просто отзвонились бы, как только ее команда была бы выполнена. И тогда знакомство бы состоялось.
Комментарии
Члены MessageQueue класса включают следующие методы для чтения и записи сообщений в очередь.
-
SendМетод позволяет приложению записывать сообщения в очередь. Перегрузки метода позволяют указать, следует ли отправлять сообщение с помощью Message (которое обеспечивает подробный контроль над отправляемой информацией) или любым другим управляемым объектом, включая классы для конкретных приложений. Метод также поддерживает отправку сообщений в рамках транзакции.
-
ReceiveМетоды, ReceiveById и ReceiveByCorrelationId предоставляют функции для чтения сообщений из очереди. Как и Send метод, эти методы предоставляют перегрузки, поддерживающие обработку транзакционной очереди. Эти методы также предоставляют перегрузки с временем ожидания.
Выходные параметры, которые позволяют продолжить обработку, если очередь пуста. Поскольку эти методы являются примерами синхронной обработки, они прерывают текущий поток до тех пор, пока не будет задано время ожидания.
-
PeekМетод аналогичен Receive , но он не вызывает удаление сообщения из очереди при чтении. Поскольку не Peek изменяет содержимое очереди, для поддержки транзакционной обработки отсутствуют перегрузки. Тем не менее, так как Peek , например Receive , считывает сообщения синхронно из очереди, перегрузки метода поддерживают указание времени ожидания, чтобы предотвратить бесконечное ожидание потока.
-
BeginPeekМетоды, , BeginReceive и предоставляют способы асинхронного чтения сообщений из очереди. Они не прерывают текущий поток при ожидании поступления сообщения в очередь.
Следующие методы MessageQueue класса предоставляют функциональные возможности для получения списков очередей по указанным критериям и определения существования определенных очередей.
-
включает получение частных очередей на компьютере.
-
, и предоставляют способы получения общих очередей по общим критериям. Перегрузка GetPublicQueues предоставляет еще более подробные сведения для выбора очередей на основе ряда условий поиска.
Другие методы MessageQueue класса предоставляют следующие функциональные возможности.
-
Создание и удаление очередей сообщений очереди.
-
Использование перечислителя сообщений для пошагового прохода по сообщениям в очереди.
-
Использование перечислителя очереди для прохода по очередям в системе.
-
Настройка прав доступа на основе ACL.
-
Работа с кэшем подключений.
MessageКласс предоставляет подробный контроль над информацией, отправляемой в очередь, и является объектом, используемым при получении или просмотре сообщений из очереди. Помимо текста сообщения, свойства Message класса включают параметры подтверждения, выбор модуля форматирования, идентификацию, проверку подлинности и шифрование, метки времени, указания по использованию трассировки, ведения журнала сервера, очереди недоставленных сообщений и данные транзакций.
MessageQueueКомпонент связан с тремя модулями форматирования, которые позволяют сериализовать и десериализовать сообщения, отправленные и получаемые из очередей:
-
XmlMessageFormatterПлатформа обеспечивает слабо связанный обмен сообщениями, обеспечивая независимое управление версиями сериализованных типов на клиенте и на сервере.
-
ActiveXMessageFormatterКомпонент совместим с элементом управления COM MSMQ. Он позволяет отправлять типы, которые могут быть получены элементом управления, и получать типы, отправленные элементом управления.
-
BinaryMessageFormatterПредоставляет более быструю альтернативу XmlMessageFormatter , но без преимуществ слабо связанного обмена сообщениями.
Универсальный обмен между идентичными конфигурациями через REST интерфейс OData. Часть І: Справочники Промо
Сейчас все чаще интеграции различных конфигураций проектируются через HTTP-сервисы — они и работают быстрее, и «войти» в режим отладки гораздо проще, тем самым обойдя «черный ящик» универсального обмена через xml, например.
Более года назад я начал работать в компании, в которой разработчики работали с конфигурациями 1С в режиме совместимости еще 8.2.16 (менять режим совместимости в типичных базах мы не хотели) — а как Вы наверное знаете, если интересовались HTTP-сервисами в 1С, их использование в режиме совместимости 8.3.4 и ниже недопустимо — и здесь я уже не надеялся на разработку и использование HTTP-сервисов.
Но позже меня заинтересовал такой «сервис» как REST интерфейс OData, так как его можно использовать не меняя режим совместимости конфигурации — именно он и стал для меня идеальным вариантом решения «нетривиальных» задач.
Паттерны межсервисного общения
Всё просто. Их два — это синхронные и асинхронные сообщения.
Синхронные
Отправляя синхронное сообщение мы ожидаем, что тут же что-то придёт в ответ. Это может быть удалённый вызов процедуры (Remote Procedure Call, далее — RPC), или HTTP запрос к RESTful сервису, либо что-нибудь другое. Но в любом случае за запросом должен последовать ответ.
Синхронное общение заходит лучше всего. С удачным API создаётся впечатление, что на самым деле мы вызываем методы локального объекта, или отправляем запросы к локальной же базе данных. К несчастью, это всё ложь: на самом деле вызываемый объект лежит где-то далеко в сети, а сеть, господа, имеет тенденцию падать. Так уж она сделана. Но на дизайне приложения эта реальность никак не сказывается. Может, добавим дополнительный try-catch, и всё.
Еще одна проблема при использовании синхронного подхода кроется в том, что RPC и REST предполагают какое-то знание о вызываемой стороне. Но сервисам полагается быть независимыми — так в умных книгах пишется. В общем, проблема.
Асинхронное
Асинхронные сообщения подходят с другого боку. Вместо того, чтобы говорить удалённому сервису «а сделай-ка вот это», сервис-инициатор отправляет сообщение в космос «а вот было бы здорово, если бы…» не особо, впрочем, рассчитывая, что его молитвы будут услышаны. Но обычно боги микросервисов не дремлют, и кто-то слышит и реагирует.
Вот более атеистический пример этой же идеи. Например, пусть в нашем приложении будут два сервиса: UI и сервис обработки заказов. При синхронном подходе, как только UI-сервис заметит, что пользователь нажал кнопку «Заказать», он вызовет PlaceOrder метод в удалённом обработчике заказов через какой-нибудь RPC или REST.
При асинхронном подходе UI просто отправит сообщение «Тут приходили… Есть заказ..», но при этом ему не особо интересно, кто получит это сообщение и что он будет с этим делать.
Простой способ
Передача сообщения в очередь простым способом требует буквально двух
этапов. Во-первых, нужно получить ссылку на соответствующую
очередь сообщений (по пути к очереди, строке Format Name или
метке), а во-вторых, вызвать метод Send объекта
MessageQueue, передав ему посылаемый объект и при необходимости
метку сообщения. Вот и все. Формат и параметры отправки сообщения
определяются значениями по умолчанию — указывать их не требуется.
Следующий код использует процедуру GetQ (подробнее о ней — далее в этой
статье) и посылает в локальную закрытую очередь простое строковое
сообщение:
Private Const QUEUE_NAME As String = ".\private$\Orders" Dim msqQ As MessageQueue Dim msgText As String ' Подготавливаем текстовое сообщение msgText = String.Format("Sample Message Sent At {0}", DateTime.Now()) Try ' Получаем объект MessageQueue очереди msqQ = GetQ(QUEUE_NAME) ' Отправляем сообщение в очередь msqQ.Send(msgText) Catch e As Exception ' Обрабатываем ошибку … End Try
См. Example 3 в полном
исходном коде (EN) BDAdotNetAsync1.vb.
Указать метку сообщения можно, включив в вызов метода Send
второй параметр:
msgQ.Send(sMessage, "Sample Message")
Просмотреть имеющиеся на компьютере очереди и отдельные сообщения
в них можно при помощи MSMQ Explorer (в Windows NT) или оснастки
Computer Management (в Windows 2000/XP). Если сообщению присвоена метка,
она отображается на правой панели; в ином случае правая панель пуста.
Рис. 1. Узел Message Queuing в оснастке Computer Management
Чтобы просмотреть какое-либо сообщение, щелкните его правой кнопкой
мыши, выберите Properties и перейдите на вкладку Body. Если сообщение
было отправлено простым способом и значения параметров по умолчанию
не изменялись, любой переданный объект сериализован в XML-код —
по аналогии со следующим примером (и на рис. 2):
<?xml version="1.0"?> <string>Sample Message Sent At 5/25/2001 3:51:48 PM</string>
Рис. 2. Тело сообщения, сериализованное в XML
По умолчанию сообщения
отсылаются в XML-формате, но можно выбрать и другой формат.
Подробнее о параметрах форматирования, используемых при сериализации
сообщения, см. раздел «Сложный способ».
Данный способ позволяет отправить в очередь любой объект, просто
передав его методу Send. Любой аргумент, получаемый этим методом,
сериализуется в XML (см. предыдущее примечание) и помещается в тело
сообщения. MSMQ используется для передачи не только базовых типов
данных, таких как String, Integer и Double,
но и объектов двух других типов: DataSet и специфичных для
приложения классов объектов, создаваемых пользователем. Следующий
фрагмент кода отправляет в очередь объекты обоих этих типов,
демонстрируя, насколько легко передавать любую информацию через классы
System.Messaging.
Чтобы отправить DataSet, создайте его экземпляр и передайте
методу Send объекта MessageQueue. Как и в случае со String,
объект DataSet будет сериализован в XML и помещен в тело
сообщения:
Dim msgQ As MessageQueue Dim msgText As String Dim dSet As DataSet ' Внешний код, заполняющий объект DataSet dSet = GetDataSet() ' Получаем объект MessageQueue для очереди msgQ = GetQ(QUEUE_NAME) ' Отправляем сообщение с объектом DatSet в очередь с меткой "Order" msgQ.Send(dSet, "Order")
См. Example 4 в полном
исходном коде (EN) BDAdotNetAsync1.vb.
Если в вашей системе есть собственные классы для хранения данных,
например класс Order, соответствующие объекты можно передавать
в очередь через MSMQ, как в примере с объектом DataSet.
Классы .NET Messaging сериализуют экземпляр объекта в XML и добавят его
в сообщение. Следующий фрагмент кода создает экземпляр класса Order
и задает значения его свойств. Затем методом Send объекта
MessageQueue этот код помещает объект в сообщение и добавляет его
в очередь. Рассмотрев сложный способ передачи сообщений, мы обсудим, как
получить этот или любой другой объект из очереди при работе с входящими
сообщениями.
Dim msgQ As MessageQueue Dim myOrder As New Order() ' Заполняем Order-объект myOrder.CustomerID = "ALKI" myOrder.ID = 34 myOrder.ShipDate = DateTime.Now() ' Получаем объект MessageQueue для очереди msgQ = GetQ(QUEUE_NAME) ' Посылаем в очередь сообщение с Order-объектом msgQ.Send(myOrder, "Order")
См. Example 5 в полном
исходном коде (EN) BDAdotNetAsync1.vb.
[ELMA3] Инструкция по настройке очереди сообщений MSMQ
В данной статье мы рассмотрим, как установить и настроить очереди MSMQ сообщений в Windows 2008 R2. Это необходимо сделать для наличия возможности настройки блоков отправки и ожидания сообщений в Дизайнере.
1. Открываем Диспетчер сервера (Server Manager).
2. В контекстном меню узла Компоненты выбираем пункт Добавить компоненты. Запустится мастер добавления компонентов.
3. Устанавливаем флажки Очередь сообщений и Службы очереди сообщений, нажимаем на кнопку Далее.
4. В окне подтверждения выбранных элементов для установки нажимаем на кнопку Установить.
5. Ждем завершения установки – результат установки будет отображен в данном окне.
6. Результат установки «Установка прошла успешно», нажимаем на кнопку Закрыть.
7. В Диспетчере сервера появится компонент Очереди сообщений.
8. Для локального сервера настраиваются частные очереди. Можно создавать общие очереди на удаленных компьютерах с помощью оснастки «Active Directory – пользователи и компьютеры». Для этого необходимо обладать правами администратора домена и войти в систему под пользователем, обладающим правами администратора. Папки «Исходящие очереди» и «Системные очереди» создаются автоматически.
Рассмотрим пример создания частных очередей. Преимущество частных очередей заключается в том, что у них нет дополнительных затрат на взаимодействие со службой каталогов.
Для создания частной очереди нажимаем правой кнопкой мыши по папке Частные очереди и в выпадающем меню выбираем пункт Создать – Частную очередь.
9. В открывшемся окне указываем название очереди (в нашем случае test) и нажимаем на кнопку ОК.
10. В Дизайнере настраиваем строку подключения для очереди MSMQ. Строка задается в формате: .\private$\test
11. Для проверки работоспособности созданной очереди нажимаем на кнопку Послать тестовое сообщение – будет отображено окно с результатом отправки сообщения.
12. После успешной отправки сообщения в настроенной очереди появится соответствующее сообщение:
Применение специфического форматирующего объекта
При использовании форматирующего XML-объекта процесс
передачи одинаков независимо от типа данных. Если вы применяете другой
форматирующий объект, например BinaryMessageFormatter, то перед
извлечением тела сообщения укажите нужный форматирующий объект.
Следующий фрагмент кода получает экземпляр класса Order с помощью
двоичного форматирующего объекта:
Dim msgQ As MessageQueue Dim myOrder As Order ' Получаем объект MessageQueue для очереди msgQ = GetQ(QUEUE_NAME) ' Задаем форматирующий объект BinaryMessageFormatter msgQ.Formatter = New BinaryMessageFormatter() ' Считываем сообщение из очереди и преобразуем его в Order-объект myOrder = CType(msgQ.Receive().Body, Order) ' Выполняем требуемые действия над Order-объектом Console.WriteLine(myOrder.CustomerID)
См. Example 9 в полном
исходном коде (EN) BDAdotNetAsync1.vb.
Процедура SendMessage — еще один способ передачи сообщений:
задав свойство Formatter объекта MessageQueue, вы можете
контролировать сериализацию, применяемую при отправке сообщения простым
способом.
Эта статья посвящена асинхронной обработке задач, избавляющей
от ожидания их выполнения. Однако метод Receive, встречавшийся
в нескольких примерах, — полностью синхронный: коду приходится ожидать,
пока метод не получит сообщение из очереди. В наших примерах эта
задержка незаметна, но в реальной ситуации размер сообщения может
составлять до 4 МБ (особенно при обмене объектами DataSet), и,
возможно, вы не захотите дожидаться, когда сообщение будет полностью
помещено в пространство памяти вашей программы. Для поддержки сообщений
большого размера и сетевых соединений с низкой пропускной способностью
объект MessageQueue не только предоставляет метод Receive,
но и позволяет получать сообщения асинхронно. Для этого вы делаете все
то же, что и прежде, но вместо Receive вызываете метод
BeginReceive, при необходимости указывая интервал ожидания. Этот
метод запускает процесс получения сообщения, а программа переходит
к следующей строке кода:
Private Const QUEUE_NAME As String = "liquidsoap\private$\Orders" Private WithEvents msgQ As MessageQueue ' Получаем объект MessageQueue для очереди msgQ = GetQ(QUEUE_NAME) ' Начинаем прослушивать сообщения msgQ.BeginReceive()
Когда сообщение будет успешно извлечено, сработает событие
ReceiveCompleted объекта MessageQueue, и будет вызван ваш
обработчик этого события. Полученное сообщение передается обработчику
как один из его параметров, и этот объект можно использовать для
извлечения реальных данных обычным способом:
Public Sub msgQ_ReceiveCompleted(ByVal sender As Object, _ ByVal e As ReceiveCompletedEventArgs) _ Handles msgQ.ReceiveCompleted Dim msgText As String Dim eMsg As Message ' Получаем сообщение из аргументов события (e) eMsg = e.Message ' Указываем форматирующему объекту, что ' мы ожидаем единственный тип данных (string) eMsg.Formatter = _ New XmlMessageFormatter(New System.Type() {GetType(String)}) ' Получаем тело сообщения и приводим его к типу String msgText = CStr(eMsg.Body) Console.WriteLine(msgText) ' Устанавливаем флаг, чтобы выйти из цикла в AsyncReceive msgReceived = True End Sub
См. Example 10 в полном
исходном коде (EN) BDAdotNetAsync1.vb.
Резюме
Один из основных способов реализации в приложении асинхронной
обработки — интерфейс Message Queuing (MSMQ). Классы System.Messaging
позволяют ставить в очередь сообщения и считывать их оттуда, при этом
сообщения могут содержать данные типов, допустимые в .NET или COM.
О других способах реализации асинхронной обработки см. готовящуюся
к публикации статью
Architectural Topics (EN).
Асинхронная интеграция микрослужб способствует их автономности
Как уже упоминалось, при создании приложения на базе микрослужб важно подумать о способе их интеграции. В идеале взаимодействие между внутренними микрослужбами необходимо свести к минимуму
Чем меньше взаимодействия между микрослужбами, тем лучше. Вам часто придется каким-то образом интегрировать микрослужбы. И когда это необходимо, помните, что взаимодействие между микрослужбами обязательно должно быть асинхронным. Это не означает, что нужно использовать конкретный протокол (например, асинхронный обмен сообщениями, а не синхронный HTTP). Просто взаимодействие между микрослужбами должно происходить только путем асинхронного распространения данных. Но попытайтесь устранить зависимость от других внутренних микрослужб в начальной операции запроса-ответа HTTP.
Если это возможно, никогда не используйте синхронное взаимодействие (запрос-ответ) между несколькими микрослужбами, даже для запросов. Каждая микрослужба должна быть автономной и доступной для клиента, даже если другие службы в этом приложении отключены или не работают. Если вы считаете, что одна микрослужба должна обращаться к другой (например, отправлять HTTP-запрос на получение данных), чтобы предоставить ответ клиентскому приложению, вы создадите архитектуру, неустойчивую к сбоям.
Более того, наличие зависимостей HTTP между микрослужбами, как при создании длинных циклов запрос-ответ с цепочкой HTTP-запросов, как показано в первой части рисунка 4-15, не только нарушит автономность микрослужб, но и повлияет на их производительность, если одна из служб в цепочке не будет работать правильно.
Чем больше синхронных зависимостей между микрослужбами, например запросов, тем больше время отклика в клиентских приложениях.
Рис. 4-15. Антишаблоны и шаблоны при взаимодействии между микрослужбами
Как показано на схеме выше, при синхронном обмене данными «цепочка» запросов создается между микрослужбами при обслуживании запроса клиента. Это антишаблон. В асинхронной связи микрослужбы используют асинхронные сообщения или опрос по HTTP для взаимодействия с другими микрослужбами, но запрос клиента обрабатывается сразу.
Если микрослужба должна вызвать дополнительное действие в другой микрослужбе, по возможности не выполняйте это действие синхронно в рамках исходной операции запроса и ответа микрослужбы. Руководствуйтесь принципом асинхронности (с помощью асинхронного обмена сообщениями или событий интеграции, очередей и т. д.). Старайтесь не вызывать действие синхронно в рамках исходной синхронной операции запроса и ответа.
И, наконец (и именно на этом этапе создания микрослужб возникают проблемы), если исходной микрослужбе нужны данные, принадлежащие другим микрослужбам, не создавайте синхронные запросы этих данных. Лучше реплицировать или распространять эти данные (только необходимые атрибуты) в базу данных исходной службы, используя итоговую согласованность (обычно с помощью событий интеграции, как описано в следующих разделах).
Как уже упоминалось в статье Определение границ модели предметной области для каждой микрослужбы, дублирование данных в нескольких микрослужбах допускается. Более того, таким образом вы сможете перевести данные на конкретный язык, используя термины этой области или ограниченного контекста. Например, в приложении eShopOnContainers есть микрослужба , которая отвечает за большую часть данных пользователя с сущностью, называемой . Но если вам нужно хранить данные о пользователе в микрослужбе , вы используете отдельную сущность под названием . Сущность имеет тот же идентификатор, что и исходная сущность , но содержит лишь некоторые атрибуты, необходимые для предметной области , а не весь профиль пользователя.
Вы можете использовать любой протокол для асинхронной передачи и распространения данных в микрослужбах, чтобы достичь итоговой согласованности. Как уже упоминалось, можно использовать события интеграции с помощью шины событий или брокера сообщений или даже HTTP, чтобы опрашивать другие службы
Это неважно. Главное правило — не создавать синхронные зависимости между микрослужбами
В следующих разделах описываются разные стили взаимодействия, которые вы можете использовать в приложении на базе микрослужб.
Когда следует использовать Message Queuing
Одной из ситуаций, в которых удобно применять Message Queuing — это когда клиентское приложение часто отключается от сети (например, у коммивояжера, навещающего заказчиков на местах). Коммивояжер может вводить данные заказа непосредственно у заказчика. Приложение ставит сообщение о каждом заказе в очередь сообщений, находящуюся на клиентской системе. Как только коммивояжер возвращается в свой офис, заказ автоматически передается из очереди сообщений клиентской системы в очередь сообщений целевой системы, где и обрабатывается.
Помимо портативного компьютера, коммивояжер может использовать устройство Pocket Windows, где также доступно Message Queuing.
Технология Message Queuing может быть полезна и в подключенной среде. Представьте сайт электронной коммерции (показан на рисунке ниже), где в определенные периоды времени сервер полностью загружен обработкой заказов, например, в ранний вечер и в выходные, при этом по ночам нагрузка значительно уменьшается. Решение проблемы может состоять в приобретении более быстрого сервера или в добавлении дополнительных серверов к системе, чтобы они справлялись с пиковыми нагрузками.
Однако существует более дешевое решение: сгладить пиковые нагрузки, сдвинув транзакции со времени максимальных нагрузок на время с низкой загрузкой. В такой схеме заказы отправляются в очередь сообщений, а принимающая сторона читает их тогда, когда это удобно системе базы данных. Таким образом, нагрузка на систему сглаживается по времени, так что сервер, обрабатывающий транзакции, может быть дешевле и не требовать модернизации:
Асинхронное взаимодействие, управляемое событиями
При использовании асинхронного взаимодействия, управляемого событиями, одна микрослужба публикует событие интеграции, когда что-то происходит внутри ее домена, а другая микрослужба должна узнать об этом. Пример такого события — изменение цены в микрослужбе каталога продукции. Дополнительные микрослужбы подписываются на события, что позволяет им получать данные о них асинхронно. В этом случае получатели могут обновить свои собственные сущности домена, что может вызвать появление новых событий интеграции, которые будут опубликованы. Эта система публикаций и подписок реализуется с помощью шины событий. Шина событий может быть разработана как абстракция или интерфейс с API, необходимым для подписки и отмены подписки на события и для публикации событий. Шина событий может иметь одну или несколько реализаций на основе любого межпроцессорного брокера или брокера обмена сообщениями как очередь сообщений или служебная шина, поддерживающая асинхронное взаимодействие и модель публикаций и подписок.
Если система использует итоговую согласованность, управляемую событиями интеграции, рекомендуется разъяснить этот подход конечным пользователям. Не следует использовать в системе подход, который имитирует события интеграции, как, например, в системе SignalR или системах опроса клиентов. Конечный пользователь и владелец компании должны открыто принимать использование в системе принципа итоговой согласованности и понимать, что в большинстве случаев у бизнеса не возникает проблем с этим подходом до тех пор, пока он является открытым
Это важно, поскольку пользователи ожидают увидеть некоторые результаты сразу, а с итоговой согласованностью их может не быть
Как отмечалось ранее в разделе Распределенное управление данными. Проблемы и решения, события интеграции можно использовать для реализации бизнес-задач, охватывающих многие микрослужбы. Таким образом, вы получите итоговую согласованность между этими службами. Согласованная по принципу итоговой согласованности транзакция состоит из коллекции распределенных действий. В каждом действии соответствующая микрослужба обновляет сущность домена и публикует другое событие интеграции, которое вызывает следующее действие в рамках той же конечной задачи.
Важно то, что вы можете передать сообщение сразу нескольким микрослужбам, которые подписаны на это событие. Чтобы сделать это, вы можете использовать систему обмена сообщениями о публикациях и подписках на основе взаимодействия, управляемого событиями, как показано на рис
4-19. Этот механизм публикаций и подписок используется не только в архитектуре микрослужб. Он похож на способ, которым взаимодействуют ограниченные контексты в DDD, или на способ распространения обновлений от баз данных записи к базам данных чтения в архитектурах типа разделение команд и запросов (CQRS). Цель — получить итоговую согласованность между различными источниками данных в распределенной системе.
Рис. 4-19. Асинхронное взаимодействие, управляемое сообщением о событиях
В асинхронном взаимодействии на основе событий одна микрослужба публикует события в шине событий, и многие микрослужбы могут подписаться на нее, чтобы получать уведомления и реагировать на них. Протокол, используемый для взаимодействия на основе сообщений, управляемого событиями, зависит от вашей реализации. Протокол AMQP позволяет добиться надежного взаимодействия с использованием очередей.
При использовании шины событий может возникнуть необходимость использования уровня абстракции (например, интерфейса шины событий) на основе соответствующей реализации в классах с кодом, использующим API из брокера сообщений, например RabbitMQ или служебной шины, такой как служебная шина Azure с разделами. Кроме того, вы можете использовать более высокий уровень служебной шины, например NServiceBus, MassTransit или Brighter , для формулировки шины событий и системы публикации и подписки.