Этот набросок является продолжение предыдущей статьи 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 причин, почему интеграцию необходимо строить на очередях
На данном этапе были переосмыслены все ошибки, было принято решение интеграцию строить на очередях, пока видны только одни плюсы, недостатки незаметны:
- используя очереди RabbitMQ, мы не ограничиваем себя одним языком программирования. Мы можем использовать, в большинстве случаев, родные языки систем которые интегрируются для отправки и приема сообщений;
- для гарантии доставки, нам не нужно доставать бубны и велосипеды, если сообщение не может быть положено в очередь, то и транзакция будет безболезненно отменена;
- интеграция одной системы к многим и наоборот выполняется одной единственной настройкой роутинга в RabbitMQ;
- простота формата сообщений, это бинарные данные — мы используем JSON, который переводится в бинарный формат;
- при высокой нагрузке, на довольно слабом виртуальном сервере, в очередь были доставлены все сообщения;
- каждый елемент системы не связан с другими елементами другой системы и бизнес-логика не дублируется, контроль целостности данных проверяется в едином месте в той системе для которой эти данные предназначены;
- множество типов очередей, даже такие что позволяют выполнять remote procedure call, но нужно стараться что бы системы были минимально связаны, бизнес-логика в «1С:Предприятие 8», а рабочие места, например, это мобильное приложение или веб-сайт.
Передача сообщения из «1С:Предприятие 8» в RabbitMQ
Для реализации этого механизма было принято решение отказаться от веб-сервиса 1С в пользу NativeAPI компоненты, так как планируется передавать большие объемы данных. Весь инструментарий, который понадобится:
- Использование .NET сборок в 1С 8.2, 8.3 без установки и без регистрации в реестре. Это нам позволит загрузить произвольный c# класс, который будет принимать JSON строку и отправлять в очередь;
- Интеграция корпоративных приложений на платформе 1С (как это было). Полезно для общего развития.
Исходный код, компоненты:
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):
Создадим exchanges, в которых укажем routing:
ВыполнитьОтправкуДанныхВОчередь(Компонента, "users", "users.event.create", Message);
Соответственно, после выполнения данной строки кода, в очередь users-create-queue упадут данные из параметра Message. Обратите внимание, нигде не указывается в какую очередь отправляются данные, а указывается exchange и routing key. Для обмена с двумя системами просто нужно добавить новую очередь, которая предназначена для системы №2 и в exchange добавить тот же routing key, но указать очередь №2. Вот так осуществляется подписка на простые события.
Послесловие
Этот подход работает лучше чем Zato ESB + OData. Время рассудит, станет ли это — «серебряной пулей» или простой рабочей лошадкой для специфических задач интеграции.
Можно поподробнее про компиляцию RabbitMQ.Client.dll? Что для этого требуется? Есть ли готовая либа в сети?
Найдите ее здесь: http://www.nuget.org. А далее, в Visual Studio с помощью NuGet console установите.
Версия Visual Studio ?
На момент статьи версия была: MS Visual Studio Community Edition 2015
[…] для больше надежности присутствует возможность подключения сервиса очередей RabbitMQ. […]
Здравствуйте
клиент 1С падает на этой строке:
ПолучитьОбщийМакет(«RabbitMQ»).Записать(КаталогКомпонент + «\RabbitMQ.Client.dll»);
RabbitMQ — нужно завести в макетах?
Добрый день, да в этом примере «RabbitMQ» должен быть в общем макете
Благодарю за ответ. Выложите, пожалуйста, скомпилированную 1CV8Publisher.dll , т.к. нет VisualStudio