Серверные вызовы в управляемых формах 1С: устройство, цена и оптимизация

· 9 мин

Содержание

Вызов сервера в 1С — это обращение клиентского кода (формы на тонком, веб- или мобильном клиенте) к процедуре, которая выполняется в серверном контексте. Он нужен каждый раз, когда коду на клиенте не хватает данных или прав: прочитать из базы, записать документ, выполнить запрос. Каждый такой вызов идёт по сети, поэтому число серверных вызовов на одно действие пользователя напрямую определяет, быстро ли «отзывается» форма.

Если коротко о выборе: процедура с &НаСервере передаёт на сервер весь контекст формы и стоит дороже, &НаСервереБезКонтекста передаёт только параметры и работает быстрее. Остальное в статье — как это устроено внутри, где ломается и как проверить, сколько вызовов делает ваша форма.

Что происходит, когда клиент зовёт сервер

В файловой и клиент-серверной базе управляемая форма живёт в двух местах одновременно. На клиенте работает интерфейс и клиентский код, на сервере — данные, запросы и основная логика. Между ними стоит граница, и пересечь её можно только явным вызовом процедуры или функции с серверной директивой.

Разберём, что делает платформа за один вызов:

  1. Клиент собирает параметры вызова и (если нужно) состояние формы.
  2. Данные сериализуются и уходят на сервер: по сети в клиент-серверном режиме, через внутренний канал в файловом.
  3. Сервер создаёт контекст, выполняет код, формирует результат.
  4. Изменённые данные формы и возвращаемые значения упаковываются и возвращаются клиенту.
  5. Клиент обновляет интерфейс и продолжает выполнение.

Пока идут шаги 2–4, клиентский код стоит и ждёт ответа. Сеанс пользователя при этом «замирает» — для него это обычная пауза в работе формы.

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

Директивы компиляции: какой вызов выбрать

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

ДирективаГде выполняетсяЧто передаётсяКогда использовать
&НаКлиентена клиентеничегоработа с интерфейсом, открытие форм, вопросы пользователю
&НаСерверена серверепараметры + контекст формы (реквизиты, данные)нужно читать и менять реквизиты формы
&НаСервереБезКонтекстана серверетолько параметрызапрос в базу, расчёт, получение значения
&НаКлиентеНаСервереБезКонтекстаи там и тамтолько параметрыобщая функция, которую зовут с обеих сторон

Главное различие — в контексте. Вызов &НаСервере тащит на сервер реквизиты формы и возвращает их обратно. Если в форме большая табличная часть на несколько тысяч строк, она едет туда-сюда при каждом вызове. Вызов &НаСервереБезКонтекста об этом не знает: на сервер уходят только те параметры, которые вы передали явно.

Что можно передавать в параметрах

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

Практическое правило: передавайте на сервер минимум. Не «всю структуру со всем подряд», а те три поля, которые реально нужны.

Где вызов сервера обходится дорого

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

  • ⚠️ Вызов сервера в цикле. Клиент перебирает 200 строк и для каждой зовёт серверную функцию. Это 200 обращений вместо одного.
  • 📦 Лишний контекст. Процедура с &НаСервере вызывается ради одного значения, а вместе с ней едет вся форма.
  • 🔁 Цепочки обращений. Обработчик события вызывает сервер, потом по результату снова сервер, потом ещё раз.
  • 🌐 Сетевая задержка. Для веб-клиента, удалённого офиса или облака каждое обращение стоит десятки и сотни миллисекунд сверху, причём независимо от того, насколько быстро работает сервер.
  • 🐘 Большие возвращаемые данные. Сервер отдаёт клиенту десятки тысяч строк, а на экране нужно двадцать.

Простой пример расчёта. Пусть сетевая задержка в обе стороны — 80 мс, а серверная часть выполняется за 10 мс. Цикл на 150 строк с вызовом сервера на каждой итерации даст около 150 × (80 + 10) = 13 500 мс, то есть почти 14 секунд ожидания. Тот же объём работы одним вызовом с пакетной обработкой — около 80 + 150 × 10 = 1 580 мс, то есть меньше двух секунд. Цифры условные, но порядок разницы реальный.

Примеры кода: как не надо и как надо

Плохой вариант: сервер вызывается для каждой строки.

&НаКлиенте
Процедура ЗаполнитьЦены(Команда)
    Для Каждого Строка Из Объект.Товары Цикл
        Строка.Цена = ПолучитьЦенуНаСервере(Строка.Номенклатура);
    КонецЦикла;
КонецПроцедуры

&НаСервере
Функция ПолучитьЦенуНаСервере(Номенклатура)
    // запрос к регистру на каждую строку
    Возврат ЦенаИзРегистра(Номенклатура);
КонецФункции

Здесь две проблемы сразу: вызов в цикле и лишний контекст из-за &НаСервере.

Хороший вариант: одно обращение с набором данных.

&НаКлиенте
Процедура ЗаполнитьЦены(Команда)
    Номенклатура = Новый Массив;
    Для Каждого Строка Из Объект.Товары Цикл
        Номенклатура.Добавить(Строка.Номенклатура);
    КонецЦикла;

    Цены = ПолучитьЦены(Номенклатура);

    Для Каждого Строка Из Объект.Товары Цикл
        Строка.Цена = Цены[Строка.Номенклатура];
    КонецЦикла;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьЦены(Номенклатура)
    Результат = Новый Соответствие;
    // один запрос с условием "В (&Номенклатура)"
    // заполняет соответствие "номенклатура -> цена"
    Возврат Результат;
КонецФункции

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

Если серверная процедура меняет реквизиты формы, её нельзя переводить в &НаСервереБезКонтекста — изменения не вернутся в форму. В этом случае оставляйте контекстный вариант или возвращайте результат явно и записывайте его на клиенте.

Типичные ошибки при работе с серверными вызовами

Часть ошибок видна сразу, часть проявляется только на живой базе с большими объёмами.

  • Вызов сервера «на всякий случай». Проверка условия, которую можно сделать на клиенте по уже имеющимся данным, всё равно уходит на сервер.
  • Обращение к серверу из события, которое срабатывает часто. Например, при каждом изменении поля ввода или при выводе каждой строки списка.
  • Передача объекта, который не сериализуется. Попытка отправить на сервер то, что существует только в клиентском контексте, заканчивается ошибкой выполнения.
  • Ожидание изменений формы после &НаСервереБезКонтекста. Процедура отработала, а реквизиты не обновились: контекста на сервере не было.
  • Дублирование данных. Реквизит формы хранит копию большой таблицы, которая дополнительно ездит при каждом контекстном вызове.
  • Смешение клиентской и серверной логики. Расчёт, который можно целиком сделать на сервере одним вызовом, разбит на кусочки с промежуточными возвратами.

Как посмотреть, сколько вызовов делает ваша форма

Ощущения «тормозит» мало. Нужны цифры: сколько вызовов сделано и сколько времени ушло. Есть несколько способов, от простого к подробному.

  1. Замер вручную. В клиентском коде зафиксируйте ТекущаяУниверсальнаяДатаВМиллисекундах() до вызова и после, выведите разницу. Годится для одного подозрительного места.
  2. Замеры производительности. Встроенный механизм платформы собирает время выполнения ключевых операций и показывает, какие действия самые долгие. Подходит, когда непонятно, где искать.
  3. Технологический журнал. События CALL и SCALL фиксируют серверные вызовы с длительностью. Настраивается файлом logcfg.xml на стороне сервера.
  4. Подсчёт по месту. Временно добавьте счётчик в каждую серверную процедуру и посмотрите, сколько раз её дёрнули за одно действие пользователя.

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

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

Оптимизация: что делать по шагам

Порядок такой: сначала убрать лишнее, потом ускорить оставшееся.

  1. Уберите вызовы из циклов. Соберите данные в массив или соответствие и отправьте одним обращением.
  2. Переведите по возможности на &НаСервереБезКонтекста. Если процедуре не нужны реквизиты формы, контекст на сервер не едет.
  3. Объедините цепочки. Если после первого вызова всегда следует второй, сделайте одну серверную функцию, которая выполняет обе части и возвращает результат целиком.
  4. Сократите передаваемое. Передавайте идентификаторы или ссылки, а не готовые таблицы. Возвращайте только то, что реально отобразит форма.
  5. Кэшируйте на клиенте. Данные, которые за сеанс не меняются (настройки, справочные значения), получите один раз при открытии и держите в реквизите формы.
  6. Используйте кэширование возвращаемых значений. Для общих модулей с неизменными между вызовами результатами подходит соответствующее свойство модуля.
  7. Подумайте об асинхронности. В актуальных версиях платформы часть клиентских операций (диалоги, вопросы пользователю) можно выполнять без блокировки потока.

Контроль: после правок повторите замер на тех же данных и сравните. Если число вызовов не упало, значит правили не то место.

Вызов сервера через сеть: тонкий клиент, веб-клиент и облако

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

Условия работыНа что смотреть в первую очередь
Локальная сеть, тонкий клиенттяжёлые запросы, блокировки
Удалённый офис, VPNчисло вызовов, объём передаваемых данных
Веб-клиентчисло вызовов и размер контекста формы
Облачный сервисчисло вызовов, скорость канала пользователя

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

Если форма внезапно «поплыла» у пользователей из удалённого офиса, а в главном офисе работает нормально, причина почти всегда в числе серверных вызовов, а не в мощности сервера.

Если вызов упал или завис

Когда серверный вызов не возвращается, а форма не реагирует, разберите по порядку.

  1. Подождите: возможно, запрос просто тяжёлый или идёт ожидание блокировки.
  2. Откройте в консоли кластера список сеансов и посмотрите, чем занят сеанс пользователя. Это административное действие, делайте его с правами администратора.
  3. Проверьте журнал регистрации на ошибки в это время.
  4. При подозрении на зависший процесс отметьте, что завершение сеанса или рабочего процесса оборвёт работу пользователей, поэтому выполняйте его вне рабочих часов или после предупреждения.
Диагностика: выберите симптом

Частые вопросы

Чем вызов сервера отличается от запроса к базе?
Вызов сервера — это передача управления из клиентского кода в серверный контекст. Запрос к базе выполняется уже внутри серверного кода. Один вызов сервера может содержать много запросов или ни одного.
Можно ли вообще обойтись без вызовов сервера?
Для работы с данными нет. Но число вызовов можно свести к минимуму: получить всё нужное при открытии формы, а дальше работать с локальными данными до записи.
Что быстрее: `&НаСервере` или `&НаСервереБезКонтекста`?
Второй вариант, если процедуре не нужны реквизиты формы. Контекст не передаётся туда и обратно, объём данных меньше.
Влияет ли режим работы базы на стоимость вызова?
Да. В файловом режиме канала по сети нет, в клиент-серверном и особенно в веб-клиенте появляется сетевая задержка, которая добавляется к каждому обращению.
Как найти самые тяжёлые серверные вызовы?
Используйте замеры производительности для общей картины и технологический журнал с событиями CALL и SCALL для подробностей по длительности.