Фильтрация узлов при обмене
Проблема
При создании в предприятии системы обмена со множеством узлов, например при внедрении распределенной системы баз: головной офис (центральный узел) + филиалы предприятия (подчиненные узлы) могут возникнуть различные проблемы. Мы здесь рассмотрим один из таких проблем, которая может привести к искажению данных при обмене: «Проблема загрузки файла обмена с неправильного узла».
Бывает, что пользователи (обычно администраторы или программисты) любят делать копии базы для различных целей. Часто эти копии потом открывают в файловом режиме, но иногда могут делать копии и на сервере, например для различных тестов или разработок. При этом даже специалисты часто забывают отключать автообмен. Тогда и возникает данная проблема: с копии базы может выгрузиться файл обмена в рабочий ресурс обмена (например, на ftp ресурс). Тогда, если рабочая база не успеет переписать неправильный файл, то в рабочую базу может загрузиться файл обмена не с рабочей базы, а с его копии, что может привести к ошибочному изменению данных.
Это касается обмена РИБ между центральной базой и с несколькими (особенно, когда их много) подчиненными базами, но также это может касаться и универсального обмена между многими базами. В данном при мере мы рассмотрим именно обмен в системе РИБ типовой конфигурации.
Решение
Для решения этой проблемы этого мы предлагаем каждый раз включать в файл обмена данными информацию о местонахождении базы, с которого он был выгружен и при чтении файла обмена на другом узле сверять информацию о местонахождении с известным списком и на основании этого принимать решение о загрузке данных.
Следующий вариант реализации был использован в типовой конфигурации Бухгалтерии Предприятия 2.0, но, скорее всего, годиться и для других типовых конфигураций, разработанных на обычных формах (например, для Управление торговли 10,3)
Для определения местонахождения текущей базы используем строку соединения, которую легко можно посмотреть в списке баз и выглядит она так
Srvr="Сервер1С";Ref="РабочаяБаза"; для серверного варианта или File="\\Сервер1С\С\ПапкаРабочейБазы"; для файлового варианта информационной базы.
Для удобства хранения правильного местонахождения, мы в план обмена «Полный» (как пример РИБ обмена) добавили реквизит «Параметры» типа «Строка» неограниченной длины, где и будем хранить строку соединения правильной базы, с которой можно загружать данные.
При выгрузке файла наша процедура будет добавлять в файл обмена в начало блога непосредственно до начала блога наш элемент
info:
[CODE]
Srvr="Сервер1С ";Ref="РабочаяБаза";
[/CODE]
Пример пустого файла сообщений из главного узла в подчиненный с нашей вставкой:
При чтении же этого файла обмена на другом узле будем проверять соответствие строки соединения из файла обмена правильной строке соединения из реквизита «Параметры» соответствующего узла обмена.
Реализация записи дополнительной информации:
В Общем модуле «ПроцедурыОбменаДанными», в процедуре «ЗаписатьСообщенияСИзмененеиямиДляУзла» после кода
Если ЭтоРИБ Тогда
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, УзелОбмена);
Вставляем ссылку на нашу процедуру:
ЗаписатьДопИнфо(ЗаписьСообщения,ЗаписьXML,УзелОбмена);
которая записывает дополнительную информацию в файл обмена.
Процедура ЗаписатьДопИнфо(ЗаписьСообщения,ЗаписьXML,УзелОбмена) Экспорт
если ПроверятьСтрокуСоединенияУзла(ПолучитьСтрокуСоединенияУзла(УзелОбмена)) тогда
ЗаписьXML.ЗаписатьНачалоЭлемента("info");
ЗаписьXML.ЗаписатьАтрибут("dop_info", "Строка");
ВключитьСтоку=СтрокаСоединенияИнформационнойБазы();
ЗаписьXML.ЗаписатьТекст(ВключитьСтоку);
ЗаписьXML.ЗаписатьКонецЭлемента();
КонецЕсли;
КонецПроцедуры
Следующая функция получает информацию о местонахождении правильной базы
Функция ПолучитьСтрокуСоединенияУзла(УзелОбмена)
если ТипЗнч(УзелОбмена)=тип("ПланОбменаСсылка.Полный") тогда
Возврат УзелОбмена.Параметры;
иначе
возврат "";
КонецЕсли;
КонецФункции
Следующая функция определяет – нужно ли записывать дополнительную информацию или проверять файлы обмена перед загрузкой. Такая проверка позволяет отключать наш фильтр, что весьма полезно на этапе внедрения. В нашем случае просто – если реквизит «Параметры» - пустой – то не фильтруем.
Функция ПроверятьСтрокуСоединенияУзла(стр_изУзла)
если стр_изУзла="" тогда
возврат Ложь;
иначе
Возврат Истина;
КонецЕсли;
КонецФункции
z88;
Реализация чтения дополнительной информации:
В Общем модуле «ПроцедурыОбменаДанными», в процедуре «ЗагрузитьCообщениеСИзменениямиОтРИБУзла» после кода
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
Комментируем следующую строку
//ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
и вместо него вставляем ссылку на нашу процедуру:
ПроверитьВозможностьЗагрузки(СтруктураНастроекОбменаДанными.ДанныеНастройки.УзелИнформационнойБазы,ЧтениеСообщения,ЧтениеXML);
Следующая функция реализует схему фильтрации узлов, даже тогда когда возникает ошибка при чтении заголовка файла (например «Номер сообщения меньше или равен номеру ранее принятого сообщения»), чтобы вовремя выявить «неотключённый автообмен» в копиях информационной базы. Иначе даже если файл из неправильного узла не будет загружаться, то есть не будет искажения данных, но файл из неправильного узла может затирать файл из правильного узла и тем самым тормозить обмен между узлами:
Процедура ПроверитьВозможностьЗагрузки(УзелОбмена,ЧтениеСообщения,ЧтениеXML) Экспорт
Попытка
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
ОписаниеОшибки= ПроверитьПравильностьУзла(УзелОбмена,ЧтениеСообщения,ЧтениеXML);
Исключение
ОписаниеОшибки=ОписаниеОшибки();
если ЧтениеXML<>Неопределено тогда
ОписаниеОшибки=ОписаниеОшибки+" (номер сообщения = " + ЧтениеXML.Значение+" номер ранее принятого = "+СтрЗаменить(УзелОбмена.НомерПринятого,символы.НПП,"")+") ";
ОписаниеОшибки1= ПроверитьПравильностьУзла(УзелОбмена,ЧтениеСообщения,ЧтениеXML);
если ОписаниеОшибки1<>"" тогда
ОписаниеОшибки=ОписаниеОшибки1 + Символы.ПС + ОписаниеОшибки;
КонецЕсли;
КонецЕсли;
КонецПопытки;
если ОписаниеОшибки<>"" тогда
ВызватьИсключение ОписаниеОшибки;
КонецЕсли;
КонецПроцедуры
Следующая функция читает блок наш из файла обмена и проверяет возможность его загрузки:
Функция ПроверитьПравильностьУзла(УзелОбмена,ЧтениеСообщения,ЧтениеXML)
// ограничим чтение для непридвиденных случаев (неправильных файлов)
для счетчик=1 по 30 цикл
ЧтениеXML_Имя=ЧтениеXML.Имя;
ЧтениеXML.Прочитать();
СтрокаСоединения=ЧтениеXML.Значение;
если ЧтениеXML_Имя="info" тогда
Строки_изУзла=ПолучитьСтрокуСоединенияУзла(УзелОбмена);
если Не ПроверятьСтрокуСоединенияУзла(Строки_изУзла) тогда
ОписаниеОшибки="";
иначе
ОписаниеОшибки=ПроверкаСтрокиСоединенияУзла(Строки_изУзла,СтрокаСоединения);
КонецЕсли;
если ОписаниеОшибки="" тогда
// читаем конец элемента info
ЧтениеXML.Прочитать();
// позиционируемся на начало элемента v8de:Changes
ЧтениеXML.Прочитать();
возврат "";
иначе
Возврат ОписаниеОшибки;
КонецЕсли;
КонецЕсли;
если ЧтениеXML.Имя="v8de:Changes" или ЧтениеXML.Имя="v8de:Data" тогда
// позиционируемся на начало элемента v8de:Changes
ЧтениеXML.Прочитать();
возврат "";
КонецЕсли;
КонецЦикла;;
Возврат "";
КонецФункции
При реализации фильтрации узлов по строке соединения может возникнуть проблема разных строк соединения у разных пользователей.
Например, сервер может называться по имени, или же как «localhost» или же может быть задан IP адрес. Причем IP для локальной базы может быть задан как 127.0.0.1. Для файлового варианта тоже местонахождение базы может быть задана несколькими способами:
Srvr="Сервер1С";Ref="РабочаяБаза";
Srvr="localhost";Ref="РабочаяБаза";
Srvr="192.168.0.1 ";Ref="РабочаяБаза";
Srvr="127.0.0.1 ";Ref="РабочаяБаза";
File="\\Сервер1С\С\ПапкаРабочейБазы";
File="\\192.168.0.1\С\ПапкаРабочейБазы";
File="C:\Базы1С\ПапкаРабочейБазы"; где С:- локальный диск.
File="Z:\ПапкаРабочейБазы"; где Z:- подсоединенный диск.
Обмен может идти с одного компьютера (например автообмен из сервера), или же обмен может быть сделан вручную пользователями разных компьютеров. Если на разных компьютерах и у разных пользователей прописаны разные строки соединения, то в реквизите «Параметры» нужно включить несколько «правильных» строк соединения через разделитель «//»
Например: Srvr="Сервер1С";Ref="РабочаяБаза";//Srvr="localhost";Ref="РабочаяБаза";//Srvr="192.168.0.1 ";Ref="РабочаяБаза";
Но для большей гарантии того, что файл идет с правильного узла, конечно, лучше настроить на всех компьютерах и для всех пользователей единую, универсальную строку соединения, например: Srvr="Сервер1С";Ref="РабочаяБаза"; или File="\\Сервер1С\С\ПапкаРабочейБазы"; Здесь выбран вариант имени сервера, так как IP часто могут меняться, а имя сервера обычно редко меняется.
В клиент-серверном варианте, для того, чтобы при автообмене в файл записывалась правильная строка соединения, мы должны в списке баз на сервере поменять (если нужно) строку соединения на правильный, т.к. обычно системные администраторы любят при настройке базы писать не имя сервера, а localhost или 127.0.0.1. Здесь есть тонкость:
необходимо проверить 2 файла на сервере приложений, в которых сохранены строки настроек баз 1С на сервере:
впапке C:\Program Files (x86)\1cv82\srvinfo\ или C:\Program Files\1cv82\srvinfo\ в файле srvribrg.lst проверить, что стоит в строке настройки рабочей базы
Второй файл лежит в следующей папке: C:\ProgramFiles (x86)\1cv82\srvinfo\reg_1541 или C:\ProgramFiles\1cv82\srvinfo\reg_1541 (обычно, эта папка единственная в папке \1cv82\srvinfo\) в файле 1CV8Reg.lst также проверить настройку строки соединения.
Если в файлах что-то неправильно, то перед тем как редактировать эти файлы необходимо сохранить копию этих файлов (на случай неудачного редактирования) остановить сервер 1С и после редактирования – заново запустить сервер 1С. При этом помните, что операция перезапуска сервера аварийно завершит все сеансы пользователей 1С во свех базах этого сервера приложений!
z88;
Следующая функция проверяет соответствие строки соединения, записанного в файле обмена с правильными данными из реквизита «Параметры». При этом, функция работает, если задана ода строка соединения, так и если заданы несколько «правильных» строк соединения через «//».
z88;
Функция ПроверкаСтрокиСоединенияУзла(знач Строки_изУзла,знач СтрокаСоединения)
СтрокаСоединения=ВРег(СтрокаСоединения);
МассивСтрок=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(Строки_изУзла, "//");
для Каждого СтрокаИзУзла из МассивСтрок Цикл
если СтрокаСоединения=ВРег(СтрокаИзУзла) тогда
возврат "";
КонецЕсли;
КонецЦикла;
Если МассивСтрок.Количество()=1 тогда
ОписаниеОшибки = " Файл обмен из неправильного узла ("+СтрокаСоединения+"). Файл не будет загружен! Правильный узел = "+СтрокаИзУзла+". ";
иначе
ОписаниеОшибки = " Файл обмен из неправильного узла ("+СтрокаСоединения+"). Файл не будет загружен! Правильные узлы = "+Строки_изУзла+". ";
КонецЕсли;
Возврат ОписаниеОшибки;
КонецФункции