Сервис очередей RabbitMQ и «1С:Предприятие 8»
Интеграция с сервисом очередей RabbitMQ

Этот набросок является продолжение предыдущей статьи 7 причин, почему интеграция стала приятной. Не упускайте ряд потрясающих возможностей. В большей части это описание боли через которую пришлось пройти на практике, используя сервисную шину данных Zato ESB и OData протокол совместно с «1С:Предприятие 8». 

Практические минусы Open Data Protocol

В 1С Предприятие 8.3.5.1068 появилась поддержка автоматического REST-сервиса. Теперь платформа может автоматически формировать REST интерфейс для всего прикладного решения.

Это был глоток свежего воздуха, все данные как на ладони бери и получай. Конечно, в скором времени появились простые и небольшие сервисы и работали они стабильно как часы. Соответственно гений инженерной мысли думает вот она: «серебряная пуля». Что ж, в порыве эйфории, один глобальный проект решено было выполнить используя: Zato ESB (что же это?) и использовать REST интерфейс OData «1С:Предприятие 8» для обмена между двумя большими системами.

Проблемы связанные с Open Data Protocol

Для реализации проекта на стороне Zato ESB были нанята команда программистов питонщиков. Основной задачей команды было брать данные из «1С:Предприятие 8» по протоколу OData, а так же получать данные при помощи фоновых заданий «1С:Предприятие 8», которые, в свою очередь, передавали изменения данных необходимых для сторонней системы. На этапе технического задания все выглядело прозрачно, но во время реализации возникли проблемы:

  • работа с большими таблицами (некоторые таблицы имели более 100 млн. записей). Получить срез данных было очень проблематично, что часто вызывало падение всех служб 1С;
  • получение данных в несколько потоков. Соединения бывало, часто, зависали и лицензии заканчивались без видимой на то причины, спасал только рестарт службы «1С:Предприятие 8». Забавно было видеть несколько тысяч соединений, которые нельзя удалить;
  • танцы с бубнами и велосипедами, чтобы гарантировать доставку сообщений;
  • так как обмен работал в две стороны, возникла проблема в контроле логики и верности создания данных в «1С:Предприятие 8»;
  • при изменении бизнес-процесса одной системе, приводил к необходимости внесения изменений в Zato ESB — в итоге получилась очень большая связность;
  • команда питонщиков, по сути, реализовывала буферные копии функционала 1С конфигурации — на лицо дублирование кода;
  • ребятам питонщикам пришлось учить платформу «1С:Предприятие 8», что бы понимать как с этим работать;
  • ошибки в самом протоколе OData, что заставляло нас использовать только вышедшие обновления платформы «1С:Предприятие 8» с новой порцией ошибок, уже не связанных с OData.

Проблемы связанные с Zato ESB

  • дорогие программисты, которым пришлось постигать азы платформы 1С;
  • ограничиваемся только одним языком программирования для выполнения обменов между системами;
  • в промышленных масштабах еще рано использовать, если, конечно, у вас много лишних серверов — можно попробовать на свой страх и риск;
  • иногда сервис падал без видимой причины и докопаться до «почему?» не удалось.

Проект сейчас в режиме «заморожен» и чувствую, что вскоре будет полностью закрыт, в связи с техническими причинами описанными выше.

7 причин, почему интеграцию необходимо строить на очередях

На данном этапе были переосмыслены все ошибки, было принято решение интеграцию строить на очередях, пока видны только одни плюсы, недостатки незаметны:

  1. используя очереди RabbitMQ, мы не ограничиваем себя одним языком программирования. Мы можем использовать, в большинстве случаев, родные языки систем которые интегрируются для отправки и приема сообщений;
  2. для гарантии доставки, нам не нужно доставать бубны и велосипеды, если сообщение не может быть положено в очередь, то и транзакция будет безболезненно отменена;
  3. интеграция одной системы к многим и наоборот выполняется одной единственной настройкой роутинга в RabbitMQ;
  4. простота формата сообщений, это бинарные данные — мы используем JSON, который переводится в бинарный формат;
  5. при высокой нагрузке, на довольно слабом виртуальном сервере, в очередь были доставлены все сообщения;
  6. каждый елемент системы не связан с другими елементами другой системы и бизнес-логика не дублируется, контроль целостности данных проверяется в едином месте в той системе для которой эти данные предназначены;
  7. множество типов очередей, даже такие что позволяют выполнять remote procedure call, но нужно стараться что бы системы были минимально связаны, бизнес-логика в «1С:Предприятие 8», а рабочие места, например, это мобильное приложение или веб-сайт.

Передача сообщения из  «1С:Предприятие 8» в RabbitMQ

Для реализации этого механизма было принято решение отказаться от веб-сервиса 1С в пользу NativeAPI компоненты, так как планируется передавать большие объемы данных. Весь инструментарий, который понадобится:

Исходный код, компоненты:

using System;
using System.Text;
using RabbitMQ.Client;

namespace _1CV8Publisher
{
    public class V8Publisher
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        public string HostName { get; set; }  

        public string Exchange { get; set; }
        public string RoutingKey { get; set; }

        public string SengMessage(string message)
        {
            try
            {
                var factory = new ConnectionFactory()
                {
                    HostName = HostName,
                    UserName = UserName,
                    Password = Password
                };
                using (var connection = factory.CreateConnection())
                {
                    using (var channel = connection.CreateModel())
                    {
                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish(exchange: Exchange, 
                                            routingKey: RoutingKey,
                                            basicProperties: null,
                                            body: body);
                    }
                }
            }
            catch (Exception e)
            {
                return e.ToString();
            }
           
            return "Data successfully delivered!";
        }       
    }
}

С помощью NuGet console выполняем команду «install-package RabbitMQ.Client». В проекте должны появится необходимые ссылки на RabbitMQ.Client. Компилируем и компонента готова.

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

#Область ПрограммныйИнтерфейс

Функция ПолучитьОбъектКласса1CV8Publisher() Экспорт
    
    ПодключитьВнешнююКомпоненту("ОбщийМакет.NETLoader", "NET", ТипВнешнейКомпоненты.Native);
    
    Попытка
        Компонента = Новый("AddIn.NET.NETLoader");
    Исключение
        ТекстСообщения = НСтр("ru = 'Ошибка при подключении компоненты ""AddIn.NET.NETLoader"": %ТекстСообщения%'");
        ТекстСообщения = СтрЗаменить(ТекстСообщения, "%ТекстСообщения%", ОписаниеОшибки());
        ЗаписьЖурналаРегистрации("AddIn.NET.NETLoader", 
                        УровеньЖурналаРегистрации.Ошибка, 
                            ,
                            ,
                            ТекстСообщения,
                            );
        Возврат Неопределено;
    КонецПопытки;
    
    КаталогКомпонент = КаталогВременныхФайлов() + Новый УникальныйИдентификатор;
    СоздатьКаталог(КаталогКомпонент);
    ПолучитьОбщийМакет("RabbitMQ").Записать(КаталогКомпонент + "\RabbitMQ.Client.dll");
    ПолучитьОбщийМакет("V8Publisher").Записать(КаталогКомпонент + "\1CV8Publisher.dll");
    
    Попытка        
        Компонента.CreateObject(КаталогКомпонент, "1CV8Publisher", "_1CV8Publisher.V8Publisher", , Истина);
    Исключение
        ТекстОшибки = Компонента.GetLastError();
        Если ПустаяСтрока(ТекстОшибки) Тогда
            ТекстОшибки = ОписаниеОшибки();
        КонецЕсли;
        ТекстСообщения = НСтр("ru = 'Ошибка при вызове метода ""CreateObject"": %ТекстОшибки%'");
        ТекстСообщения = СтрЗаменить(ТекстСообщения, "%ТекстОшибки%", ТекстОшибки);
        ЗаписьЖурналаРегистрации("1CV8Publisher", 
                        УровеньЖурналаРегистрации.Ошибка, 
                            ,
                            ,
                            ТекстСообщения,
                            );
        Возврат Неопределено;
    КонецПопытки;
    
    Компонента.HostName = "hostname";
    Компонента.UserName = "admin";
    Компонента.Password = "admin";
    Возврат Компонента;   
КонецФункции // ПолучитьОбъектКласса1CV8Publisher()

Функция ВыполнитьОтправкуДанныхВОчередь(Компонента, Exchange, RoutingKey, Message) Экспорт
    Компонента.Exchange = Exchange;
    Компонента.RoutingKey = RoutingKey;  
    Результат = Компонента.SengMessage(Message);
    Если Результат <> "Data delivered successfully!" Тогда
        ВызватьИсключение Результат;  
    КонецЕсли;
    Возврат Результат;
КонецФункции // ВыполнитьОтправкуДанныхВОчередь()

#КонецОбласти

Теперь создадим очереди в RabbitMQ (связь систем 1 к 1, для связей 1 к 2 и больше необходимо создать дополнительные очереди которые будут заполняется с помощью routing):
1-1

Создадим exchanges, в которых укажем routing:exch-1exch-2

ВыполнитьОтправкуДанныхВОчередь(Компонента, "users", "users.event.create", Message);

Соответственно, после выполнения данной строки кода, в очередь users-create-queue упадут данные из параметра Message. Обратите внимание, нигде не указывается в какую очередь отправляются данные, а указывается exchange и routing key. Для обмена с двумя системами просто нужно добавить новую очередь, которая предназначена для системы №2 и в exchange добавить тот же routing key, но указать очередь №2. Вот так осуществляется подписка на простые события.

Послесловие

Этот подход работает лучше чем Zato ESB + OData. Время рассудит, станет ли это — «серебряной пулей» или простой рабочей лошадкой для специфических задач интеграции.

От pbazeliuk

8 комментарий для “7 причин, почему интеграцию необходимо строить на очередях. Практика RabbitMQ. Отказ от Zato ESB и OData в 1С”
  1. Можно поподробнее про компиляцию RabbitMQ.Client.dll? Что для этого требуется? Есть ли готовая либа в сети?

  2. Здравствуйте
    клиент 1С падает на этой строке:
    ПолучитьОбщийМакет(«RabbitMQ»).Записать(КаталогКомпонент + «\RabbitMQ.Client.dll»);

    RabbitMQ — нужно завести в макетах?

      1. Благодарю за ответ. Выложите, пожалуйста, скомпилированную 1CV8Publisher.dll , т.к. нет VisualStudio

Добавить комментарий

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