gifts2017

Обмен с помощью WMI

Опубликовал Юрий Лоек (loekyn) в раздел Администрирование - Распределенная БД (УРИБ, УРБД)

Полный контроль над процессом, осуществляемый из единого центра.

Читаю здешние темы "обмен через FTP", "через облачный севис", "через почту"... Все эти механизмы имеют кучу недостатков:

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

 - При любой нештатной ситуации (например, не правильный номер сообщения или проблемы с изменением конфигурации) все эти методы требуют ручной обработки события.

- При необходимости внепланового обмена, надо руками лезть во все базы и запускать в них обменные процессы.

- Излишние телодвижения файла выгрузки: из базы во временный каталог, оттуда в письмо (яндекс-диск и пр.), потом во временный каталог приемника и потом загрузка данных в приемник.

 - И т.д.

Использование механизма WMI позволяет исключить все эти неприятности и получить еще кучу плюшек.

У меня есть готовый механизм, но т.к. он интегрирован с другими механизмами общего назначения (резервное копирование, отключение пользователей и т.д.) в отдельную базу, выкладывать его тут не буду, просто опишу принципы.

Я создал отдельную базу, которая (кроме всего прочего), осуществляет обмен РИБ по расписанию регламентного задания.

1. Можно сначала сделать резервную копию (если надо).

2. Проконтролировав выполнение 1 этапа делаем выгрузку центрального узла для всех периферий.

3. Проконтролировав завершение просто копируем файл из временного каталога центрального компьютера на периферийный (при хорошем интернете можно грузить даже непосредственно, но это чревато риском в случае обрыва связи). Контролируем совпадение размеров исходного и полученного файлов. При сбое повторяем установленное количество раз. Не вышло - отмечаем сбой.

4. Загружаем одновременно (последовательно запустив процессы в цикле) периферийные базы.

5. Анализируем лог загрузки и обрабатываем ошибку, если она есть: изменяем номер сообщения или выгружаем, передаем и загружаем конфигурацию центрального узла и т.д. - в зависимости от вида сбоя. Контролируем - не вышло - отмечаем сбой (в дальнейшем "***").

6. В случае загрузки конфигурации, повторяем загрузку выгрузки центра.

7. Выгружаем периферию. ***

8. Передаем выгрузку периферии в центр. ***

9. Загружаем в центральной узел. ***

Все управление осуществляется одной отдельной базой, которая последовательно запускает распределённые базы в описанном выше порядке, контролирует и фиксирует результаты выполнения.

Для визуализации процесса я использую форму списка справочника. Который содержит в себе список РИБ с необходимыми атрибутами (место выгрузки, путь к исполняемому файлу 1С и пр.), в котором отражается текущая пиктограмма процесса, и лог операций для каждого узла.

Теперь наконец о собственно механизмах WMI.

Запуск удаленной базы:

Locator=Новый COMОбъект("WbemScripting.SWbemLocator");

objWMIService=Locator.ConnectServer(КомпIP,"\root\cimv2", Константы.ЛогинАдминистратора.Получить(),Константы.ПарольАдминистратора.Получить());

Ели база на том же компьютере что и Управляющая БД, то

objWMIService=Locator.ConnectServer(".");

objProcess=Ст.objWMIService.Get("Win32_Process");

intProcessID=0;              

strCommand= ПутьКИсполняемымФайлам1С+"1cv8.exe ENTERPRISE /S /NZipOut /P"+СокрЛП(Константы.ПарольZip.Получить())+" /DisableStartupMessages  /Out"+ИмяФайлаЛогаВыгрузки;                                       

objProcess.Create(strCommand,null,,intProcessID);

После запуска базы контролируем окончание процесса:

 

pEnum=Ст.objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE Name = '1cv8.exe'");  

                Для Каждого Object Из pEnum Цикл

                               Про=Object.Properties_;

                               Для Каждого Свойство Из Про Цикл

                                               Если Свойство.Name="ProcessId" Тогда

                                                               Если Свойство.Value=Ст.intProcessID Тогда

                                                                              Если ТекущаяДата()>Ст.Ждать Тогда

                                                                                              Попытка

                                                                                                              Object.Terminate();

                                                                                              Исключение

                                                                                              КонецПопытки;

                                                                                              Ст.Лог=Ст.Лог+"Превышен лимита ожидания операции!"+Символы.ПС;

                                                                                              Ст.Ждать=0;

                                                                                              Возврат Ложь;

                                                                              Иначе

                                                                                              Возврат Истина;

                                                                              КонецЕсли;

                                                               КонецЕсли;

                                                               Прервать;

                                               КонецЕсли;

                               КонецЦикла; 

                КонецЦикла;


В модуль конфигурации РИБ надо добавить процедуры:

 

Процедура ПередНачаломРаботыСистемы(Отказ)

                Если ИмяПользователя()="Zip" Тогда

                               Ждать(5);

                               Если ЗагрузкаОбмена(ПараметрЗапуска) Тогда

                                               ВыгрузкаОбмена(ПараметрЗапуска);                

                               КонецЕсли;       

                               Отказ=Истина;

                ИначеЕсли ИмяПользователя()="ZipOut" Тогда

                               Ждать(5);

                               ВыгрузкаОбмена(ПараметрЗапуска);                

                               Отказ=Истина;

                ИначеЕсли ИмяПользователя()="ZipIn" Тогда

                               Ждать(5);

                               Если ПараметрЗапуска="NodeOff" Тогда

                                               Попытка

                                                               ПланыОбмена.УстановитьГлавныйУзел(Неопределено);

                                                               //Сообщить("Распределение отключено.");

                                               Исключение

                                                               //Сообщить("Распределение уже отключено.");

                                               КонецПопытки;

                               ИначеЕсли ПараметрЗапуска="NodeOn" Тогда

                                               Попытка

                                                               ГлавныйУзел=ПланыОбмена.Филиалы.НайтиПоКоду("000");

                                                               ПланыОбмена.УстановитьГлавныйУзел(ГлавныйУзел);         

                                                               //Сообщить("Распределение включено.");

                                               Исключение

                                                               //Сообщить("Распределение уже включено.");

                                               КонецПопытки;

                               Иначе

                                               ЗагрузкаОбмена(ПараметрЗапуска);

                               КонецЕсли;

                               Отказ=Истина;

                               КонецЕсли;

                КонецЕсли;

КонецПроцедуры

//------------

Процедура ВыгрузкаОбмена(УзелЗВ)

                ФСО=Новый COMОбъект("Scripting.FileSystemObject");

                Попытка

                               ВыборкаУзлов=ПланыОбмена.Филиалы.НайтиПоНаименованию(УзелЗВ); 

                               Узел=ВыборкаУзлов.Ссылка; 

                               ЭтотУзел=ПланыОбмена.Филиалы.ЭтотУзел();

                               Путь=СокрЛП(ЭтотУзел.Место);

                               Если ПланыОбмена.ГлавныйУзел()=Неопределено Тогда

                                               //Путь=СокрЛП(ЭтотУзел.ПутьКБД)+"PC\";

                                               //ПутьП=СокрЛП(ЭтотУзел.ПутьКБД)+"PC\";

                                               

                                               //НФ=НайтиФайлы(ПутьП, СокрЛП(Узел.Код)+"-"+СокрЛП(ЭтотУзел.Код)+".zip");

                                               НФ=НайтиФайлы(Путь, СокрЛП(Узел.Код)+"-"+СокрЛП(ЭтотУзел.Код)+".zip");

                                               Попытка

                                                               ФайлЗ=НФ[0].ПолноеИмя;

                                                               ФСО.DeleteFile(ФайлЗ);

                                               Исключение

                                                               Возврат;

                                               КонецПопытки;

                               //Иначе

                               //            Путь=СокрЛП(ЭтотУзел.ПутьКБД)+"PC\";

                               //            ПутьП=СокрЛП(ЭтотУзел.ПутьКБД)+"CP\";

                               КонецЕсли;

                               

                               ИмяФайлаСообщения=Путь+"Выгрузка.xml"; 

                               ЗаписьXML=Новый ЗаписьXML(); 

                               ЗаписьXML.ОткрытьФайл(ИмяФайлаСообщения); 

                               ЗаписьСообщения=ПланыОбмена.СоздатьЗаписьСообщения(); 

                               ЗаписьСообщения.НачатьЗапись(ЗаписьXML,Узел); 

                                ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения,Константы.ТранзакцииОбмена.Получить()); 

                               ЗаписьСообщения.ЗакончитьЗапись(); 

                               ЗаписьXML.Закрыть(); 

                               ЗФ=Новый ЗаписьZipФайла(Путь+СокрЛП(ЭтотУзел.Код)+"-"+СокрЛП(Узел.Код)+".zip"); 

                               ЗФ.Добавить(ИмяФайлаСообщения);

                               ЗФ.Записать();

                               Сообщить("Выгружены данные ("+ЭтотУзел+" - "+УзелЗВ+") -  "+ТекущаяДата());

                Исключение

                               ОО=ОписаниеОшибки();

                               ЗаписьЖурналаРегистрации("ОБМЕН.Выгрузка ("+ЭтотУзел+" - "+УзелЗВ+")", УровеньЖурналаРегистрации.Ошибка, , , ОО);

                               Сообщить(">>> ВЫГРУЗКА("+ЭтотУзел+" - "+УзелЗВ+"): "+ОО);

                КонецПопытки;

                Попытка

                               ФСО.DeleteFile(Путь+"Выгрузка.xml");

                Исключение

                КонецПопытки;

КонецПроцедуры

//------------

Функция ЗагрузкаОбмена(УзелЗВ)

                Первый=Истина;

                ЭтотУзел=ПланыОбмена.Филиалы.ЭтотУзел();

~АА:      

                Попытка

                               Если Первый Тогда

                                               ФСО=Новый COMОбъект("Scripting.FileSystemObject");

                                               Попытка

                                                               ФСО.DeleteFile(СокрЛП(ЭтотУзел.Место)+"New.txt");

                                               Исключение

                                               КонецПопытки;

                                               ВыборкаУзлов=ПланыОбмена.Филиалы.НайтиПоНаименованию(УзелЗВ); 

                                               Узел=ВыборкаУзлов.Ссылка; 

                                               Путь=СокрЛП(ЭтотУзел.Место);

                                               //Если ПланыОбмена.ГлавныйУзел()=Неопределено Тогда

                                               //                           Путь=СокрЛП(ЭтотУзел.ПутьКБД)+"PC\";

                                               //            Иначе

                                               //                           Путь=СокрЛП(ЭтотУзел.ПутьКБД)+"CP\";

                               //            КонецЕсли;

                                               НФ=НайтиФайлы(Путь, СокрЛП(Узел.Код)+"-"+СокрЛП(ЭтотУзел.Код)+".zip");

                                               Попытка

                                                              ФайлЗ=НФ[0].ПолноеИмя;

                                               Исключение

                                                               Возврат Ложь;

                                               КонецПопытки;

                                               ЗФ=Новый ЧтениеZipФайла(ФайлЗ); 

                                               ЗФ.ИзвлечьВсе(Путь);

                                               ИмяФайлаСообщения=Путь+"Выгрузка.xml";

                               КонецЕсли;

                               ЧтениеXML=Новый ЧтениеXML(); 

                               ЧтениеXML.ОткрытьФайл(ИмяФайлаСообщения); 

                               ЧтениеСообщения=ПланыОбмена.СоздатьЧтениеСообщения(); 

                               ЧтениеСообщения.НачатьЧтение(ЧтениеXML); 

                               Попытка

                                               ПланыОбмена.ПрочитатьИзменения(ЧтениеСообщения,Константы.ТранзакцииОбмена.Получить()); 

                                               

                                               Об=Узел.ПолучитьОбъект();

                                               Об.ДатаОбмена=ТекущаяДата();

                                               Об.Записать();

                               Исключение

                                               ОО=ОписаниеОшибки();

                                               ЧтениеXML.Закрыть(); 

                                               ФСО.DeleteFile(Путь+"Выгрузка.xml");

                                               Если ПланыОбмена.ГлавныйУзел()<>Неопределено Тогда

                                                               ФСО.DeleteFile(ФайлЗ);

                                               КонецЕсли;

                                               Если Найти(Строка(ОО),"изменения конфигурации")>0 Тогда

                                                               Если КонтрольПериферии() Тогда

                                                                              ФСО.CreateTextFile(СокрЛП(ЭтотУзел.Место)+"New.txt");

                                                                              Сообщить("Загружена конфигурация ("+УзелЗВ+" - "+ЭтотУзел+") -  "+ТекущаяДата());

                                                               КонецЕсли;

                                                               Сообщить(">>> ЗАГРУЗКА ("+УзелЗВ+" - "+ЭтотУзел+"): "+ОО);

                                                               Возврат Истина;

                                               Иначе  

                                                               ЗаписьЖурналаРегистрации("ОБМЕН.Загрузка ("+УзелЗВ+" - "+ЭтотУзел+")",                УровеньЖурналаРегистрации.Ошибка, , , ОО);

                                                               Сообщить(">>> ЗАГРУЗКА ("+УзелЗВ+" - "+ЭтотУзел+"): "+ОО);

                                                               Возврат Ложь;

                                               КонецЕсли;

                               КонецПопытки;

                               ЧтениеСообщения.ЗакончитьЧтение(); 

                               ЧтениеXML.Закрыть(); 

                               ФСО.DeleteFile(Путь+"Выгрузка.xml");

                               Если ПланыОбмена.ГлавныйУзел()<>Неопределено Тогда

                                               ФСО.DeleteFile(ФайлЗ);

                               КонецЕсли;

                               Сообщить("Загружены данные ("+УзелЗВ+" - "+ЭтотУзел+") -  "+ТекущаяДата());

                               Возврат Истина;

                Исключение

                               ОО=ОписаниеОшибки();

                               //=========

                               Если Найти(Строка(ОО),"Номер сообщения меньше или равен")>0 Тогда

                                               Об=Узел.ПолучитьОбъект();

                                               Об.НомерПринятого=Об.НомерПринятого-1;

                                               Об.Записать();

                                               Первый=Ложь;

                                               Перейти ~АА;  

                               КонецЕсли;

                               //=========

                               Попытка

                                               ФСО.DeleteFile(Путь+"Выгрузка.xml");

                               Исключение

                               КонецПопытки;

                               Попытка

                                               Если ПланыОбмена.ГлавныйУзел()<>Неопределено Тогда

                                                               ФСО.DeleteFile(ФайлЗ);

                                               КонецЕсли;

                               Исключение

                               КонецПопытки;

                               ЗаписьЖурналаРегистрации("ОБМЕН.Загрузка", УровеньЖурналаРегистрации.Ошибка, , , ОО);

                               Сообщить(">>> ЗАГРУЗКА("+УзелЗВ+"): "+ОО);

                               Возврат Ложь;

                КонецПопытки;

КонецФункции

 //------------

Процедура Ждать(СекЖ)

                Врем=ТекущаяДата()+СекЖ;

                КК=1;

                Пока Врем>=ТекущаяДата() Цикл

                               Предупреждение("Ждем: "+КК, 1, "Задержка...");    

                               КК=КК+1;            

                КонецЦикла;

КонецПроцедуры         

//-------------

Функция ПолучитьСервер(Стр) Экспорт

                Пром=СтрЗаменить(Стр,"\\","");

                Пром=СтрЗаменить(Пром,":","");

                Поз=Найти(Пром,"\");

                Если Поз=0 Тогда

                               Возврат Неопределено;

                Иначе

                               Возврат Лев(Пром,Поз-1);

                КонецЕсли;

КонецФункции             

  

 

Если обмен осуществляется между базами с конфигурацией на поддержке, то весь этот механизм можно оформить через запуск соответствующих внешних обработок из командной строки.

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

Резюме: с помощью WMI можно управлять всем процессом обмена из одного места, имея перед собой полную картину происходящего (если не ленится оформить соответствующую визуализацию) и возможность прервать процесс или осуществить ручной запуск обменной цепочки.

 

См. также

Подписаться Добавить вознаграждение

Комментарии

1. sdf sdf (ruskiy1) 10.11.14 18:12
глаза сломать можно от такого форматирования кода
2. Призрак (davdykin) 10.11.14 22:02
Честно говоря, при беглом чтении кода так и не понял как перекидывается файл. Насколько я понял данный механизм позволяет запускать на удаленном компьютере произвольные процессы, доступ осуществляется по ip-ику (если не прав поправьте), Собственно основной вопрос какие сетевые настройки на целевом и центральном компьютере требуются (проброска портов, dmz, белый ip)?
3. Юрий Лоек (loekyn) 11.11.14 09:11
За форматирование извините - 2я публикация здесь, поэтому опыта по местным обстоятельствам никакого.

Файл перекидывается простым копированием: если у вас есть удаленный доступ к компьютеру, то чего мудрить?
На центральном и целевом компах должен быть включен WMI. Так же на целевом надо настроить доступ с определенного IP (во избежание) и знать логин и пароль админа целевого компа.
Вот код передачи файла:

Функция ПередачаВыгрузкиПериферии(Ст,ФСО,ТЗ,Сост)	
	Ст.Источник=СокрЛП(Ст.База.МестоВ)+СокрЛП(Ст.База.КодЦ)+"-"+СокрЛП(Ст.Центр.КодЦ)+".zip";
	Ст.Приемник=СокрЛП(Ст.Центр.МестоВ)+СокрЛП(Ст.База.КодЦ)+"-"+СокрЛП(Ст.Центр.КодЦ)+".zip";
	Если Не ПереносФайла(Ст,ФСО,ТЗ,Сост) Тогда
		Возврат Ложь;
	КонецЕсли;
	Возврат Истина;
КонецФункции


Функция ЕстьФайл(ИмяФ,ФСО) 
	Если ФСО.FileExists(ИмяФ) Тогда
		Файл=ФСО.GetFile(ИмяФ);
		Возврат Файл.Size;
	Иначе
		Возврат -1;
	КонецЕсли;	
КонецФункции
//------
Функция ЖдатьФайл(Ст,ФСО)
	Если ФСО.FileExists(Ст.Источник) И ФСО.FileExists(Ст.Приемник) Тогда
		Если ФСО.GetFile(Ст.Источник).Size=ФСО.GetFile(Ст.Приемник).Size Тогда
			Возврат Ложь;
		Иначе
			Возврат Истина;
		КонецЕсли;
	Иначе
		Возврат Истина;
	КонецЕсли;
КонецФункции
//------
Функция Передача(Ст,ФСО,Сост)
	Попытка
		ФСО.CopyFile(Ст.Источник,Ст.Приемник);
		Ст.Ждать=ТекущаяДата()+Константы.ЖдатьМАКС.Получить();
	Исключение
		ОформитьСбой(Ст,Сост,"> "+ОписаниеОшибки()+Символы.ПС);	
		Возврат Ложь;
	КонецПопытки;
	Возврат Истина;
КонецФункции
//------
Функция ПереносФайла(Ст,ФСО,ТЗ,Сост,Центр=0)
	Если ЕстьФайл(Ст.Источник,ФСО)<0 Тогда
		Попытка
			Если Прав(СокрЛП(Ст.Источник),2)="cf" Тогда
				ПрочитатьЛог(Ст,СокрЛП(Ст.Центр.Место)+"Conf.log",ТЗ,Сост);
			Иначе
				Если Центр=0 Тогда
					Источник=СокрЛП(Ст.База.МестоВ)+СокрЛП(Ст.База.КодЦ)+"-"+СокрЛП(Ст.Центр.КодЦ)+".log";
				ИначеЕсли Центр=1 Тогда
					Источник=СокрЛП(Ст.Центр.МестоВ)+СокрЛП(Ст.Центр.КодЦ)+"-Х.log";
				КонецЕсли;
				ПрочитатьЛог(Ст,Источник,ТЗ,Сост);
			КонецЕсли;
		Исключение
			ОформитьСбой(Ст,Сост,"> "+ОписаниеОшибки()+Символы.ПС);	
		КонецПопытки;
		Возврат Ложь;
	Иначе
		Возврат Передача(Ст,ФСО,Сост);
	КонецЕсли;	
КонецФункции
...Показать Скрыть
4. Юрий Лоек (loekyn) 11.11.14 09:12
Здесь ФСО это

ФСО=Новый COMОбъект("Scripting.FileSystemObject");
5. Сергей Карташев (Elisy) 12.11.14 11:20
Не совсем понятно. При управлении по WMI доступ осуществляется через IP-адрес. А при копировании файла - через файловую систему
ФСО.CopyFile(Ст.Источник,Ст.Приемник);

Не понятно, как этот способ будет работать в случае, если удаленный сервер опубликован через статический внешний IP-адрес? Способ применим, если 2 сервера находятся внутри локальной сети?
6. Юрий Лоек (loekyn) 12.11.14 11:34
(5) Elisy,
Это один из вариантов копирования файлов, для случая когда один комп "видит" другой в сети (Грубо говоря к нему можно обратиться "\\DrugoyServer\..." Или "\\123.0.12.105\...." ). Потрясите своего админа что бы он это организовал. Иначе можно воспользоваться другими средствами, коих масса. Основной смысл в том, что если удаленный комп доступен по сети, то незачем использовать FTP, почту и прочие дополнительные источники вероятного сбоя.
(Согласно "теории отказов" чем больше элементов в системе, тем больше вероятность отказа).
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа