Освобождение лицензий. Регламентное задание


(Разработано для 1С:Документооборот). Доработка решает проблему недостатка лицензий при сравнительно бОльшем количестве активных пользователей. Реализуется путем наблюдения «Протокола работы пользователей» и при отсутствии активности пользователя — отключение его сеанса на сервере.
У нас данная доработка включена как рег. задание в 1С:Документооборот.
Убиваются все сеансы с неактивностью за последние 30 минут, запускать рег. задание можно соответственно каждые 35 минут.

Суть доработки сводится к тому, что постоянно анализируется ПротоколРаботыПользователей на наличие записей с привязкой к пользователю за последние N-минут.

выбрать * из регистрсведений.протоколработыпользователей где пользователь=&юзер и дата > &датаубийства

При этом список пользователей формируется из выборки активных сеансов 

ПолучитьСеансыИнформационнойБазы()

А дальше из списка на закрытие активного сеанса выкидываются полноправные пользователи, пользователи с сеансом в Конфигураторе и пользователи, поименованные в специальной Роли, у меня для примера она называется "ПользователиНеотключаемыеПоНеактивностиСеанса". Другими словами, если вам надо сделать пользователя, который постоянно может сидеть в приложении и не бояться, что система закроет его сеанс, просто добавьте его в эту роль, это может быть полезно для делопроизводителей, топ-менеджеров.

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

АдминистрированиеКластераКлиентСервер.УдалитьСеансыИнформационнойБазы(ПараметрыАдминистрирования,ПараметрыАдминистрированияИБ, Фильтр);

Для этого вам понадобится две учетных записи: администратор кластера, администратор БД

ПараметрыАдминистрирования = АдминистрированиеКластераКлиентСервер.ПараметрыАдминистрированияКластера(); ПараметрыАдминистрирования.АдресАгентаСервера = "имя_сервера";
ПараметрыАдминистрирования.ПортАгентаСервера = 7540; //по умолчанию в 1С используется 1540
ПараметрыАдминистрирования.ПортКластера = 7541; //по умолчанию в 1С используется 1541
ПараметрыАдминистрирования.ИмяАдминистратораКластера = "имя_администратора_кластера";
ПараметрыАдминистрирования.ПарольАдминистратораКластера = "пароль_администратора_кластера";
ПараметрыАдминистрированияИБ = АдминистрированиеКластераКлиентСервер.ПараметрыАдминистрированияИнформационнойБазыКластера(); ПараметрыАдминистрированияИБ.ИмяВКластере = "имя_базы";
ПараметрыАдминистрированияИБ.ИмяАдминистратораИнформационнойБазы = "имя_администратора_базы"; ПараметрыАдминистрированияИБ.ПарольАдминистратораИнформационнойБазы = "пароль_администратора_базы";

У всей этой доработки есть один несущественный минус — т.к. не все события пишутся в ПротоколРаботыПользователей, то существует ненулевая вероятность, что пользователя "выкинет" даже если он вёл активность в приложении, но процент этих событий мал. 

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

 
Процедура УбитьСеансыВНочи() Экспорт

Таб = Новый ТаблицаЗначений;
Таб.Колонки.Добавить("Пользователь");
Таб.Колонки.Добавить("ПользовательСсылка");
Таб.Колонки.Добавить("Номер");
//Таб.Колонки.Добавить("Поле");
СеансыИнформационнойБазы = ПолучитьСеансыИнформационнойБазы();
НомераСеансов = Новый Массив;
стСписокКРасстрелу = "";
Для Каждого СеансИБ Из СеансыИнформационнойБазы Цикл
если не Пользователи.ЭтоПолноправныйПользователь(СеансИБ.Пользователь, Истина) и СеансИБ.ИмяПриложения = "1CV8C" тогда
ЗадачиЗапрос = Новый Запрос; //Не отключаются пользователи с полными правами, конфигураторы и фоновые задания
ЗадачиЗапрос.Текст = "выбрать * из регистрсведений.протоколработыпользователей где пользователь=&юзер и дата > &датаубийства";
Пользователь = Справочники.Пользователи.НайтиПоНаименованию(СеансИБ.Пользователь.ПолноеИмя,Истина);

стРоли = РегистрыСведений.ИсполнителиЗадач.РолиИсполнителя(Пользователь);
стРольПерманентногоПользователя = Справочники.РолиИсполнителей.НайтиПоНаименованию("ПользователиНеотключаемыеПоНеактивностиСеанса",Истина); //тут прописывается имя роли, входящие в нее пользователи не отключаются
стПерманентныйПользователь = Ложь;
Для Каждого ЭлементМассива из стРоли Цикл
если ЭлементМассива.Владелец.Наименование = стРольПерманентногоПользователя.Наименование тогда
стПерманентныйПользователь = Истина;
Прервать;
конецесли;
КонецЦикла;

Если Не стПерманентныйПользователь Тогда

ЗадачиЗапрос.УстановитьПараметр("юзер", Пользователь);
ЗадачиЗапрос.УстановитьПараметр("датаубийства", ТекущаяДата() - 1800);
Результат = ЗадачиЗапрос.Выполнить();
Если Результат.Пустой() Тогда
Сеанс = Таб.Добавить();
Сеанс.Пользователь             = СеансИБ.Пользователь.Имя;
стСписокКРасстрелу            = стСписокКРасстрелу + СеансИБ.Пользователь.Имя + " ";
Сеанс.Номер                 = СеансИБ.НомерСеанса;
НомераСеансов.Добавить(СеансИБ.НомерСеанса);
//Сеанс.ПользовательСсылка      = НайтиСсылкуПоИдентификаторуПользователя(СеансИБ.Пользователь.УникальныйИдентификатор);
конецесли;

КонецЕсли;

конецесли;
КонецЦикла;

если НомераСеансов.Количество()>0 тогда

ПараметрыАдминистрирования = АдминистрированиеКластераКлиентСервер.ПараметрыАдминистрированияКластера();
ПараметрыАдминистрирования.АдресАгентаСервера = "имя_сервера";
ПараметрыАдминистрирования.ПортАгентаСервера = 7540; //по умолчанию в 1С используется 1540
ПараметрыАдминистрирования.ПортКластера = 7541; //по умолчанию в 1С используется 1541
ПараметрыАдминистрирования.ИмяАдминистратораКластера = "имя_администратора_кластера";
ПараметрыАдминистрирования.ПарольАдминистратораКластера = "пароль_администратора_кластера";
ПараметрыАдминистрированияИБ = АдминистрированиеКластераКлиентСервер.ПараметрыАдминистрированияИнформационнойБазыКластера();
ПараметрыАдминистрированияИБ.ИмяВКластере = "имя_базы";
ПараметрыАдминистрированияИБ.ИмяАдминистратораИнформационнойБазы = "имя_администратора_базы";
ПараметрыАдминистрированияИБ.ПарольАдминистратораИнформационнойБазы = "пароль_администратора_базы";

СтруктураСеанса = Новый Структура;
СтруктураСеанса.Вставить("Свойство", "Номер");
СтруктураСеанса.Вставить("ВидСравнения", ВидСравнения.ВСписке);
СтруктураСеанса.Вставить("Значение", НомераСеансов);
Фильтр = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(СтруктураСеанса);

АдминистрированиеКластераКлиентСервер.УдалитьСеансыИнформационнойБазы(ПараметрыАдминистрирования,ПараметрыАдминистрированияИБ, Фильтр);

ЗаписьЖурналаРегистрации("УдалениеПользователейВНочи", УровеньЖурналаРегистрации.Информация, , , "Список пользователей к отключению: "
+ стСписокКРасстрелу);

конецесли;

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

 

22 Comments

  1. sergathome

    Эх ребяты, где ж вы были года 3 назад, когда нас угораздило вляпаться в релиз платформы, который не освобождал лицензии…

    Reply
  2. spezc

    А штатное время засыпания и время завершения спящего? Чем хуже.

    Reply
  3. o4karek

    А каким образом формируется анализируемый регистр сведений?

    ЗЫ: С ДО не знаком

    Reply
  4. aabogachev

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

    настройки в базе по освобождению зависших/спящих сеансов сделаны, но таковыми сеансы становятся только тогда, когда приложение завершило свою работу «некорректно»

    Reply
  5. aabogachev

    (4) есть общий модуль «ПротоколированиеРаботыПользователей». Там куча функций и процедур, которые пишут в этот РС в зависимости от тех или иных событий и действий пользователя в системе (кстати, чтобы это происходило должна быть включена настройка «Настройка и Администрирование» — «Настройка прав доступа» — «Протоколировать работу пользователей»)

    Например когда пользователь открывает приложение происходит это

    ПротоколированиеРаботыПользователей.ЗаписатьВходВСистему();

    и т.п.

    вот (скриншот) список событий которые РС протоколирует

    Reply
  6. o4karek

    (6) Понял. Т.е. это не совсем честная активность? Это так активность, которую отслеживает подсистема ДО?

    Т.е. если я решу доработать ДО и воткну туда что-то, что этим общим модулем не пользуется, предложенное регламентное задание может этот сеанс выстричь даже во время работы. Я правильно понял?

    Reply
  7. herfis

    Идея проста и понятна. Весь дьявол — в критериях определения активности.

    Если пользователь, скажем, в течение получаса заполняет новый документ — это как зафиксируется в протоколе?

    Reply
  8. aabogachev

    (7) если вы доработаете и будете отслеживать «свои» действия, которые запихнете в этот РС — то конечно оно отработает запрос, найдет их и не выкинет пользователя. выборка то будет не пустой. только вид действия надо будет добавить в Перечисление, скриншот которого я положил выше. все просто.

    Reply
  9. aabogachev

    (8) никак. как только мы столкнемся с подобного рода проблемами — мы передвинем планку, то есть рег задание будет стартовать не раз в 30 минут, а раз в час и т.п.

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

    Reply
  10. o4karek

    (9) Ну тогда (ИМХО) стоит откорректировать заголовок и/или начало статьи. Я подумал, что сделано некоторое универсальное регламентное задание, которое для примера вклеено в ДО.

    А у вас сделано специализированный вариант для конкретной конфы. Он не становится от этого плохим, просто стоит сразу об этом сказать 🙂

    Reply
  11. herfis

    (10) Мне показалось, что вы позиционировали решение как универсальное.

    Если речь об учетной системе предприятия, то подобные сценарии не являются чем-то невероятным.

    «Честную» активность, к сожалению, можно будет определять только если соответствующую возможность добавят в платформу.

    Как промежуточный вариант, в принципе, можно реализовать такое: выдавать пользователю предупреждение о завершении сеанса с большим таймаутом при длительном отсутствии активности по протоколу а при отмене предупреждения пользователем — записывать это событие в протокол.

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

    Reply
  12. aabogachev

    (12) вы правы, если делать «универсально», то так и следует поступить, но это можно оставить для тех, кто захочет оптимизировать данное решение, у меня «это» работает уже полгода, как в том анекдоте про сисадмина — «работает и работает, не надо ничего трогать».

    Reply
  13. kolya_tlt

    как и все разработчики 1С:ДО решение наполнено запросами в цикле, очень жаль …

    зы у вас реально Елен Николаевн так много в компании?

    Reply
  14. aabogachev

    (14) я к сожалению не разработчик, я просто временно решаю проблемы. так сказать создаю костыли. и мне за это платят.

    Елен Николавн да, много.

    Reply
  15. teller

    (15)

    так сказать создаю костыли. и мне за это платят

    — тут все такие, никто блокчейны не изобретает

    Reply
  16. kolya_tlt

    (15) вообще сложно человека перед конфигуратором назвать разработчиком, но мы все эти делаем, а в резюме пишим — программист 🙂

    Reply
  17. androgin

    Вот это запускается у каждого пользователя само каждые 20 мин (подписка или через обработчик ожидания — сами решите, как вам угодно!):

    // на клиенте
    Процедура ЗавершитьНеактивныйСеансКлиент() Экспорт
    СеансНеАктивен = ПростойСеансаПоЖурналуРегистрации();
    Если СеансНеАктивен Тогда
    ЗавершитьРаботуСистемы();
    КонецЕсли;
    КонецПроцедуры
    
    // на сервере
    Функция ПростойСеансаПоЖурналуРегистрации() Экспорт
    
    СеансНеАктивен = Ложь;
    
    // только последние 20 минут
    ДатаНачала = ТекущаяДата()-1200;
    ДатаОкончания = ТекущаяДата();
    
    МассивСобытий = Новый Массив;
    МассивСобытий.Добавить(«_$Data$_.New»);
    МассивСобытий.Добавить(«_$Data$_.Update»);
    МассивСобытий.Добавить(«_$Data$_.Delete»);
    МассивСобытий.Добавить(«_$Data$_.Post»);
    МассивСобытий.Добавить(«_$Data$_.Unpost»);
    
    ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(Пользователи.ИдентификаторПользователяИБ());
    Фильтр = Новый Structure(«ДатаНачала, ДатаОкончания, Пользователь, Событие», ДатаНачала, ДатаОкончания, ПользовательИБ, МассивСобытий);
    
    ТЗ = Новый ТаблицаЗначений;
    ТЗ.Колонки.Добавить(«Дата»);
    ТЗ.Колонки.Добавить(«Пользователь»);
    ТЗ.Колонки.Добавить(«ИмяПользователя»);
    ТЗ.Колонки.Добавить(«Компьютер»);
    ТЗ.Колонки.Добавить(«Событие»);
    ТЗ.Колонки.Добавить(«ПредставлениеДанных»);
    
    ВыгрузитьЖурналРегистрации(ТЗ, Фильтр,»Дата, ИмяПользователя, Компьютер, Событие, ПредставлениеСобытия , ПредставлениеДанных»);
    
    Если ТЗ.Количество() = 0 Тогда
    СеансНеАктивен = Истина;
    КонецЕсли;
    
    Возврат СеансНеАктивен;
    
    КонецФункции
    

    Показать

    Меня устраивает )))

    Reply
  18. androgin

    (13) а чем журнал регистрации не устраивает?

    Reply
  19. mikolamail

    (19)

    подписка

    Не понятно как можно Подписку использовать. Ведь она срабатывает на определенные события. А раз есть событие, значит сеанс еще активен…

    Reply
  20. androgin

    (21) это я так выразился, давая понять, что «пихайте куда хотите» )))

    Reply
  21. ildary

    (19) спасибо за интересную реализацию. Жалко что в БСП такого нет.

    Reply
  22. Svet_Serg
    Для этого вам понадобится две учетных записи: администратор кластера,

    У нас нет администратора кластера. Совсем. Что можно вписать вместо учётки админа кластера?

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *