Интеграция решений на 1С и сервиса обмена данными RabbitMQ








«Hello world» из 1С на сервер RabbitMQ и обратно. Полностью открытый код 1С!
Реализация протестирована на 1С 8.3.12.1714 (x64).

WARNING

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

 

В больших компаниях и холдингах не редкое явление встретить огромное количество интеграционных потоков. Бывают ситуации, когда эти потоки делались по факту потребности и на скорую руку. Решением для возможности управления такими потоками являются сервисы (службы или брокеры) обмена.

Некоторые плюсы использования единого сервера обмена:

  • Один или несколько стандартных протоколов обмена данными;
  • Возможность построить карту маршрутов передаваемых данных.

На данный момент один из бесплатных и популярных решений — сервис (отдельный сервер или служба) RabbitMQ. Данный сервис имеет множество библиотек под самые разные языки программирования за исключением 1С. Посмотреть подробное описание и возможности самого сервиса можно на его официальном сайте RabbitMQ.com

Итак приступим к "Hello world!" интеграции RabbitMQ и 1С!


Что нам нужно:

  1. Платформа 1С v8.3.*;
  2. Сервер RabbitMQ с настроенным обменом и пользователем для подключения;
  3. Стандартная библиотека RabbitMQ Client для DotNet.

 

С первым пунктом все ясно, но со второго возникают сложности:

Бесплатный экземпляр сервера RabbitMQ (а точнее его хост) можно получить на CloudAMQP.com. Нужно просто зарегистрироваться, создать новый "инстанс" и новый хост с правами админа готов.

 

 Немного о предоставляемой CloudAMQP бесплатной услуге (тарифный план Little Lemur)

 

Библиотека RabbitMQ Client для DotNet:

Где получить:

  • Сама библиотека (v5) доступна в репозитории NuGet;

  • Имеет единственную зависимость Microsoft.Diagnostics.Tracing.EventSource.Redist v1.1.28

Как установить:

  • Можно установить через стандартные менеджеры пактов NuGet или DotNet;

  • Можно скачать оба пакета NuPkg, извлечь нужные DLL и зарегистрировать в windows через RegAsm.exe:

    • Для x32 WindowsMicrosoft.NETFrameworkv4.0.30319RegAsm.exe /codebase <пусть до распакованной RabbitMQ.Client.dll>

    • Для x64 WindowsMicrosoft.NETFramework64v4.0.30319RegAsm.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();

КонецПроцедуры

Описание общих этапов алгоритма:

  1. Получение зарегистрированного COM объекта из стандартной сборки DotNet — RabbitMQ.Client;
  2. Создание соединения с сервером;
  3. Проверка правильности заполнения "Параметров обмена";
  4. Подготовка сообщения для отправки;
  5. Отправка сообщения;
  6. Проверка наличия сообщений для прочтения;
  7. Чтение сообщения;
  8. Преобразование сообщения в текст.

Плюсы использования:

  • Реализация полностью OpenSource;
  • Нет никакого платного либо самописного коннектора;
  • Можно использовать и обновлять официальную библиотеку RabbitMQ.Client.dll.

Описание дополнительных способов кодирования и декодирования:

  1. Помимо манипуляции с байтами(описано тут) при помощи потоков можно еще использовать стандартный COM Объект "System.Text.UTF8Encoding".
    Пример:

    UTF8Encoding = Новый COMОбъект("System.Text.UTF8Encoding");
    СтрокаSafeArray = UTF8Encoding.GetBytes_4("Hello world!");
    Строка = UTF8Encoding.GetString(СтрокаSafeArray);
    

    Но тогда мы ограничиваем себя исключительно кодировкой UTF-8.

  2. Еще можно использовать стандартные возможности платформы "Символ()" и "КодСимвола()", для сборки и разборки COMSafeArray. Но тогда мы ограничимся символами чей код не более 255 (как бы ASCII получается), потому что массив типа "VT_UI1" имеет однобайтовые ячейки.

P.S. так как обработка грубо говоря состоит из одной процедуры, выкладывать ее сюда не вижу смысла.

43 Comments

  1. Идальго

    Хм, никогда не сталкивался с такими системами (как разработчик). Скажите пожалуйста, а вы этот RabbitMQ внедрили у себя в проекте (в конторе), или просто изучали возможность интеграции? Какие результаты от внедрения, решило ли это какие-то проблемы(если таковые были, конечно)?

    Reply
  2. TODD22

    (1)

    — сливать свои данные куда-попало;

    — изучать нечто долго и упорно с риском, что

    — его блокнут;

    — оно перестанет работать;

    — захочет денег.

    Так можно и свой сервер развернуть.

    Reply
  3. Senator_I

    Я читал, как на основании таких запросов получали моментально данные по чекам (только на кассе закрывался чек, тут же летел в RabbitMQ, а оттуда уже в 1С, в итоге продажи приходили в Центр практически онлайн, причем через самое плохое интернет-подключение и без УРБД.

    Reply
  4. Eret1k

    (2)

    Внедрение RabbitMQ, да это был проект,

    Решило ли проблемы — да,

    Результаты — положительные,

    Проблемы, а куда без них)

    Стоило ли оно того — однозначно да!

    А вообще тут рядом живут две полезные статьи: «Что такое обмен сообщениями» и Хорошое описание масштабной интеграции через RabbitMQ

    Reply
  5. Eret1k

    (3) Не просто развернуть!

    Превратить в собственный бесплатный провайдер EDI.

    Reply
  6. TODD22

    (6)

    Превратить в собственный бесплатный провайдер EDI.

    За свой счёт это не бесплатно 🙂 бесплатно это за чужой счёт 🙂

    Reply
  7. Infactum

    (4) Думаю вот статья, которую вы читали.

    Reply
  8. Labotamy

    Вот все отлично кроме com(

    Reply
  9. spogo

    (9)а как можно без него?

    Reply
  10. Labotamy

    (10) Мне известен только один кроссплатформенный метод расширения функциональных возможностей платформы — внешние компоненты по технологии Native API.

    А эту цитату из документации я просто оставлю тут:

    При работе на сервере «1С:Предприятия» допустимо использовать только компоненты, разработанные по технологии Native API, которые могут быть как отдельными файлами, так и упакованными в специальные zip-архивы.
    Reply
  11. Идальго

    (11) Погодите, ведь тут речь о системе, которая, как я опять же понял, обеспечивает гарантированную доставку с использованием специального механизма очереди. Хм, ну в таком случае можно через rest, grpс, наконец сокеты передавать в некий сервак(т.н. брокер) сообщения. Там они ставятся в стековую очередь, сервак выдает квитанцию что типа получил и зарегал сообщение (все это в рамках одной транзакции). Ну и всё, а далее сервак примерно таким же макаром передает сообщение приемнику, а тот подтвержает через аналогичный механизм подтверждения доставки. Теперь брокер может считать это сообщение доставленным. Как-то так. И тут не нужно никаких com или ВК))) Хотя для сокетов всеж потребуется.

    Reply
  12. comol

    Получение сообщений из очереди путём регулярного опроса RabbitMQ с заданным интервалом… хм… В случае такой «Архитектуры» точно нужен RabbitMQ? Ну и как бы COMSafeArray… ну нельзя так 🙁

    Reply
  13. d.zhukov

    Добрый день. К сожалению, некогда вникать в тему. Просто подскажите плз, можно ли данной штукой отправить файл (допустим pdf) на сервер rabbitmq и получить ссылку на его открытие в браузере без каких-либо авторизаций?

    Reply
  14. GreenDragon

    (14) Вам немножко не сюда. Вам бы файлопомойку без авторизации организовать под такую задачу.

    Reply
  15. Labotamy

    (14)Нет.

    Reply
  16. Labotamy

    (12)Речь о добавлении поддержки протокола amqp в платформу.

    Reply
  17. Senator_I

    (8) да, она самая

    Reply
  18. EvgeTrofi

    Подскажите пожалуйста, чему у Вас равны переменные:

     ФабрикаAMQP.HostName = АдресСервера;
    ФабрикаAMQP.UserName = ИмяПользователя;
    ФабрикаAMQP.Port = Порт;
    ФабрикаAMQP.VirtualHost = Хост;
    

    Я задал

     АдресСервера = «zebra.rmq.cloudamqp.com»;
    ИмяПользователя = «kibgbbpf»;
    Порт = «1883»;
    Хост = «kibgbbpf»;
    

    После строчки

    Соединение = ФабрикаAMQP.CreateConnection();

    Получается ошибка: Произошла исключительная ситуация (RabbitMQ.Client): None of the specified endpoints were reachable

    Не знаете, в чём может быть причина?

    Reply
  19. Eret1k

    (19)

    Скорее всего проблема с номером порта:

    у меня так — ФабрикаAMQP.Port 5 672 Число

    Я смог повторить вашу ошибку, когда правилами файрвола закрыл напрочь этот порт:

    {ВнешняяОбработка.HelloWorldForRabbitMQ.Форма.Форма.Форма(46)}: Ошибка при вызове метода контекста (CreateConnection)
    Соединение = ФабрикаAMQP.CreateConnection();
    по причине:
    Произошла исключительная ситуация (RabbitMQ.Client): None of the specified endpoints were reachable
    
    Reply
  20. EvgeTrofi

    (20) Огромное спасибо! Проблема была в номере порта. Его действительно нужно задавать числом, а не строкой.

    Reply
  21. dracoola

    Добрый день, пытаюсь подключиться к облачному CloudAMQP.com выпадает ошибка

    None of the specified endpoints were reachable

    ФабрикаAMQP.HostName = «toad-01.rmq.cloudamqp.com»; ФабрикаAMQP.UserName = «afaneugp»; ФабрикаAMQP.Port = 1883; ФабрикаAMQP.VirtualHost = «afaneugp»;

    Подскажите,пожалуйста, в чем может быть проблема ? Ошибка выпадает именно на моменте самого подключения. Порт задан именно числом . Пробовала и другой 8883, ошибка аналогична (

    Reply
  22. Eret1k

    (22)номер порта очень странный у вас.

    С чего вы вообще взяли эти цифры?

    Reply
  23. dracoola

    (23)

    Reply
  24. dracoola

    Может что то неправильно настроила изначально . Такие настройки в облачном кролике

    Reply
  25. Eret1k

    (25) попробуйте порт по умолчанию 5 672

    Reply
  26. Eret1k

    (25)Проверил, у меня в облаке как ни странно тоже указан порт 1883 но работает нормально только по 5 672

    Reply
  27. dracoola

    (27) спасибо огромное!! вначале вывалилась какая-то другая ошибка, но в итоге все заработало!!!

    Reply
  28. juliia1992

    Добрый день! При попытке подключения к фабрике

    ФабрикаAMQP = Новый COMОбъект(«RabbitMQ.Client.ConnectionFactory»);

    возникает ошибка: «Класс не зарегистрирован!» Кто-нибудь сталкивался с такой проблемой?

    Reply
  29. Eret1k

    (29) Значит RabbitMQ.Client.dll не зарегистрирован в системе.

    Reply
  30. juliia1992

    (30) Компоненту регистрировала

    Для x64 WindowsMicrosoft.NETFramework64v4.0.30319RegAsm.exe /codebase <пусть до распакованной RabbitMQ.Client.dll>

    И файл Microsoft.Diagnostics.Tracing.EventSource.dll положила в ту же папку, где и лежит RabbitMQ.Client.dll.

    Reply
  31. Eret1k

    (31)странно

    Есть в сети утилита показывающая все зарегистрированные библиотеки RegDllView Ссылка

    Проверьте зарегистрировался ли тип: RabbitMQ.client или что-то на подобие того.

    Reply
  32. juliia1992

    (32) Спасибо, библиотека зарегистрирована.

    Reply
  33. Evil Beaver

    (18) О, дык это ж я писал 🙂

    Reply
  34. Evil Beaver

    (13) Где ком, там и safearray, чего удивительного. Бяка? Бяка. Зато бесплатно и без этих ваших сиплюсплюсов. (Про добавленный гемор с RegAsm умолчим, к тому же Labotamy все сказал выше)

    Reply
  35. Evil Beaver

    (14) Нет.

    Reply
  36. Evil Beaver

    (33) А теперь не забывайте повторять на каждом сервере 1С и в случае переездов с машины на машину. 🙂

    Reply
  37. Senator_I

    (34) о как, рад познакомится с автором, статья очень зашла, жаль поздно узнал, на одном проекте как раз такое нужно было.

    Reply
  38. GreenDragon

    (37) некропостер 🙂

    Reply
  39. 2dnk

    Коллеги, Дмитрий, приветствую!

    Благодаря Вашему примеру смогли подключиться к Раббиту. Создали в нем очередь. А дальше просьба пояснить, чему должны быть равны параметры «ИмяМаршрута» и «ИмяОбмена». Я не нашел таких переменных в Раббит.

    Ваш пример:

    // установим параметры обмена

    ПараметрыОбмена = Новый Структура(«ИмяМаршрута, ИмяОчереди, ИмяОбмена»);

    Пока для меня только очевиден один параметр — ИмяОчереди

    Заранее благодарен за помощь!

    Reply
  40. 2dnk

    и подскажите пожалуйста, где параметры «ИмяМаршрута» и «ИмяОбмена» прописываются в Реббит?

    Reply
  41. Eret1k

    (40)

    Имя маршрута -> Routing key;

    ИмяОбмена -> Exchange;

    Reply
  42. kotlovD

    Спасибо большое за статью! Реализовываем обмен данными 1С — сайт. Рэббит это просто панацея.

    Есть одно замечание к коду 1С, а именно сериализация сообщения в ComSafeArray. Код из статьи работает очень медленно на больших объемах данных (может занимать до 90% всего времени выполнения обмена), также может вызывать ошибки выделения памяти.

    Переписал на этот, может кому пригодятся:

     Текст = Новый COMОбъект(«System.Text.UTF8Encoding»);
    СтрокаSafeArray = Текст.GetBytes_4(ДанныеДляОбмена);
    Reply
  43. 2dnk

    (43) Спасибо за информацию!

    Reply

Leave a Comment

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