gifts2017

«Жемчужный» доступ к 1С: Предприятию 8.2

Опубликовал Вячеслав Н. Бойко (boy13) в раздел Администрирование - Системное

Думаю, всем вам известен такой программный продукт, как 1С: Предприятие 8.2. И, наверное, многим из вас известен тот факт, что к 1С: Предприятию можно подключиться, используя OLE/COM-соединение. А многие ли из вас знают, что с помощью OLE/COM-соединения можно не только выполнять программный код 1С, но и “управлять” сервером 1С: Предпрития? К примеру, можно подключиться к Агенту кластера серверов 1С: Предприятия, получить список открытых клиентских сессий, прочитать информацию о выданных им лицензиях… К тому же, наличие варианта подключения посредством OLE/COM-соединения расширяет в арсенале программиста добавляет возможность выбора языка программирования, отличного от встроенного языка 1С: Предприятия. Можно выбрать любой язык, который способен работать с OLE/COM-компонентами: будь то VB.Net, C#.Net, или Java, или даже… Perl. Да, вы не ослышались. Именно Perl.
Итак…

Задача.


Необходимо реализовать автоматический рестарт службы Агент сервера 1С: Предприятия 8.2 с помощью планировщика задач Windows. Но перед рестартом необходимо проверить, не работает ли кто-нибудь в базе Base, расположенной на сервере 1С: Предприятия. Если кто-нибудь работает, то перезапуск службы недопустим.

Решение.


Итак. Задача поставлена. Основной проблемой для меня стало условие проверки существующих подключений к определенной БД. Для реализации поставленной задачи я решил не использовать встроенный язык 1С: Предприятия, ни один из .Net-языков, а также Java (only console, only true linux-way).

В синтаксис-помощнике 1С: Предприятия был найден метод проверки наличия подключений к БД, но он предполагал использование OLE/COM-соединения к Агенту сервера 1С: Предприятия. Что ж… Приступим.


Выдержка из синтаксис-помощника

Соединение с агентом сервера (IServerAgentConnection)
GetInfoBaseConnections (GetInfoBaseConnections)
Синтаксис:

GetInfoBaseConnections(, )

Параметры:

(обязательный)
Тип: Кластер серверов. Кластер серверов, для которого должен быть получен массив описаний соединений.

(обязательный)
Тип: Описание информационной базы. Информационная база, для которой должен быть получен массив описаний соединений.

Возвращаемое значение:
Тип: COMSafeArray. Массив описаний соединений кластера. Каждое описание соединения представлено объектом с интерфейсом Описание соединения.

Описание:
Получает массив описаний соединений информационной базы.

Доступность:
Интеграция.

Для работы с OLE/COM-объектами средствами Perl нам понадобиться задействовать модуль Win32::OLE. В используемой мной сборке StrawberryPerl данный модуль уже был предустановлен. Но даже если это не так, можно установить его через CPAN:

C:\Perl\perl\bin\cpan Win32::OLE

Чтобы получить список соединений базы данных 1С, нам нужно:

  • Создать COM-подключение к объекту V82.COMConnector;
  • Подключиться к агенту кластера серверов 1С: Предприятия;
  • Получить объект кластера 1С;
  • Авторизоваться на кластере;
  • Получить список баз 1С на этом кластере;
  • Найти необходимую нам базу;
  • Получить список активных соединений к этой базе данных.



Вот код с комментариями, который получает список активных соединений клиентов с базой данных 1С: Предприятия, расположенной на сервере 1С: Предприятия.

use strict;

use warnings;


use Win32::OLE;


use Data::Dumper;


sub getConnectionsCount {

    # имя сервера (с портом) и имя базы будем получать из параметров метода

    my ($server, $dbname) = @_;

    # создаем COM-соединение

    my $ole1c = Win32::OLE->new("V82.COMConnector") or die "Could not create OLE-connector!\n";

    # создаем объект Агента сервера 1С:Предприятия

    my $_agent = $ole1c->ConnectAgent($server) or die "Could not connect to server!\n" . cnv(Dumper $ole1c->ErrorDescription());

    # получаем объект кластера (у меня один кластер)

    my $_cluster = $_agent->GetClusters()->[0] or die "Could not get cluster 0 from array\n";

    # авторизуемся на кластере серверов.

    # Авторизоваться надо с помощь логина и пароля Администратора кластера серверов,

    # которого можно задать в утилите Администрирования сервера 1С:Предприятия.

    # не путайте этого администратора с администратором базы данных, что заводится

    # через конфигуратор в пользователях.

    # У меня нет администраторов, оставляю пустыми параметры логина и пароля.

    $_agent->Authenticate($_cluster, "", "");

    # получаем список баз данных, расположенных в данном кластере

    my $_basesinfo = $_agent->GetInfoBases($_cluster);

    # найдем нашу базу данных

    my @_bases = grep { defined $_->{Name} && $_->{Name} =~ m/^$dbname$/ } @$_basesinfo;

    # она точно будет одна

    my $_base = $_bases[0];

    # заведем счетчик подключений

    my $connCounter = 0;

    # получим все активные подключения или сразу вернем 0

    my $_baseconns = $_agent->GetInfoBaseConnections($_cluster, $_base) or return 0;

    # “отсечем” все соединения по типу приложения, на которые можно не обращать внимание.

    # если эти подключения есть, то можно смело перегружать службу

    # Агента сервера 1С:Предприятия

    foreach (@$_baseconns) {

       # нам не важны планировщики задач и подключения через консоль кластера

       if ($_->{Application} !~ m/JobScheduler/ && $_->{Application} !~ m/SrvrConsole/) {

           $connCounter++;

       }

    }


    return $connCounter;

}


# вызов очень простоой.

# только порт нужно указывать тот, который слушает агент кластера серверов 1С:Предприятия

print “There are “ . getConnectionsCount(“localhost:1540”, “Base”) . “ active connections\n”;

 


Перезапустить службу Агента можно с помощью команд “net start” и “net stop”:

net stop 
net start



Полный код, выполняющий поставленную задачу я расположил на Bitbucket-е (для тех, у кого есть доступ). У кого нет доступа, привожу здесь:

 

use strict;
use warnings;

#use locale;
#use encoding 'cp1251';

use utf8;

use Win32::OLE;
use Win32::OLE::Variant;

# раскомментировать, если непонятные ошибки или не работает
# но тогда не будет видно комментариев о перезапуске службы
#Win32::OLE->Option(Warn => 3);
#binmode STDOUT, ':encoding(cp1251)';
#`chcp 1251`;

use Data::Dumper;

my $DEBUG = 0;

# настройки
my $_S = {
    host => 'localhost', #адрес агента сервера 1С
    port => '1540', #порт агента!
    bases => [ #список баз
        {
            name => 'GTVCopy', #имя базы, которую надо проверять, как оно задано в кластере сервера 1С
            killall => 0 #удалять ли открытые сессии
        },
        {
            name => 'MET',
            killall => 1
        }
    ],
    serviceName => '1C:Enterprise 8.2 Server Agent (x86-64)', #имя службы сервера 1С
    serviceRestart => {
        need => 1, #нужно ли рестартовать службу сервера 1С
        ifNoConnectionsOnly => 1 #только в том случае, если нет подключений ко всем БД из списка выше
    }

};



sub prn {
    print "\n" . shift . "\n" if $DEBUG;
}

sub getConnectionsCount {
    my ($server, $dbname) = @_;

    my $counter = 1;
    print "GET CONNECTIONS\n" if $DEBUG;

    prn 1;
    my $ole1c = Win32::OLE->new("V82.COMConnector") or die "Could not create OLE-connector!\n";
    print "COM-connector created...\n" if $DEBUG;
    prn 2;
    my $_agent = $ole1c->ConnectAgent($server) or die "Could not connect to server!\n" . cnv(Dumper $ole1c->ErrorDescription());
    prn 3;
    my $_cluster = $_agent->GetClusters()->[0] or die "Could not get cluster 0 from array\n";
    print "CLUSTER: \n" . Dumper $_cluster if $DEBUG;
    prn 4;
    $_agent->Authenticate($_cluster, "", "");

    prn 5;
    my $_basesinfo = $_agent->GetInfoBases($_cluster);
    my @_bases = grep { defined $_->{Name} && $_->{Name} =~ m/^$dbname$/ } @$_basesinfo;
    my $_base = $_bases[0];
    print Dumper $_base if $DEBUG;

    prn 6;
    my $connCounter = 0;
    my $_baseconns = $_agent->GetInfoBaseConnections($_cluster, $_base) or return 0;
    foreach (@$_baseconns) {
        if ($_->{Application} !~ m/JobScheduler/ && $_->{Application} !~ m/SrvrConsole/) {
            print $_->{Application} . "\n" if $DEBUG;
            $connCounter++;
        }
    }

    return $connCounter;
}

sub killAllSessions {
    my ($server, $dbname) = @_;

    my $counter = 1;
    print "KILL SESSIONS\n" if $DEBUG;

    prn 1;
    my $ole1c = Win32::OLE->new("V82.COMConnector") or die "Could not create OLE-connector!\n";
    print "COM-connector created...\n" if $DEBUG;
    prn 2;
    my $_agent = $ole1c->ConnectAgent($server) or die "Could not connect to server!\n" . cnv(Dumper $ole1c->ErrorDescription());
    prn 3;
    my $_cluster = $_agent->GetClusters()->[0] or die "Could not get cluster 0 from array\n";
    print "CLUSTER: \n" . Dumper $_cluster if $DEBUG;
    prn 4;
    $_agent->Authenticate($_cluster, "", "");

    prn 5;
    my $_basesinfo = $_agent->GetInfoBases($_cluster);
    my @_bases = grep { defined $_->{Name} && $_->{Name} =~ m/^$dbname$/ } @$_basesinfo;
    my $_base = $_bases[0];
    print Dumper $_base if $DEBUG;

    my $_sessions = $_agent->GetSessions($_cluster);

    return unless defined $_sessions;

    my @_sessions = grep { defined $_->{infoBase}->{Name} && $_->{infoBase}->{Name} eq $_base->{Name} } @$_sessions;
    #print Dumper @_sessions;

    foreach (@_sessions) {
        if ($_->{AppID} !~ m/JobScheduler/ && $_->{AppID} !~ m/SrvrConsole/) {
            $_agent->TerminateSession($_cluster, $_);
        }
    }
}




#print "Connections count: " . getConnectionsCount("localhost", ) . "\n";
#killAllSessions("localhost:2040", $ARGV[0]);
#print "Connections count: " . getConnectionsCount("localhost", $ARGV[0]) . "\n";

my $host = $_S->{host} . $_S->{port} ne '' ? ":" . $_S->{port} : '';

my $cons = 0;

for my $base (@{$_S->{bases}}) {
    print "Checking connections in base named $base->{name}...";
    my $connCount = getConnectionsCount($host, $base->{name});
    print "$connCount\n";
    if ($base->{killall} && $connCount > 0) {
        print "Killing connections...\n";
        killAllSessions($host, $base->{name});
    }
    $cons += getConnectionsCount($host, $base->{name});
}

print "Total active connections: $cons\n";

if ($_S->{serviceRestart}->{need}) {
    if ($_S->{serviceRestart}->{ifNoConnectionsOnly} && $cons > 0) {
        print "Will not restart service.\n";
        exit;
    }
    print "Restarting service named $_S->{serviceName}...\n";
    print `net stop "$_S->{serviceName}"`;
    print `net start "$_S->{serviceName}"`;
}

См. также

PowerTools от 1 000
Подписаться Добавить вознаграждение
Комментарии
1. Adapter Бахтыреев (adapter) 01.04.13 14:27
Хорошая статья, тока в этой "перловке" мало кто из 1С-ников разбирается, поэтому применить на практике не сможет. А вот если сделать это на 1С, то получаются отличные инструменты. У меня например вот такой:
"Мониторинг кластеров серверов 1С" http://www.adaptersoft.ru/helpdesk/?p=501
Показывает расход ключей, реализует механизм "лимита производительности", собирает историю, показывает диаграммы\графики

Ну а вместо назначенных заданий windows в конфе используются регламентные задания 1С. Это позволяет запускать скрипты на языке 1С и там уже использовать возможности монитора кластера.
2. andrewks 01.04.13 15:27
(1) adapter, я так понял , разработка платная?
3. Вячеслав Н. Бойко (boy13) 01.04.13 15:39
(1) adapter, я как раз и не хочу использовать встроенный язык 1С.
4. Adapter Бахтыреев (adapter) 01.04.13 15:53
да разработка платная, мониторинг кластера это только одна из доп. функций системы. Такую обработку не сложно сделать и самостоятельно, все по справочнику функций.
5. Ийон Тихий (cool.vlad4) 01.04.13 16:42
а почему perl, - я так понимаю из-за вкусовщины? поскольку это под виндами(а иначе быть не может), то можно выбрать, то, что уже есть - либо vbs, либо powershell. просто не понимаю, зачем использовать этот страшный perl (лучше уж python тогда, да простят меня апологеты perl-а). Но за разнообразие +
6. Вячеслав Н. Бойко (boy13) 01.04.13 16:46
(5) cool.vlad4, потому что я его знаю, и пока нет желания изучать vbs, powershell, python и иже с ними. И чтобы остальные не боялись перл.
7. Алексей Новиков (Новиков) 01.04.13 20:37
За перл - плюс.

Правда не понятно, что мешает автору сначала поставить во всех базах режим "мягкого выгона" всех пользователей, дождаться пока оные выйду и уже потом сделать ГАРАНТИРОВАННЫЙ рестарт сервера 1С? При этом вся моя предыдущая строка, впишется в один батник, число строчек в котором будет напорядок меньше, чем в приведенном скрипте :) Это не критика, просто здоровый интерес - почему было принято именно такое перловское решение?
8. Станислав Раташнюк (stanru1) 01.04.13 21:34
Ставлю "плюс" - ради хакерской ценности круто управлять 1с через перл. Для совместимости и переносимости стоило бы еще написать на стороне 1с веб-сервисы, которые бы позволяли "рулить" кластером без COM (например, из линукса).
9. ediks (ediks) 01.04.13 21:48
А почему "Жемчужный" доступ? PERL <> PEARL. :)
10. Ийон Тихий (cool.vlad4) 01.04.13 22:22
Название языка представляет собой аббревиатуру, которая расшифровывается как Practical Extraction and Report Language — «практический язык для извлечения данных и составления отчётов»[1]. Первоначально аббревиатура состояла из пяти символов и в таком виде в точности совпадала с английским словом pearl («жемчужина»). Но затем стало известно, что такой язык существует (см. PEARL (англ.)), и букву «a» убрали.
(9) ediks,
11. Сергей Ожерельев (Поручик) 01.04.13 23:38
(1) Я разбираюсь. Perl когда-то был моим основным языком, потом уже эта ваша 1С стала.
12. Вячеслав Н. Бойко (boy13) 02.04.13 00:47
(7) Новиков, Ничего не мешает. Кроме упоротости автора, т.е. меня :), с какого-то времени по этому древнему языку.
Захотелось сделать все с минимальным участием 1С. Гибко и переносимо.
13. Вячеслав Н. Бойко (boy13) 02.04.13 00:49
(9) ediks, В интернете много аналогий перла с "жемчугом". Хоть тут.
14. Вячеслав Н. Бойко (boy13) 02.04.13 00:50
(11) Поручик, :) а я вот - наоборот, ухожу, расширяюсь...
15. Вячеслав Н. Бойко (boy13) 02.04.13 00:52
(8) stanru1, да, круто. Но, повторюсь, я не хотел использовать никакую базу 1С и ее внутренний язык. Только извне.
16. Вячеслав Н. Бойко (boy13) 02.04.13 00:57
ВСЕМ ВОПРОШАЮЩИМ О ВЫБОРЕ СРЕДСТВА отвечу еще раз. Ну вот захотелось мне разнообразия. Почему нет? just for fun с выгодой для работы.
Да и к тому же, если глючит 1с, то надо бы избежать ее использования для ее же "починки". :)
17. Алексей Новиков (Новиков) 02.04.13 09:34
(16) boy13, я тоже иногда на перле пишу, но серьезного давно не писал :) Ну джаст фор фан, так джаст фор ван, как говориться.
18. Владимир Буоц (vbuots) 02.04.13 11:29
Отличная статья, а вот так это можно сделать с помощью Power Shell на win сервере:
Stop-Service "1C:Enterprise 8.2 Server Agent (x86-64)" -Force
Start-Sleep -s 15
Get-Process -Name rphost*,rmngr*,ragent* | Stop-Process -Force
Start-Sleep -s 5
Start-Service "1C:Enterprise 8.2 Server Agent (x86-64)"
...Показать Скрыть

Однако удобнее всего повесить эти команды на SQL Server Agent.
19. Юрий Осипов (yuraos) 02.04.13 14:18
А есть ли стандартная раскраска кода для "Жемчуга" ???
20. qweasd qweasdzc (serega3333) 02.04.13 16:14
нормальный замес, перл круто смотрится, спасибо за инфу что можно и перл прикрутить
21. Вячеслав Н. Бойко (boy13) 02.04.13 22:49
(18) vbuots, у нас он отказался job-ы выполнять :)
22. Вячеслав Н. Бойко (boy13) 02.04.13 22:49
23. Вячеслав Н. Бойко (boy13) 02.04.13 22:50
(20) serega3333, пожалуйста! У меня есть еще одна разработка на перле, которая активно используется. Но 1С там опосредованно. Бекапы баз скульных делаю замысловато. Если надумаю, выложу.
24. Владимир Буоц (vbuots) 03.04.13 09:28
(21) запуск службы надо сделать от имени Администратора (например локального). И погуглить как разрешить Power Shell выполнять команды от того или иного пользователя...
25. Maximilian Alekseevich (1cmax) 03.04.13 12:06
Перл - это хорошо, когда он сам по себе парсит строки, часто юзаю микроскрипты для обработки файлов с использованию регулярных выражений.
, но не вижу смысла вязать с 1с..
26. Алексей Роза (DoctorRoza) 03.04.13 12:26
Крутотень или изврат? :) Но + однозначно!
27. Антон Стеклов (asved.ru) 03.04.13 17:29
перл придумали марсиане. Из соседнего с создателями регэкспов канала.
28. Вячеслав Н. Бойко (boy13) 03.04.13 18:54
(24) vbuots, не, слетели DCOM-библиотеки какие-то.
29. andrewks 03.04.13 19:23
(27) asved.ru, а кто же тогда придумал пролог?
31. Константин Носов (NosovK) 03.04.13 23:42
(1) adapter, немогу оценить всю обработку, но модуль мониторинга кластера - чудесен.
А нет у вас его в виде отдельной обработки?
32. Adapter Бахтыреев (adapter) 04.04.13 14:06
Нету, механизм шире чем просто обработка, поэтому является частью конфигурации. Обсуждение HD предлагаю вести
в профильной теме http://infostart.ru/community/groups/740/
33. artem666 Bogomaz (artem666) 05.04.13 15:42
Автор, исправь вводную статьи, java не поддерживает OLE/COM
34. Ийон Тихий (cool.vlad4) 05.04.13 15:48
35. Ийон Тихий (cool.vlad4) 05.04.13 15:52
(33) и если говорить про COM и про все, то что имеет доступ к api винды, как оно может не поддерживать COM?
36. Александр Крынецкий (echo77) 07.04.13 14:55
На WSH кто-нибудь подобное может исполнить?
37. Денис Яковлев (iceflash) 08.04.13 14:53
Вообще-то, существует штатный механизм, при котором происходит "мягкий" рестарт процессов rphost самим сервером 1с через процесс менеджера кластера сервера(процесс rmngr). Настраивается элементарно через оснастку сервера 1с предприятия. К чему такие костыли?
Незнание заставляет изобретать велосипеды?;)
Вообще можно задать параметры, по лимитам привышения, и сколько должен продержаться лимит (например процесс выделил сверх лимита память, но возможно это ресурсоемкая операция, и рубить ее сразу смысла нет, ставим отсрочку в 1-2 минуты, если память сверх лимита рестарт такого процесса). Причем после рестарта, клиенты подвиснут на определенный момент времени, если будет выполняться какая то операция, и переключаться на свободный процесс который может их принять. И для пользователя это будет "не заметно".

Доступ через ole/com довольно ресурсоемкая операция, все зависит от задачи конечно, возможно просто не удачный пример=)
38. Ak A (frc) 11.04.13 00:57
(35) cool.vlad4,
то что имеет доступ к api винды, как оно может не поддерживать COM?

Так и может.
COM не равно API.
Наоборот, АПИ "делает" COM.
COM - технология доступа к приложениям, лицензированная Микрософт. С какого пререпугу COM опустился на уровень АПИ и стал поддерживаться любым приложением (и процессом, им генерируемым)?
(3) boy13,
все это прекрасно реализуется из 1С - все теми же средствами.
Причем бесплатно.