Обмен для распределенной базы данных (РБД) на основе API.
Используются стандартная структура и механизм обмена РБД.
Реализован на HTTP сервисе, используется нестандартный метод при загрузке и выгрузке данных (без использования файлов).
Задача стояла отказаться от выгрузки и загрузки файлов по Интрасети и упростить мониторинг и контроль процесса обмена.
Ключевой момент:
Вместо файла при отправке сообщения используется Новый ПотокВПамяти() и при получении сообщения ПолучитьСтрокуИзДвоичныхДанных(). По HTTP на сервис отправляется двоичные данные, в заголовке пишем, что это атачмент.
Логика работы:
1) В ЦБ регламентное задание №1 удаленно по API только инициирует на подчиненном узле (ПУ) другое регламентное задание №2 на обновление, это задание обращается запросом по API ан ЦБ и производит чтение, затем вторым запросом на запись.
2) В узлах плана обмена добавлены реквизиты: Отправлено узлом, Принято узлом они заполняются данными от ПУ. Попарное сравнение значений: Номер отправленного, Принято узлом и Отправлено узлом, Номер принятого покажет успешность завершения процесса обмена.
Сжатие/распаковка тела запроса через ХранилищеЗначения взял из отзыва alexandersh : в Публикации № 618906
Сжатие/Распаковка данных по алгоритму Deflate встроенными (!) средствами платформы 1С
Дополнение:
- по п.1) при удаленном запуске регламентного задания в теле запроса от ЦБ отправляются данные для обновления (запуск регламента с параметрами) , иначе если запрос на обновление пойдет от подчиненного узла, тогда будет влиять тайм-аут соединения http при чтении данных от ЦБ в он-лайне.
- на веб сервере IIS необходимо выполнить настройки (в разделе configuration editor) ограничивающие размер тела в запросе:
system.webServer/serverRuntime/uploadReadAheadSize
system.webServer/serverRuntime/maxRequestEntityAllowed
system.webServer/security/requestFiltering/maxAllowedContentLength
// Методы HTTP сервиса
Функция Принять_POST(Запрос)
Попытка
ПараметрыЗапроса=Запрос.ПараметрыЗапроса ; // /accept/{КодОтправителя_КодПолучателя}, запрос на прием данных от ЦБ или лога от узла
ПарамЗапроса=ПараметрыЗапроса.Получить("param");
ПараметрыURL=Запрос.ПараметрыURL ;
КодыУзлов=ПараметрыURL.Получить("Параметр_КодОтправителя_КодПолучателя"); // оправляется в строке запроса
Если ЗначениеЗаполнено(КодыУзлов) ТОгда
Масс=СтрРазделить(КодыУзлов,"_",истина);
Параметр=Масс[0];
КодОтправителя=Масс[1];
КодПолучателя=Масс[2];
УзелИсточник=ПланыОбмена.Обмен.НайтиПоКоду(КодОтправителя);
УзелПриемник=ПланыОбмена.Обмен.НайтиПоКоду(КодПолучателя);
Если Параметр = "ВызовРегламентаОбмена" Тогда
РезЗагрузки = МодульОбмена_API.ИнициироватьРегламентОбмена(УзелПриемник,УзелИсточник); // Удаленный запуск реглмента на обновление РБД
ИначеЕсли Параметр = "ЛогОбмена" Тогда // принимаем лог обмена от Узла
ТелоСтрокой = Запрос.ПолучитьТелоКакСтроку();
РезЗагрузки = МодульОбмена_API.ОбработатьЛогОтУзла(ТелоСтрокой,УзелИсточник);
Иначе
ДД=Запрос.ПолучитьТелоКакДвоичныеДанные();
текстСсобщения="";
Локально=Ложь ;
РезЗагрузки = МодульОбмена_API.ПринятьСообщение(УзелПриемник,УзелИсточник, текстСсобщения,Локально,ДД);
КонецЕсли;
Иначе
РезЗагрузки="Параметры: "+ КодыУзлов+ " "+КодОтправителя+" ПарамЗапроса >>"+ПарамЗапроса+" метод или параметр запроса не поддерживается!" ;
КонецЕсли;
Исключение
РезЗагрузки=ОписаниеОшибки();
КонецПопытки;
Результат_JSON = Получить_JSON (РезЗагрузки,"Загрузка");
Ответ = Новый HTTPСервисОтвет(200);
Заголовки=Новый Соответствие;
Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
Ответ.Заголовки=Заголовки;
Ответ.УстановитьТелоИзСтроки(Результат_JSON,КодировкаТекста.UTF8);
//Ответ.УстановитьТелоИзСтроки("Обновление принято, "+" НомерПринятого:"+РезЗагрузки);
Возврат Ответ;
КонецФункции
Функция Отправить_POST(Запрос)
Попытка
ПараметрыЗапроса=Запрос.ПараметрыЗапроса ; // /send/{КодОтправителя_КодПолучателя}, запрос на отправку даннх в ЦБ
ПарамЗапроса=ПараметрыЗапроса.Получить("param");
ПараметрыURL=Запрос.ПараметрыURL ;
КодыУзлов=ПараметрыURL.Получить("КодОтправителя_КодПолучателя"); // оправляется в строке запроса
Если ЗначениеЗаполнено(КодыУзлов) ТОгда
Масс=СтрРазделить(КодыУзлов,"_",истина);
КодОтправителя=Масс[0];
КодПолучателя=Масс[1];
Локально= ЛОЖЬ;
текстСсобщения="";
УзелИсточник=ПланыОбмена.Обмен.НайтиПоКоду(КодОтправителя);
УзелПриемник=ПланыОбмена.Обмен.НайтиПоКоду(КодПолучателя);
ДД=Неопределено ;
РезЗагрузки = МодульОбмена_API.ОтправитьСообщение(УзелИсточник,УзелПриемник,текстСсобщения,Локально,ДД);
Иначе
РезЗагрузки="Параметры: "+ КодОтправителя+" ПарамЗапроса >>"+ПарамЗапроса+" метод или параметр запроса не поддерживается!" ;
КонецЕсли;
Исключение
РезЗагрузки=ОписаниеОшибки();
Возврат РезЗагрузки;
КонецПопытки;
Ответ = Новый HTTPСервисОтвет(200);
Заголовки=Новый Соответствие;
Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
Заголовки.Вставить("Content-Disposition","attachment");
Ответ.Заголовки=Заголовки;
Ответ.УстановитьТелоИзДвоичныхДанных(ДД);
Возврат Ответ;
КонецФункции
// *******
Функция Получить_JSON (РезЗагрузки, типЗагрузки)
ЗаписьJSON_=Новый ЗаписьJSON;
ЗаписьJSON_.ПроверятьСтруктуру=Истина;
ПараметрыЗаписи= Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет);
ЗаписьJSON_.УстановитьСтроку(ПараметрыЗаписи);
ЗаписатьJSON(ЗаписьJSON_,РезЗагрузки);
Результат_JSON = ЗаписьJSON_.Закрыть();
Возврат Результат_JSON ;
КонецФункции
// Функция ПринятьСообщение(Приемник,Источник,ТекстСообщения,Локально,ДД) Экспорт - фрагмент кода
....
СтрокаBase64 = Base64Строка(ДД); // распакуем
ХЗ = СериализаторXDTO.XMLЗначение(Тип("ХранилищеЗначения"), СтрокаBase64);
ДД_ХЗ = ХЗ.Получить();
СтрЗагрузки = ПолучитьСтрокуИзДвоичныхДанных(ДД_ХЗ, КодировкаТекста.UTF8);
СтрЗагрузки= СтрЗаменить(СтрЗагрузки,Символы.ПС,"");
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеXML = Новый ЧтениеXML;
Попытка
//ЧтениеXML.ОткрытьФайл(ИмяФайла);
ЧтениеXML.УстановитьСтроку(СтрЗагрузки);
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
ПланыОбмена.ПрочитатьИзменения(ЧтениеСообщения, 1000);
ЧтениеСообщения.ЗакончитьЧтение();
ТекстСообщения = ТекстСообщения +"Чтение Завершилось Успешно" + Символы.ПС;
Исключение
ТекстСообщения = ТекстСообщения +"Ошибка при чтении сообщения:" + Символы.ПС+ ОписаниеОшибки()+Символы.ПС ;
ЕстьОшибка = Истина;
КонецПопытки;
// Функция ОтправитьСообщение(Источник,Приемник,ТекстСообщения,Локально,ДД) Экспорт - фрагмент кода
.....
Попытка
ЗаписьXML = Новый ЗаписьXML;
//ЗаписьXML.ОткрытьФайл(ИмяФайла);
ПотокВПамяти_=Новый ПотокВПамяти();
ЗаписьXML.ОткрытьПоток(ПотокВПамяти_, "UTF-8" );
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Приемник);
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения,1000);
ЗаписьСообщения.ЗакончитьЗапись();
ЗаписьXML.Закрыть();
ДД_=ПотокВПамяти_.ЗакрытьИПолучитьДвоичныеДанные();
ХЗ = Новый ХранилищеЗначения(ДД_, Новый СжатиеДанных(9)); // сжимаем
СтрокаBase64 = СериализаторXDTO.XMLСтрока(ХЗ);
ДД = Base64Значение(СтрокаBase64);
Если Локально Тогда
Ответ=ОтправитьДанныеОбмена_API(ДД,Источник,Приемник);
Иначе
Ответ=ДД;
КонецЕсли;
Исключение
ТекстСообщения = ТекстСообщения +"Ошибка при формировании сообщения:" + Символы.ПС+ОписаниеОшибки()+Символы.ПС;
ЕстьОшибка = Истина;
Ответ=ТекстСообщения;
КонецПопытки;
Функция ОтправитьДанныеОбмена_API(ДД,Источник,Приемник,ТипЗапроса="", ТекстЛога="") // отсылаем запрос на /accept/
Настройки=ПолучитьДанныеДляПодключения(Приемник);
SSL= Настройки.SSL ;
Если SSL Тогда
ЗащищенноеСоединение=Новый ЗащищенноеСоединениеOpenSSL( неопределено, неопределено );
Иначе
ЗащищенноеСоединение= неопределено;
КонецЕсли;
Соединение = Новый HTTPСоединение(Настройки.АдресУзла,Настройки.Порт,Настройки.Логин,Настройки.Пароль, ,60,ЗащищенноеСоединение);
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
Заголовки.Вставить("Content-Disposition","attachment");
Запрос = Новый HTTPЗапрос(Настройки.Директорий+ТипЗапроса+"_"+Источник.Код+"_"+Приемник.Код , Заголовки);
Если ЗначениеЗаполнено(ДД) Тогда
Запрос.УстановитьТелоИзДвоичныхДанных(ДД);
ИначеЕсли ЗначениеЗаполнено(ТекстЛога) Тогда
Запрос.УстановитьТелоИзСтроки(ТекстЛога);
КонецЕсли;
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Возврат Ответ ;
КонецФункции
Функция ПолучитьДанныеОбмена_API(Источник,Приемник) // отсылаем запрос на /send/
Настройки=ПолучитьДанныеДляПодключения(Источник);
SSL= Настройки.SSL ;
Если SSL Тогда
ЗащищенноеСоединение=Новый ЗащищенноеСоединениеOpenSSL( неопределено, неопределено );
Иначе
ЗащищенноеСоединение= неопределено;
КонецЕсли;
Соединение = Новый HTTPСоединение(Настройки.АдресУзла,Настройки.Порт,Настройки.Логин,Настройки.Пароль, ,60,ЗащищенноеСоединение);
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
Запрос = Новый HTTPЗапрос(Настройки.ДиректорийДляОтправки+Источник.Код+"_"+Приемник.Код , Заголовки);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Возврат Ответ ;
КонецФункции