RabbitMQ + 1С. Быстрый старт


Внешняя компонента для отправки сообщения из 1С в кролика. Сервис прослушивания и перенаправления сообщений из кролика в http или web-сервис.

Здравствуйте.

Описанные ниже мысленные изыскания были навеяны статями сильвер булетов и прочих товарищей по использованию очередей на основе RabbitMQ. Описывать, что, как и зачем использовать этот брокер мессенджер не вижу смысла — в предыдущих статьях все хорошо описано. Здесь я опишу сугубо практику применения.

Причина использования очередей — зоопарк систем на новом месте работы, обменивающихся друг с другом информацией всевозможными спосособами. Как правило, способами медленными не информативными и отнимающими тонны рабочего времени отдела на собственную поддержку.

Основной обмен происходил файлами через файлопомойку без какого-либо оповещения об обработке входящих данных и т.п. Зачастую получалась ситуация, что данные отправлены, что-то пошло не так, данные не получены системой получателем, но система отправитель об этом ни сном ни духом. В результате получаем бесконечный поток писем в поддержку "Где наши платежи/заказы/etc?! В рознице деньги оприходовали/заказ провели,, а на сайт они не попали" и т.п. На диагностику проблемы уходило безумное количество времени, потому что, чтобы понять причину сотрудник поддержки:

  1. Смотрел лог отправки в системе отправителе.
  2. Проверял, что файла нет в папке получателе. (данные не стоят в очереди)
  3. Проверял, что файл есть в архивной папке системы получателя (данные получены и обработаны)
  4. Проверял данные самого файла.
  5. Проверял логику формирования данных.

И это на каждое письмо. Теперь:

  1. Открывает лог отправки данных в системе отправителе и смотрит в нем все статусы пакета данных (дата отправки, дата получения, ссылка на текст отправленных данных).
  2. Проверяет логику формирования данных.

В итоге получаем значительную экономию времени. Думаю, у каждого таких примеров наберется немало.

Итак, выбор пал на RabbitMQ (далее, Кролик). К сожалению, я не обнаружил сразу описания HTTP интерфейса Кролика, а на глаза попала библиотека для C# на самом сайте Кролика. Было решено в исследовательских целях написать COM-компоненту на коленке и небольшой сервис (службу) для обслуживания входящих сообщений и направлений их к адресату.

Компоненту можно регистрировать, как 32, так и 64 битную, зависит от того, какой версией регасма будете регистрировать. В итоге для работы сервера с ней нет необходимости оборачивать ее в COM+. Регистрация компоненты проходит regasm’ом, а не regsvr32:

64 бита:

C:WindowsMicrosoft.NETFramework64v4.0.30319
egasm /codebase <Путь к файлу компоненты>

32 бита:

C:WindowsMicrosoft.NETFrameworkv4.0.30319
egasm /codebase <Путь к файлу компоненты>

Не знаю, обязателен ли ключ /codebase, но я регистрировал с ним. Версию .NET фреймворка используйте последнюю свою.

Использование компоненты отправки сообщений в 1С:

Объект = НОВЫЙ COMОбъект("AMQPSend.COM"); //Создаем COM  объект
Объект.Connect(ИмяСервераКролика, Логин, Пароль);// Подключаемся к серверу Кролика
Результат = Объект.Send(ИмяУзла, КлючМаршрутизации, СтрокаДанных); //Отправляем данные
Объект.Close();//Закрываем соединение к серверу Кролика

У объекта только одна функция Send. Имя узла — Exchange Кролика, КлючМаршрутизации — routingKey, СтрокаДанных — строковое представление передаваемых данных.

В случае успешной технической отправки данных функция возвращает "0", либо описание исключения в случае технической неудачи. Проверки прав на помещение данных в указанный узел у пользователя, под которым компонента подключилась к Кролику не писал.

Таким образом, отправлять 1С данные в Кролика указанному получателю мы научились. Далее пришло время получать это все оттуда. Так же на C# была написана простенькая служба, которая создает подписчиков на определенные ключи маршрутизации в определенных узлах и переправляет эти данные получателям.

Установка службы:

C:WindowsMicrosoft.NETFramework64v4.0.30319installutil <Путь к exe файлу службы>

Удаление

C:WindowsMicrosoft.NETFramework64v4.0.30319installutil /u <Путь к exe файлу службы>

Получателям 1С служба умеет отправлять данные двумя способами — в HTTP-сервис или в Web-сервис. Служба может поднимать множественное количество подписчиков на разные ключи маршрутизации и узлы, переправляя данные в http и/или web-сервис.

Пример конфигурационного файла (находится в папке с экзешником)

<consumers>
<consumer>
<host>[Имя сервера Кролика]</host>
<routingKey>[Ключ маршрутизации]</routingKey>
<exchange>[Узел Кролика]</exchange>
<httpUrl>[URL для переправки данных]</httpUrl>
<soapUrl>[URL для POST запроса в WEB-сервис]</soapUrl>
<soapAction>[Функция, вызываемая в WEB-сервисе]</soapAction>
<user>[Пользователь Кролика]</user>
<password>[Пароль пользователя Кролика]</password>
<prefetchCount>[Число сообщений, считываемых службой из очереди Кролика]</prefetchCount>
<soapUrlExample>http://domain.com/base/ws/ESBExchange</soapUrlExample>
<soapActionExample>http://domain.com/base/ws/ESBExchange/UploadData</soapActionExample>
</consumer>
</consumers>

 

Credentials для запросов в 1С не ставил, т.к. у меня предполагается отправка все внутри сети. prefetchCount — это уже начались игры разума по вопросам распараллеливания переправки сообщений в 1С. Поднимать несколько подписчиковна одну очередь или забирать в экземпляр слушателя несколько сообщений и распараллеливать в нем — дальше бесконечное поле для ускорения и т.п. prefetchCount определяет сколько сообщений забирает на обработку подписчик себе из очереди Кролика.

В моем случае в одну очередь в день попадет до 30к сообщений — последовательно они все успешно отрабатывают, потому я оставляю этот параметр равный 1. Без создания тасков внутри подписчика увеличение данного числа ничего не даст.

Дополнительно настраивается еще один файл SOAPquery.txt:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="ns">
<soap:Header/>
<soap:Body>
<ns:UploadData>
<ns:Data>%data%</ns:Data>
</ns:UploadData>
</soap:Body>
</soap:Envelope>

В этот файл я добавил шаблон запроса в Web-сервис. Некрасиво, но удобно. Находить удобные библиотеки для работы с SOAP мне было лень. Текст %data% в шаблоне заменяется на передаваемые текстовые данные, потому лучше заворачивайте их в строку Base64, чтобы не заниматься экранированием и прочими радостями. Формат файла удобно получить из SoapUI, скормив ему WSDL вашего сервиса.

Конфиг файл и шаблон SOAP запрос мы настроили, можно запускать службу. Служба при старте создаст столько подписчиков, сколько сконфигурируете в конфиге, подписчики подключаться к узлам, Создадут временные очереди, привязанные к указанным ключам маршрутизации и начнут переправлять данные.

Теперь прием сообщения в 1С:

[Блок получения данных в HTTP/WEB сервисе]
[Парсинг данных и необходимые действия]

Объект = НОВЫЙ COMОбъект("AMQPSend.COM");
Объект.Connect(ИмяСервераКроликаДляОтвета, Логин, Пароль);
Результат = Объект.Send(ИмяУзлаДляОтвета, КлючМаршрутизацииДляОтвета, ДанныеСОтветом);
Объект.Close();

В начале производим все необходимые данные в соответствии с логикой загрузки. Далее из входящих данных получаем идентификатор, данные Кролика этого пакета данных (мы ведь их передаем вместе с данными, верно?) и передаем точно так же обратно в Кролик. Теперь наша передача сообщения в другую систему выглядит как:

  1. Система 1 формирует передаваемый пакет, дополняя его данными для отправки уведомления о доставке.
  2. Система 1 отправляет его в очередь Системы 2.
  3. Система 2 принимает пакет из очереди и обрабатывает его.
  4. Система 2 отправляет подтверждение в очередь, настройки которой пришли во входящем пакете.

Таким образом, обеим системам неважно, кто и каким образом отправляет сообщение (если это важно для бизнес-логики, то эта обработка будет на стороне каждой из систем на основании входящих данных). Схема чрезвычайно легко масштабируется. В очередь/узел Системы 2 могут посылать сообщения любые другие системы и ответ в эти системы будет точно таким же, как и в Систему 1.

Так что пробуйте, видоизменяйте, унифицируйте.

К публикации приложены как есть:

  • COM компонента для отправки сообщения из 1С
  • Служба подписчиков
  • Исходники и того, и другого.

Экономика:

Потрачено — 5 вечеров свободного времени.

Получено — высвобождение 3-4 человеко часа в день, что в условиях кадрового дефицита — бесценно 🙂

 

PS Заранее предупрежу — я не .NET разработчик. Писал все на знаниях, оставшихся после института, и туториалах из интернета. Достигать максимального уровня абстракции не собирался. Все выложено исключительно для пробных шагов в этом направлении остальных участников сообщества. Код открыт — можно и нужно модифицировать, как угодно.

35 Comments

  1. kraynev-navi

    Я бы все-таки обратил внимание на разграничение доступа. Нужна хотя бы минимальная защита. Любая копия базы, снятая с рабочей, начнет слать в рэббит «левые» сообщения. Сэкономленные человеко-часы будут перечеркнуты «случайными» отправками платежных документов из баз разработчиков.

    Reply
  2. Goleff74

    (1)

    Ну, это уже на уровне кода конфигураций контролировать. Либо ключи маршрутизации создавать вида <Строка базы 1><Строка базы 2>, или наплодить несколько узлов с именами Система отправителя в виде строк этих баз, и оттуда уже ключами маршрутизации перенаправлять в узел получателя. Простор для творчества.

    А еще лучше не регистрировать компоненту на сервере разработчиков 🙂

    Reply
  3. Goleff74

    (4)

    Вы не должны

    Reply
  4. nixel

    (3) граммар наци живёт в ЖЖ. проследуйте туда.

    Reply
  5. Новиков

    Приветствую!

    К Вашей цитате:

    я не обнаружил сразу описания HTTP интерфейса Кролика

    Не могли бы пруфом поделиться? Спасибо!

    Reply
  6. Goleff74
  7. Evil Beaver

    (8) ой не, HTTP-API это прям совсем медленно, тем более, что по ссылке — администраторский API. Да, он позволяет отправлять, но это костылище. Используйте родных клиентов, работающих по протоколу AMQP. Например, наш <скрытая реклама детектед> 🙂

    Reply
  8. Evil Beaver

    (9) дело в управлении этим хранилищем файлов. Рано или поздно, автоматизируя папку обмена и настраивая к ней доступ, вы напишете сервер очередей. Так может сразу начать с него?

    Reply
  9. Evil Beaver

    (3) Бывает же…

    Reply
  10. user634257_mryzhov

    (11) На этапе когда нужен обмен между двумя базами — сервер очередей избыточен.

    В описанном примере это именно такой случай — из-за применения Раббита осталось только еще больше не проработанных вопросов, которые до этого при обычном файловом обмене были решены. То есть мы существенно увеличили сложность решения (отдельный сервер очередей, внешние компоненты для обмена, ХТТП-сервис), функциональность та же, а качество хуже.

    Rabbit — не серебрянная пуля:)

    Я не рассматриваю случаи когда у нас сотни потребителей, распаралеленные очереди, несколько серверов очередей связанных между собой и разнородные среды для интеграции — тогда да я обеими руками за Раббит)

    Reply
  11. 1cProfit

    у кого-то есть готовая компонента для раббита ?

    Reply
  12. Goleff74

    (14)

    Прикреплена к статье вместе с исходниками.

    Reply
  13. baracuda

    Правильно понимаю, что в основном это применяется для разработки сервисных шин данных? ESB?

    Где еще можно применить связку?

    Reply
  14. Goleff74

    (16)

    Например, распределение пиковой нагрузки. Пришло вам 100500 запросов в секунду и веб-сервер отказался работать. Засунули эти 100500 в очередь, сами указали сколько потоков параллельно обрабатываются из Кролика и в колбэк функцию вернули результаты запроса.

    Reply
  15. baracuda

    (17) ясно. спасибо за пример.

    Reply
  16. zarucheisky

    (0) ActiveMQ имеет встроенный REST API 🙂

    Вовсе необязательно подсовывать кролика где ни попадя.

    Reply
  17. MherArsh

    А можно как то подписаться на события кролика?

    Reply
  18. Goleff74

    (20)

    Дописывать / Переписывать приложенную службу.

    Reply
  19. MherArsh

    (21) не реализовано, понял, но вот в чем вопрос, как в контексте сервера можно подписаться на события, это не регламентное задание где периодически что то вызывается, все, а иметь запущенного клиента тоже не очень хорошо… у меня варианты кончились )

    Reply
  20. Goleff74

    (22)

    Не понимаю вашей боли. Давайте пример, чтоли, что вы хотите получить.

    Reply
  21. MherArsh

    (23) хочу обработать события по конкретному каналу из кролика, как это сделать в контексте севера 1С, без запущенных клиентов 1С.

    Reply
  22. Goleff74

    (24)

    При получении сообщения служба посылает http-запрос по указанному вами адресу. Клиентов никаких загружать не надо. Или вы под событиями понимаете что-то иное?

    Reply
  23. MherArsh

    (25) я сейчас не говорю про http, его нет, есть RabbitMQ, как 1С может получать данные из какой то очереди, при их появлении.

    Reply
  24. Goleff74

    (26)

    Эм. Вы ведь прочитали публикацию?

    Получателям 1С служба умеет отправлять данные двумя способами — в HTTP-сервис или в Web-сервис. Служба может поднимать множественное количество подписчиков на разные ключи маршрутизации и узлы, переправляя данные в http и/или web-сервис.

    Не можете поднять веб-сервер, допишите службу, пусть напрямую в СКЛ пушит данные или КОМ-соединение поднимает и в него пуляет.

    Reply
  25. artbear

    (26) Обрати внимание на платную компоненту

    https://silverbulleters.org/rabbitmq

    Серьезно и быстро

    Reply
  26. artbear

    (28) Работает напрямую с Реббитом через нативную компоненту 1С

    Reply
  27. MherArsh

    (27) я читал, и все понял, и задал вопрос, может не по конкретной это теме но это про очереди+1с в целом, как решить вопрос я знаю, просто хочется послушать мысли умных людей по этому вопросу 🙂

    Reply
  28. MherArsh

    (29) спасибо за ссылку, не вариант

    Reply
  29. barelpro

    (10) Коллега, можно подробней плз, за счет чего прямые HTTP-запросы из 1С к RMQ медленнее вашей ВК? Сравнительные замеры проводили?

    Reply
  30. leka99

    Добрый день.

    При подключении к серверу кроля с сервера (Windows server 2008 R2) выдаёт ошибку — {ВнешняяОбработка.ПостановкаВОчередьКролика.МодульОбъекта(27)}: Ошибка при вызове метода контекста (Connect): Произошла исключительная ситуация (AMQPSending): Не удалось загрузить файл или сборку «RabbitMQ.Client, Version=5.0.0.0, Culture=neutral, PublicKeyToken=89e7d7c5feba84ce» либо одну из их зависимостей. Не удается найти указанный файл.

    Подключение с компа с Windows 7 работает.

    Не подскажите направление — в чем может быть проблема ?

    Reply
  31. ardarik

    А где в конфиг файле указывается лого пасы web-сервиса?

    Reply
  32. spogo

    Коллеги, объясните мне убогому пожалуйста.

    Правильно я понимаю: Когда на Кролик приходит некое сообщение оно переправляется слушателю.

    Слушать пусть будет вебсервис 1С. я не понимаю где я должен указать логин и пароль вебсервиса 1С чтобы кролик в него послал это некое сообщение.

    Reply
  33. seregasame

    webClient.Headers.Add(«Authorization: Basic», «логин:пароль»); логин:пароль от 1С нужно в басе64

    или

    soapRequest.Headers.Add(«Authorization: Basic», «логин:пароль»);

    в зависимости от того что вы используете, http или soap

    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(«логин:пароль»);
    webClient.Headers.Add(«Authorization: Basic», System.Convert.ToBase64String(plainTextBytes));
    

    или

    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(«логин:пароль»);
    soapRequest.Headers.Add(«Authorization: Basic», System.Convert.ToBase64String(plainTextBytes));
    
    Reply
  34. seregasame

    можете самостоятельно добавить в файл Consumers.xml в тег Consumer пару полей user1c и password1c.

    добавить их же в класс MyConsumer и в его конструктор.

    добавить в ServiceMQConsumer в методе InitialiazeConsumers их заполнение.

    при отправке запроса в хеадер добавлять логин и пасс из конкретного экземпляра mqConsumer

    Reply
  35. seregasame

    С хеадерами правильно вот так:

    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(this.user1c + «:» + this.password1c);
    webClient.Headers.Add(«Authorization», «Basic » + System.Convert.ToBase64String(plainTextBytes));
    

    и

    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(this.user1c + «:» + this.password1c);
    soapRequest.Headers.Add(«Authorization», «Basic » + System.Convert.ToBase64String(plainTextBytes));
    
    Reply

Leave a Comment

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