WARNING
Данная статья не претендует на оригинальность и не является конечным решением.
Подходы решения задач и примеры программного кода несут исключительно обучающий характер. |
В больших компаниях и холдингах не редкое явление встретить огромное количество интеграционных потоков. Бывают ситуации, когда эти потоки делались по факту потребности и на скорую руку. Решением для возможности управления такими потоками являются сервисы (службы или брокеры) обмена.
Некоторые плюсы использования единого сервера обмена:
- Один или несколько стандартных протоколов обмена данными;
- Возможность построить карту маршрутов передаваемых данных.
На данный момент один из бесплатных и популярных решений - сервис (отдельный сервер или служба) RabbitMQ. Данный сервис имеет множество библиотек под самые разные языки программирования за исключением 1С. Посмотреть подробное описание и возможности самого сервиса можно на его официальном сайте RabbitMQ.com
Итак приступим к "Hello world!" интеграции RabbitMQ и 1С!
Что нам нужно:
- Платформа 1С v8.3.*;
- Сервер RabbitMQ с настроенным обменом и пользователем для подключения;
- Стандартная библиотека RabbitMQ Client для DotNet.
С первым пунктом все ясно, но со второго возникают сложности:
Бесплатный экземпляр сервера RabbitMQ (а точнее его хост) можно получить на CloudAMQP.com. Нужно просто зарегистрироваться, создать новый "инстанс" и новый хост с правами админа готов.
Немного о предоставляемой CloudAMQP бесплатной услуге (тарифный план Little Lemur)
Open connections |
0 of 20
When you've reached the maximum concurrent connections further connections will be prohibited. You can connect again when you're under the limit.
Unfortunately when you've reached the maximum concurrent connections you can't access the management interface either
|
Messages |
0 of 1000000
When you've reached the maximum message transfer limit for this month further publishes or consumes will be prohibited.
|
Max idle queue time |
28 days
If a queue hasn't had any consumer for 28 days it gets deleted. Dedicated plans does not have this limitation.
|
Max queue length |
10 000
If queues gets longer than that, messages will be dropped from the beginning of the queue. Dedicated plans does not have this limitation.
|
Max queue count |
100
If you have more than the limit you won't be able to create new queues. Dedicated plans does not have this limitation.
|
Библиотека RabbitMQ Client для DotNet:
Где получить:
Как установить:
-
Можно установить через стандартные менеджеры пактов NuGet или DotNet;
-
Можно скачать оба пакета NuPkg, извлечь нужные DLL и зарегистрировать в windows через RegAsm.exe:
-
Для x32 \Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase <пусть до распакованной RabbitMQ.Client.dll>
-
Для x64 \Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /codebase <пусть до распакованной RabbitMQ.Client.dll>
-
Для корректной регистрации RabbitMQ.Client.dll через RegAsm, файл Microsoft.Diagnostics.Tracing.EventSource.dll должен быть в той-же папке.
Снимок экрана использования RegAam.exe
Реализация программного кода 1С с подробными комментариями:
(код описанный ниже, теоретический может быть переписан под любой язык программирования поддерживающий работу с COM объектами, так как по сути вызывает стандартные методы библиотеки RabbitMQ.Client.dll, ПримерыAPI, ОписаниеAPI)
// выполнить тест отправки и получения сообщения через RabbitMQ
&НаСервере
Процедура ВыполнитьТестНаСервере()
// создать новый COM объект RabbitMQ Client Factory
Попытка
ФабрикаAMQP = Новый COMОбъект("RabbitMQ.Client.ConnectionFactory");
Исключение
Сообщить(ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат;
КонецПопытки;
// установим параметры подключения
ФабрикаAMQP.HostName = АдресСервера;
ФабрикаAMQP.UserName = ИмяПользователя;
ФабрикаAMQP.Password = Пароль;
ФабрикаAMQP.Port = Порт;
ФабрикаAMQP.VirtualHost = Хост;
// попытка подключится
Попытка
Соединение = ФабрикаAMQP.CreateConnection();
Исключение
Сообщить(ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат;
КонецПопытки;
// создать бызовые СОМ для работы с серврером RabbitMQ
Модель = Соединение.CreateModel();
ПараметрыОтправки = Модель.CreateBasicProperties();
// установим параметры обмена
ПараметрыОбмена = Новый Структура("ИмяМаршрута, ИмяОчереди, ИмяОбмена");
ЗаполнитьЗначенияСвойств(ПараметрыОбмена, ЭтаФорма);
////////////////////////////////////////////////////////////////////////////////////////////////////
// проверить наличие обмена и очреди
// если данные введены неверно то получим исключение
Попытка
Модель.ExchangeDeclarePassive(ПараметрыОбмена.ИмяОбмена);
Модель.QueueDeclarePassive(ПараметрыОбмена.ИмяОчереди);
Исключение
Сообщить(ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат;
КонецПопытки;
// подготовить сообщение
// дело в том, что метод BasicPublish() принемает в качестве сообщения только массив байтов (COMSafeArray - VT_UI1)
// по этому соберем его !!!
ПотокВПамяти = ПолучитьДвоичныеДанныеИзСтроки(Сообщение, Кодировка).ОткрытьПотокДляЧтения();
ЧтениеДанных = Новый ЧтениеДанных(ПотокВПамяти, Кодировка);
СтрокаSafeArray = Новый COMSafeArray("VT_UI1", ПотокВПамяти.Размер());
Пока ПотокВПамяти.ТекущаяПозиция() < ПотокВПамяти.Размер() Цикл
Позиция = ПотокВПамяти.ТекущаяПозиция();
СтрокаSafeArray.SetValue(Позиция, ЧтениеДанных.ПрочитатьБайт());
КонецЦикла;
ЧтениеДанных.Закрыть();
ПотокВПамяти.Закрыть();
// собрали СтрокаSafeArray!
// подготовим параметры для отправки
ПараметрыОтправки.AppId = "Любимый 1С!"; // кто отправитель?
ПараметрыОтправки.ContentType = "text/plain"; // тип передоваемых данных
ПараметрыОтправки.DeliveryMode = 2; // 1 - хранить сообщение в ОЗУ сервера, 2 - хранить сообщение на диске сервера
ПараметрыОтправки.CorrelationId = Строка(Новый УникальныйИдентификатор); // - id сообщения
// вызвать метод отправки
// (помещать BasicPublish() в попытку нет смысла,
// он не вызыват исключений никогда, если типы передаваемых значений правельные)
Модель.BasicPublish(ПараметрыОбмена.ИмяОбмена, ПараметрыОбмена.ИмяМаршрута, False, ПараметрыОтправки, СтрокаSafeArray);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// проверить есть ли сообщения в очереди
Если НЕ Модель.MessageCount(ПараметрыОбмена.ИмяОчереди) Тогда
Возврат;
КонецЕсли;
// прочитаем одно сообщение из очереди
Результат = Модель.BasicGet(ПараметрыОбмена.ИмяОчереди, Ложь); // второй параметр делает BasicAck() - сразу, лучше сделать его потом
Если Результат = Неопределено ИЛИ Результат = NULL Тогда
Возврат;
КонецЕсли;
// получим массив байтов (оно же сообщение)
ОтветSafeArray = Результат.Body;
// получим параметры ответа
// это тоже самое что и ПараметрыОтправки только сейчас мы их получим обратно
ПараметрыОтвета = Результат.BasicProperties();
Сообщить(ПараметрыОтвета.AppID); // выведет того кто сообщение в Rabbit отправил - "Любимый 1С!"
// Тег доствки - число, позволяет удалить сообщение из очереди после приема
ТегДоставкиВПределахСессии = Результат.DeliveryTag;
// выпонить ответ об прочтении сообщения
// (без ответа оно не будет удалено и останеться в очереди)
Модель.BasicAck(ТегДоставкиВПределахСессии, false);
// преобразуем массив байтов в строку
ПотокВПамяти = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(ПотокВПамяти, Кодировка);
Для Каждого Байт Из ОтветSafeArray.Выгрузить() Цикл
ЗаписьДанных.ЗаписатьБайт(Байт);
КонецЦикла;
ЗаписьДанных.Закрыть();
Ответ = ПолучитьСтрокуИзДвоичныхДанных(ПотокВПамяти.ЗакрытьИПолучитьДвоичныеДанные(), Кодировка);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// закртыть соединение
Модель.Close();
Соединение.Close();
КонецПроцедуры
Описание общих этапов алгоритма:
- Получение зарегистрированного COM объекта из стандартной сборки DotNet - RabbitMQ.Client;
- Создание соединения с сервером;
- Проверка правильности заполнения "Параметров обмена";
- Подготовка сообщения для отправки;
- Отправка сообщения;
- Проверка наличия сообщений для прочтения;
- Чтение сообщения;
- Преобразование сообщения в текст.
Плюсы использования:
- Реализация полностью OpenSource;
- Нет никакого платного либо самописного коннектора;
- Можно использовать и обновлять официальную библиотеку RabbitMQ.Client.dll.
Описание дополнительных способов кодирования и декодирования:
- Помимо манипуляции с байтами(описано тут) при помощи потоков можно еще использовать стандартный COM Объект "System.Text.UTF8Encoding".
Пример:
UTF8Encoding = Новый COMОбъект("System.Text.UTF8Encoding");
СтрокаSafeArray = UTF8Encoding.GetBytes_4("Hello world!");
Строка = UTF8Encoding.GetString(СтрокаSafeArray);
Но тогда мы ограничиваем себя исключительно кодировкой UTF-8.
-
Еще можно использовать стандартные возможности платформы "Символ()" и "КодСимвола()", для сборки и разборки COMSafeArray. Но тогда мы ограничимся символами чей код не более 255 (как бы ASCII получается), потому что массив типа "VT_UI1" имеет однобайтовые ячейки.
P.S. так как обработка грубо говоря состоит из одной процедуры, выкладывать ее сюда не вижу смысла.