Реализация протестирована на 1С 8.3.12.1714 (x64).
WARNING
Данная статья не претендует на оригинальность и не является конечным решением. Подходы решения задач и примеры программного кода несут исключительно обучающий характер. |
В больших компаниях и холдингах не редкое явление встретить огромное количество интеграционных потоков. Бывают ситуации, когда эти потоки делались по факту потребности и на скорую руку. Решением для возможности управления такими потоками являются сервисы (службы или брокеры) обмена.
Некоторые плюсы использования единого сервера обмена:
- Один или несколько стандартных протоколов обмена данными;
- Возможность построить карту маршрутов передаваемых данных.
На данный момент один из бесплатных и популярных решений — сервис (отдельный сервер или служба) RabbitMQ. Данный сервис имеет множество библиотек под самые разные языки программирования за исключением 1С. Посмотреть подробное описание и возможности самого сервиса можно на его официальном сайте RabbitMQ.com
Итак приступим к "Hello world!" интеграции RabbitMQ и 1С!
Что нам нужно:
- Платформа 1С v8.3.*;
- Сервер RabbitMQ с настроенным обменом и пользователем для подключения;
- Стандартная библиотека 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();
КонецПроцедуры
Описание общих этапов алгоритма:
- Получение зарегистрированного 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. так как обработка грубо говоря состоит из одной процедуры, выкладывать ее сюда не вижу смысла.
Хм, никогда не сталкивался с такими системами (как разработчик). Скажите пожалуйста, а вы этот RabbitMQ внедрили у себя в проекте (в конторе), или просто изучали возможность интеграции? Какие результаты от внедрения, решило ли это какие-то проблемы(если таковые были, конечно)?
(1)
— изучать нечто долго и упорно с риском, что
— его блокнут;
— оно перестанет работать;
— захочет денег.
Так можно и свой сервер развернуть.
Я читал, как на основании таких запросов получали моментально данные по чекам (только на кассе закрывался чек, тут же летел в RabbitMQ, а оттуда уже в 1С, в итоге продажи приходили в Центр практически онлайн, причем через самое плохое интернет-подключение и без УРБД.
(2)
Внедрение RabbitMQ, да это был проект,
Решило ли проблемы — да,
Результаты — положительные,
Проблемы, а куда без них)
Стоило ли оно того — однозначно да!
А вообще тут рядом живут две полезные статьи:«Что такое обмен сообщениями» и Хорошое описание масштабной интеграции через RabbitMQ
(3) Не просто развернуть!
Превратить в собственный бесплатный провайдер EDI.
(6)
За свой счёт это не бесплатно 🙂 бесплатно это за чужой счёт 🙂
(4) Думаювот статья, которую вы читали.
Вот все отлично кроме com(
(9)а как можно без него?
(10) Мне известен только один кроссплатформенный метод расширения функциональных возможностей платформы — внешние компоненты по технологии Native API.
А эту цитату из документации я просто оставлю тут:
(11) Погодите, ведь тут речь о системе, которая, как я опять же понял, обеспечивает гарантированную доставку с использованием специального механизма очереди. Хм, ну в таком случае можно через rest, grpс, наконец сокеты передавать в некий сервак(т.н. брокер) сообщения. Там они ставятся в стековую очередь, сервак выдает квитанцию что типа получил и зарегал сообщение (все это в рамках одной транзакции). Ну и всё, а далее сервак примерно таким же макаром передает сообщение приемнику, а тот подтвержает через аналогичный механизм подтверждения доставки. Теперь брокер может считать это сообщение доставленным. Как-то так. И тут не нужно никаких com или ВК))) Хотя для сокетов всеж потребуется.
Получение сообщений из очереди путём регулярного опроса RabbitMQ с заданным интервалом… хм… В случае такой «Архитектуры» точно нужен RabbitMQ? Ну и как бы COMSafeArray… ну нельзя так 🙁
Добрый день. К сожалению, некогда вникать в тему. Просто подскажите плз, можно ли данной штукой отправить файл (допустим pdf) на сервер rabbitmq и получить ссылку на его открытие в браузере без каких-либо авторизаций?
(14) Вам немножко не сюда. Вам бы файлопомойку без авторизации организовать под такую задачу.
(14)Нет.
(12)Речь о добавлении поддержки протокола amqp в платформу.
(8) да, она самая
Подскажите пожалуйста, чему у Вас равны переменные:
Я задал
После строчки
Получается ошибка: Произошла исключительная ситуация (RabbitMQ.Client): None of the specified endpoints were reachable
Не знаете, в чём может быть причина?
(19)
Скорее всего проблема с номером порта:
у меня так — ФабрикаAMQP.Port 5 672 Число
Я смог повторить вашу ошибку, когда правилами файрвола закрыл напрочь этот порт:
(20) Огромное спасибо! Проблема была в номере порта. Его действительно нужно задавать числом, а не строкой.
Добрый день, пытаюсь подключиться к облачному 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, ошибка аналогична (
(22)номер порта очень странный у вас.
С чего вы вообще взяли эти цифры?
(23)
Может что то неправильно настроила изначально . Такие настройки в облачном кролике
(25) попробуйте порт по умолчанию 5 672
(25)Проверил, у меня в облаке как ни странно тоже указан порт 1883 но работает нормально только по 5 672
(27) спасибо огромное!! вначале вывалилась какая-то другая ошибка, но в итоге все заработало!!!
Добрый день! При попытке подключения к фабрике
ФабрикаAMQP = Новый COMОбъект(«RabbitMQ.Client.ConnectionFactory»);
возникает ошибка: «Класс не зарегистрирован!» Кто-нибудь сталкивался с такой проблемой?
(29) Значит RabbitMQ.Client.dll не зарегистрирован в системе.
(30) Компоненту регистрировала
Для x64 WindowsMicrosoft.NETFramework64v4.0.30319RegAsm.exe /codebase <пусть до распакованной RabbitMQ.Client.dll>
И файл Microsoft.Diagnostics.Tracing.EventSource.dll положила в ту же папку, где и лежит RabbitMQ.Client.dll.
(31)странно
Ссылка
Есть в сети утилита показывающая все зарегистрированные библиотеки RegDllView
Проверьте зарегистрировался ли тип: RabbitMQ.client или что-то на подобие того.
(32) Спасибо, библиотека зарегистрирована.
(18) О, дык это ж я писал 🙂
(13) Где ком, там и safearray, чего удивительного. Бяка? Бяка. Зато бесплатно и без этих ваших сиплюсплюсов. (Про добавленный гемор с RegAsm умолчим, к тому же Labotamy все сказал выше)
(14) Нет.
(33) А теперь не забывайте повторять на каждом сервере 1С и в случае переездов с машины на машину. 🙂
(34) о как, рад познакомится с автором, статья очень зашла, жаль поздно узнал, на одном проекте как раз такое нужно было.
(37) некропостер 🙂
Коллеги, Дмитрий, приветствую!
Благодаря Вашему примеру смогли подключиться к Раббиту. Создали в нем очередь. А дальше просьба пояснить, чему должны быть равны параметры «ИмяМаршрута» и «ИмяОбмена». Я не нашел таких переменных в Раббит.
Ваш пример:
// установим параметры обмена
ПараметрыОбмена = Новый Структура(«ИмяМаршрута, ИмяОчереди, ИмяОбмена»);
Пока для меня только очевиден один параметр — ИмяОчереди
Заранее благодарен за помощь!
и подскажите пожалуйста, где параметры «ИмяМаршрута» и «ИмяОбмена» прописываются в Реббит?
(40)
Имя маршрута -> Routing key;
ИмяОбмена -> Exchange;
Спасибо большое за статью! Реализовываем обмен данными 1С — сайт. Рэббит это просто панацея.
Есть одно замечание к коду 1С, а именно сериализация сообщения в ComSafeArray. Код из статьи работает очень медленно на больших объемах данных (может занимать до 90% всего времени выполнения обмена), также может вызывать ошибки выделения памяти.
Переписал на этот, может кому пригодятся:
(43) Спасибо за информацию!