7 причин, почему интеграцию необходимо строить на очередях. Практика RabbitMQ. Отказ от Zato ESB и OData в 1С

18.03.16

Интеграция - Внешние источники данных

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

Ссылка на предыдущую статью: "7 причин, почему интеграция стала приятной. Не упускайте ряд потрясающих возможностей"

Практические минусы 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):

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

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

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

 

Послесловие

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

Если вам помогла статья, благодарность можно выразить денежно.

Статья в личном блоге клац.

RabbitMQ OData Zato ESB Интеграция Обмены

См. также

Перенос данных из Парус 8 в ЗГУ 3

Зарплата Внешние источники данных Бюджетный учет Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате, фактических удержаниях, НДФЛ, вычетах, страховых взносах из базы Парус 8 учреждений в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ) и начать с ней работать с любого месяца года.

84000 руб.

19.08.2020    22447    19    1    

22

Перенос данных из Парус 10 в ЗГУ ред.3

Внешние источники данных Кадровый учет Файловый обмен (TXT, XML, DBF), FTP Обмен между базами 1C Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате, фактических удержаниях, НДФЛ, вычетах, страховых взносах из базы Парус 10 учреждений в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ) и начать с ней работать с любого месяца года.

60000 руб.

05.10.2022    9207    9    8    

10

Перенос данных из Парус 7.хх в ЗГУ ред.3

Внешние источники данных Зарплата Бюджетный учет Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате, фактических удержаниях, НДФЛ, вычетах, страховых взносах из базы Парус 7.хх учреждений в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ) и начать с ней работать с любого месяца года.

24000 руб.

24.04.2017    48696    97    163    

86

Перенос начальных остатков из Парус 7.71 в БГУ

Внешние источники данных Взаиморасчеты Учет ОС и НМА Логистика, склад и ТМЦ Бюджетный учет Платформа 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 2.0 1С:Бухгалтерия государственного учреждения Государственные, бюджетные структуры Россия Бюджетный учет Платные (руб)

Перенос словарей и начальных остатков из ПП Парус-Бухгалтерия Бюджет 7.71 в 1Сv8 БГУ2. Заполнение словарей и документов по вводу начальных остатков. Не требуется установка ПП Парус7. Возможна дозагрузка. Позволит автоматически и наиболее полно ввести данные в программу для начала работы. 

15600 руб.

08.12.2011    81559    128    123    

147

Перенос данных из Парус 10 (Торнадо) в ЗГУ ред.3 через Excel

Внешние источники данных Загрузка и выгрузка в Excel Зарплата Бюджетный учет Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате из Парус 10(Торнадо) учреждений через файлы Excel в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ). В принципе, обработка может быть использована для загрузки из файлов Excel, полученных из любых информационных систем.

24000 руб.

16.11.2018    29997    20    31    

21

Загрузка спецификаций в УНФ из системы Базис-мебельщик

Производство готовой продукции (работ, услуг) Внешние источники данных Платформа 1С v8.3 1С:Управление нашей фирмой 1.6 Лесное и деревообрабатывающее хозяйство Россия Управленческий учет Платные (руб)

Обработка предназначена для загрузки файлов, выгруженных из системы Базис-мебельщик, в справочник "Спецификации" для последующих процессов учета и диспетчирования полуфабрикатов и изделий.

7200 руб.

24.06.2021    19126    52    50    

29
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
91. pbazeliuk 1955 02.08.16 14:11 Сейчас в теме
(90) Mambetin, если с метаданными умеешь работать, можно даже сделать документацию API на выходе. Можно сделать форму, какие поля будут выгружаться/загружаться и от накликаного автоматически будет формироваться все остальное (Документация + API).
120. user1048547 10.10.18 12:37 Сейчас в теме
(89)Извините а есть пример реализации с RabbitMQ при помощи HTTP запросов?
121. Evil Beaver 8107 10.10.18 20:38 Сейчас в теме
(120) родного RMQ адаптера под HTTP нет, насколько я знаю. Можно закостылить сервер-обертку на каком-то другом языке. Но скорость будет так себе, по сравнению с честным двоичным протоколом AMQP
122. pbazeliuk 1955 11.10.18 11:57 Сейчас в теме
(120) можете воспользоваться этим сервисом (лицензия MIT) https://github.com/FoxyLinkIO/FoxyLink.RabbitMQ
97. Serginio 938 12.09.16 22:25 Сейчас в теме
Да кстати COM немного быстрее Native API
101. b00t 83 14.09.16 18:41 Сейчас в теме
(97) Serginio, COM просто не может быть быстрее NativeApi. COM -- тот ещё известный "тормоз". NativeApi по сути просто вызов кода из динамической библиотеки, с дополнительной трансляцией типов, и то не всегда. COM -- это ровно всё тоже самое, но только предваренное кучей сервисных вызовов, оборачивания объектов в диспетчеры и т.д. Т.е. если в каком-то тестировании оказалось что COM быстрее -- это очень плохое тестирование, я себе плохо представляю что там внутри набыдлокодили, чтобы так вышло.
102. Serginio 938 14.09.16 20:18 Сейчас в теме
Угу у IDispatch 2 вызова
GetIDsOfNames
Invoke

AddRef и Release вызываются при присвоении или выходу видимости
В Native API 4 вызова
FindMethod
GetNParams
HasRetVal
CallAsFunc

Да еще GetParamDefValue если количество параметров не соответствует указанному (перегрузка методов, дефолтные параметры, параметры массивы)

Для .Net так и так будут прокси. А для натива это вызов виртуальной функции. Но

Сейчас скорость вызова метода ВК почти в 15 раз медленнее вызова из С++ только
public static bool CallAsFunc(int Target, IntPtr ИмяМетодаPtr, IntPtr ReturnValue, IntPtr МассивПараметров, int РазмерМассива)

И медленнее в 5 раз аналогичного метода 1С. При этом вместо одного метода вызывается FindMethod, GetNParams, CallAsFunc. А если вызывать напрямую без ВК то и скорость будет аналогичной с использованием внутренних методов.
103. b00t 83 14.09.16 21:07 Сейчас в теме
(102) Serginio, ну значит, это адов косяк платформы, если всё так, как вы описали. Собственно это не удивительно, я недавно разбирался с одним "глюком", в процессе разборок выяснились подробности, очень "необычного" способа, если не сказать грубее, платформы на Linux искать библиотеки и определять их версии -- парсить консольный вывод ldconf -p
Да, простите, отвечая вам о скорости COM, я не учёл, что работа NativeApi может содержать "интересные" особенности. И если COM платформой используется через MFC/ATL скорее всего, то как там внутри "нагородили" NativeApi, остается только догадываться.
104. Serginio 938 14.09.16 21:15 Сейчас в теме
(103) Я написал в конце статьи http://infostart.ru/public/548701/ свои соображения. Но 1С это не особо волнует. Хотя можно и скорость увеличить и сделать кроссплатформенную замену COM
105. Serginio 938 15.09.16 16:28 Сейчас в теме
Кстати хочу статью написать по использованию в .Net Core WCF,ODATA могу и кролика вашего с событиями. Только мне какой нибудь тестовый сервис.

Есть RabbitMQ.Client под NetStandart 1.5
106. pbazeliuk 1955 15.09.16 17:09 Сейчас в теме
(105) Serginio, как-то преимуществ не вижу RabbitMQ + .Net WCF и .Net OData.
Все что может WCF можно реализовать на стороне 1С HTTP-Сервисом.
OData какой-то изврат получается.

P. S. Нужен адекватный пример\паттерн использования и обоснование почему стоит технологии использовать вместе.
107. Serginio 938 15.09.16 17:30 Сейчас в теме
(106) Нет поддержки WS протоколов, Header и прочих например
http://1c.mista.ru/topic.php?id=775298&page=1

ODATA там поддержка моделей Linq и тд. Можно динамическую компиляцию и спользовать либо сборки. Не нужно писать вручную HTTP запрос Linq to ODATA
В том числе поддержка протокола V4.


Ну а RabbitMQ HTTP клиентом то как?
108. Serginio 938 15.09.16 17:32 Сейчас в теме
Это я еще и для .Net Core пишу. Многое совсем недавно появилось. Там все меняется очень быстро.

По WS протоколам https://msdn.microsoft.com/ru-ru/library/ms752261(v=vs.110).aspx
112. vigo2 05.07.17 11:52 Сейчас в теме
"Ошибка вызова метода внешней компоненты" на строке:
Результат = Компонента.SengMessage(Message);
113. pbazeliuk 1955 05.07.17 13:52 Сейчас в теме
(112) Вы, наверное, первый кто попытался собрать все по статье. Все что здесь описано, к сожалению, уже устарело и в целом подход очень изменился и улучшился. Больше всего у вас ошибка где-то при сборке внешней компоненты (.NETLoader или _1CV8Publisher, так же может ошиблись с namespace)

P. S. Возможно, я напишу новую статью и размещу проект на github.com. Но больше помочь ничем не могу, увы.
114. vigo2 05.07.17 13:59 Сейчас в теме
(113) Есть подозрение, что дело в том, что Win7_64, а клиент 1С_32.
namespace взял из Вашего исходника: _1CV8Publisher.V8Publisher
P.S. класс в 1С видется, т.к. попробывал сгенерить ошибку:
Компонента.Test = "Нет этого property в классе";
и эта строка не отработала, т.к. нет такого свойства в Вашем классе
115. pbazeliuk 1955 05.07.17 14:10 Сейчас в теме
(114) https://github.com/pbazeliuk/RmqClient можете себе собрать .NETLoader x32 и х64, ну и так же компоненту.
116. pbazeliuk 1955 05.07.17 14:14 Сейчас в теме
(114)Тогда возможно ошибка в параметрах доступа к кластеру RabbitMQ
117. vigo2 05.07.17 14:35 Сейчас в теме
(116) Благодарю за оперативные ответы.
С JS к rabbitMQ без проблем подключается через библиотеку amqp. HostName = "amqp://somehost.org";
В 1С пробовал Компонента.HostName = "http://somehost.org:5672";
и без порта: Компонента.HostName = "http://somehost.org";
Через браузер заходит с данной комбинацией логин/пароль
118. pbazeliuk 1955 05.07.17 15:39 Сейчас в теме
(117) Подключение, что описано в примере подразумевает обычное не шифрованное TCP-подключение, попробуйте поднять локальный кластер или переписать подключение с использованием AMQP URL (тогда будет использован SSL). Это частично описано в документации.
130. ArchyAP 02.06.23 10:43 Сейчас в теме
(112)
(112)
Такая же беда. Причем добавил в компоненту свой метод-просто возвращающий строку, и он отрабатывает..
131. ArchyAP 02.06.23 10:46 Сейчас в теме
при этом если закомментировать
channel.BasicPublish(exchange: Exchange,
routingKey: RoutingKey,
basicProperties: null,
body: messageBodyBytes);
то метод отрабатывает в 1с)
119. yaxinr 57 26.02.18 09:05 Сейчас в теме
способ отправки JSONAPI в RabbitMQ через http
https://infostart.ru/public/790597/
PavelKolobkov; +1 Ответить
123. pbazeliuk 1955 11.10.18 12:03 Сейчас в теме
(119) HTTP-протокол, как плагин RabbitMQ, не гарантирует доставку, даже если был возвращен статус: 200 OK
Evil Beaver; +1 Ответить
124. seregasame 26.02.19 11:22 Сейчас в теме
В чем может быть проблема?
Could not load file or assembly '1CV8Publisher' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
125. seregasame 26.02.19 11:55 Сейчас в теме
Разобрался, криво создал проект
126. vis_tmp 32 25.04.20 14:11 Сейчас в теме
Никак не могу найти, есть ли возможность для 7.7 использовать Rabbit MQ ?
127. pbazeliuk 1955 26.04.20 11:50 Сейчас в теме
128. vis_tmp 32 26.04.20 16:06 Сейчас в теме
(127)Посмотрел сайт - не нашёл никаких подробностей/описаний...
129. pbazeliuk 1955 26.04.20 19:54 Сейчас в теме
Оставьте свое сообщение