Интеграция решений на 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 не будет опубликован. Обязательные поля помечены *