Клиент, сервер и граница между ними: механизм вызовов в 1С 8.3
Содержание
Вызов сервера в 1С — это переход выполнения кода с клиента на сервер: клиент упаковывает параметры, отправляет их серверу, ждёт ответа и только потом продолжает работу. Каждый такой переход стоит времени сети и сериализации данных, поэтому главное правило оптимизации простое: один вызов сервера на одно действие пользователя, а не вызов в цикле.
Механизм одинаков для всех конфигураций на управляемых формах: БП 3.0, ЗУП 3.1, УТ 11 и самописных решений. Разница только в том, где работает сервер: внутри процесса клиента (файловая база), на отдельной машине (клиент-серверный вариант) или в облаке за веб-сервером.
Что происходит при вызове сервера в 1С
В управляемом приложении код делится на две части. Клиентская работает на компьютере пользователя: открывает формы, реагирует на кнопки, показывает вопросы. Серверная работает рядом с базой данных: читает и записывает объекты, выполняет запросы, проводит документы.
Клиент не умеет работать с базой напрямую. В тонком и веб-клиенте нет объектов Запрос, ДокументОбъект, ТаблицаЗначений. Поэтому любое обращение к данным — это вызов сервера.
Что происходит за один вызов:
- 📦 Клиент сериализует параметры (и данные формы, если вызов контекстный).
- 🌐 Пакет уходит на сервер: по TCP в клиент-серверном варианте, по HTTP через веб-сервер в облаке.
- ⚙️ Рабочий процесс выполняет серверную процедуру.
- 📦 Результат и изменённые данные формы сериализуются обратно.
- ⏳ Всё это время клиент ждёт: интерфейс не отвечает.
В локальной сети один вызов обычно занимает единицы миллисекунд. Через интернет — десятки и сотни. Двадцать вызовов по 100 мс — это уже две секунды на одно нажатие кнопки.
В файловой базе сервер работает внутри процесса клиента, и вызовы почти бесплатны. Поэтому код, который «летал» у разработчика на файловой копии, может заметно тормозить после переноса базы на сервер или в облако.
Как устроен серверный вызов на уровне платформы
Где выполняется код, определяют директивы компиляции в модуле формы и свойства общих модулей.
Директивы в модуле формы
| Директива | Где выполняется | Что передаётся |
|---|---|---|
&НаКлиенте | на клиенте | ничего, вызова нет |
&НаСервере | на сервере | параметры + данные формы туда и обратно |
&НаСервереБезКонтекста | на сервере | только параметры и возвращаемое значение |
&НаКлиентеНаСервереБезКонтекста | там, откуда вызвали | вызова сервера нет |
Ключевая разница между &НаСервере и &НаСервереБезКонтекста. Контекстная процедура видит реквизиты формы (Объект, Элементы, свои реквизиты), но платформа гоняет их по сети в обе стороны. Форма документа с табличной частью на 500 строк — это сотни килобайт на каждый вызов.
Внеконтекстная процедура формы не видит. Ей доступно только то, что передали параметрами. Зато пакет маленький.
Общие модули с флагом «Вызов сервера»
Из клиентского кода можно вызвать экспортную процедуру общего модуля, только если у модуля в свойствах стоят флаги Сервер и Вызов сервера. Без второго флага клиент такую процедуру просто не увидит.
Остальные флаги общего модуля, которые влияют на вызовы:
- 🔒 Привилегированный — код выполняется без проверки прав. Удобно, но опасно: проверяйте входные параметры.
- 🔁 Повторное использование возвращаемых значений —
На время вызоваилиНа время сеанса. Платформа кеширует результат экспортных функций по набору параметров. - 💻 Клиент (управляемое приложение) — модуль доступен на клиенте; вызова сервера при обращении к нему нет.
Экспортные методы модулей объектов и модулей менеджеров с клиента напрямую не вызываются. Нужна прослойка: процедура формы &НаСервере или общий модуль с флагом «Вызов сервера».
Какие значения проходят через границу
Передать можно только сериализуемые значения: примитивные типы, ссылки, Массив, Структура, Соответствие, их фиксированные варианты, ХранилищеЗначения. Объекты ТаблицаЗначений, ДокументОбъект, Запрос на клиент не передаются. Таблицу перед возвратом превращают в массив структур или кладут во временное хранилище (ПоместитьВоВременноеХранилище), а на клиент отдают адрес.
Синхронные, асинхронные и фоновые вызовы
Вызов сервера из клиентского кода всегда синхронный: клиент ждёт, пока сервер вернёт ответ. Слово «асинхронный» в 1С относится к другим вещам.
Асинхронный клиентский код
ОписаниеОповещения и ключевые слова Асинх / Ждать (появились в платформе 8.3.18) нужны для диалогов: вопросов, выбора файла, ввода значения. Код не блокирует интерфейс в ожидании ответа пользователя. На скорость серверных вызовов это не влияет: серверная функция внутри асинхронной процедуры выполняется так же синхронно.
Фоновые задания
Если операция идёт дольше 2–3 секунд (пересчёт, загрузка, формирование большого отчёта), её выносят в фоновое задание. Клиент делает короткий вызов «запусти», получает идентификатор задания и периодически спрашивает статус через обработчик ожидания. Интерфейс при этом живой.
В типовых конфигурациях на БСП для этого есть общий модуль ДлительныеОперации и клиентский ДлительныеОперацииКлиент: они берут на себя запуск, ожидание и показ индикатора. В самописных решениях работают напрямую с ФоновыеЗадания.
| Вариант | Когда подходит | Чем платите |
|---|---|---|
| Синхронный вызов | быстрые операции до 1–2 секунд | интерфейс ждёт |
| Фоновое задание | долгие расчёты, загрузки, отчёты | сложнее код, результат через хранилище |
| Регламентное задание | работа по расписанию без пользователя | в файловой базе выполняется, только пока запущен клиент |
Ошибки при вызове сервера и как их распознать
Большая часть проблем — не падения, а медленная работа. Но и сообщения об ошибках тоже встречаются.
&НаСервере или &НаСервереБезКонтекста.ТаблицаЗначений, ДокументОбъект, результат запроса. Преобразуйте его в массив структур или положите во временное хранилище.&НаСервереБезКонтекста нет контекста формы. Передайте нужные значения параметрами или смените директиву на &НаСервере.ДокументОбъект, который не вернули в форму. На сервере используйте ЗначениеВРеквизитФормы после изменения объекта.Отдельно про толстый клиент обычного приложения. Там доступ через точку к реквизиту ссылки (Документ.Контрагент.ИНН) на клиенте работает, но каждый такой доступ — неявный вызов сервера. В тонком клиенте этот приём не работает
, и это скорее плюс: незаметных вызовов становится меньше.
Оптимизация серверных вызовов: что даёт эффект
Порядок — от самого выгодного к частным приёмам.
- Уберите вызовы из циклов. Соберите все данные на клиенте в массив, отправьте одним вызовом, верните
Соответствие. Это самый частый источник тормозов. - Объединяйте последовательные вызовы. Если обработчик сначала проверяет данные на сервере, потом считает, потом записывает — сделайте одну серверную процедуру, которая вернёт структуру с результатами.
- Выбирайте
&НаСервереБезКонтекста, где форма не нужна. Получить курс валюты, проверить ИНН, найти элемент по коду — данные формы для этого не нужны. - Кешируйте справочные значения. Константы, настройки пользователя, признаки функциональных опций редко меняются. Модуль с повторным использованием
На время сеансавызовет сервер один раз за сеанс на каждый набор параметров. - Передавайте на клиент только нужное. Не возвращайте объект целиком, если клиенту нужно два поля.
- Делайте больше в
ПриСозданииНаСервере. Этот обработчик уже выполняется на сервере при открытии формы. Всё, что форма знает заранее (видимость полей, значения по умолчанию, флаги), рассчитывайте там, а не отдельным вызовом изПриОткрытии. - Долгое — в фон. Всё, что дольше пары секунд, выносите в фоновое задание.
Кеш с повторным использованием возвращает одни и те же данные до конца сеанса. Не кешируйте то, что меняется во время работы: остатки, цены, статусы документов. Возвращайте из таких функций фиксированные коллекции, чтобы вызывающий код случайно не испортил закешированное значение.
Примеры кода: как писать вызовы правильно
Плохо: вызов сервера в цикле
&НаКлиенте
Процедура ЗаполнитьЦены(Команда)
Для Каждого Строка Из Объект.Товары Цикл
Строка.Цена = ЦенаНоменклатуры(Строка.Номенклатура, Объект.ТипЦен);
КонецЦикла;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЦенаНоменклатуры(Номенклатура, ТипЦен)
// один запрос на одну строку
КонецФункции
На 200 строках это 200 вызовов. В облаке с задержкой 80 мс — около 16 секунд ожидания.
Хорошо: один вызов на всю табличную часть
&НаКлиенте
Процедура ЗаполнитьЦены(Команда)
ЗаполнитьЦеныНаСервере();
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьЦеныНаСервере()
СписокНоменклатуры = Новый Массив;
Для Каждого Строка Из Объект.Товары Цикл
СписокНоменклатуры.Добавить(Строка.Номенклатура);
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
| &Дата,
| ТипЦен = &ТипЦен
| И Номенклатура В (&СписокНоменклатуры)) КАК Цены";
Запрос.УстановитьПараметр("Дата", Объект.Дата);
Запрос.УстановитьПараметр("ТипЦен", Объект.ТипЦен);
Запрос.УстановитьПараметр("СписокНоменклатуры", СписокНоменклатуры);
Цены = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Цены.Вставить(Выборка.Номенклатура, Выборка.Цена);
КонецЦикла;
Для Каждого Строка Из Объект.Товары Цикл
Цена = Цены.Получить(Строка.Номенклатура);
Строка.Цена = ?(Цена = Неопределено, 0, Цена);
КонецЦикла;
КонецПроцедуры
Один вызов и один запрос. Имена регистра и измерений возьмите из своей конфигурации: в примере структура регистра цен с измерениями «Тип цен» и «Номенклатура».
Здесь директива &НаСервере оправдана: процедура меняет табличную часть формы, и изменения должны вернуться на клиент.
Хорошо: внеконтекстная проверка
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
Реквизиты = РеквизитыКонтрагента(Объект.Контрагент);
Объект.Договор = Реквизиты.ОсновнойДоговор;
Элементы.ГруппаНерезидент.Видимость = Реквизиты.Нерезидент;
КонецПроцедуры
&НаСервереБезКонтекста
Функция РеквизитыКонтрагента(Контрагент)
Результат = Новый Структура("ОсновнойДоговор, Нерезидент");
// заполнение полей запросом по справочнику контрагентов
Возврат Результат;
КонецФункции
Одна функция возвращает всё, что нужно обработчику, структурой. Данные формы по сети не идут.
Фоновое задание без БСП
&НаСервере
Функция ЗапуститьРасчетНаСервере()
Параметры = Новый Массив;
Параметры.Добавить(Объект.Ссылка);
Задание = ФоновыеЗадания.Выполнить("РасчетыСервер.ВыполнитьРасчет", Параметры, , "Расчёт по документу");
Возврат Задание.УникальныйИдентификатор;
КонецФункции
&НаСервереБезКонтекста
Функция ЗаданиеЗавершено(Идентификатор)
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Идентификатор);
Возврат Задание = Неопределено
Или Задание.Состояние <> СостояниеФоновогоЗадания.Активно;
КонецФункции
На клиенте сохраните идентификатор в реквизит формы и подключите ПодключитьОбработчикОжидания с интервалом 1–2 секунды. Обработчик вызывает ЗаданиеЗавершено, а после завершения отключается и забирает результат. Процедура ВыполнитьРасчет должна быть экспортной и лежать в серверном общем модуле.
Каждая проверка статуса — тоже вызов сервера. Не ставьте интервал меньше секунды: при десятке пользователей это заметная нагрузка.
Отладка и мониторинг серверных вызовов
Сначала посчитайте вызовы, потом оптимизируйте. Интуиция здесь часто обманывает.
Счётчик вызовов в клиенте
Запустите клиент с параметром командной строки /DisplayPerformance. В строке состояния появится число вызовов сервера и объём переданных данных за последнее действие. Тот же режим включается в Конфигураторе в параметрах запуска 1С:Предприятия: найдите флажок показа количества вызовов сервера.
Ориентир для обычной формы: нажатие кнопки или изменение поля — один вызов, открытие формы — один-два. Если видите десять и больше на одно действие, есть что править.
Замер производительности
В Конфигураторе запустите отладку и включите замер производительности (Отладка → Замер производительности). Результат покажет, какие строки кода выполнялись дольше всего и сколько раз. Серверные строки в замере видны, если отладка сервера разрешена.
Технологический журнал
На рабочем сервере технологический журнал настраивают файлом logcfg.xml. Для вызовов полезны события:
- 📥
CALL— входящий вызов на сервере: длительность, объём, контекст; - 📤
SCALL— исходящий вызов; - 🗄️
DBMSSQL/DBPOSTGRS— запросы к СУБД внутри вызова.
Фильтруйте по длительности (Duration), иначе журнал быстро займёт диск. Держите его включённым только на время диагностики.
Подробный технологический журнал на продуктивном сервере заметно нагружает диск. Включайте его в часы низкой нагрузки и ограничивайте отбором по событиям и длительности.
Вызов сервера в облаке 1С:Fresh и при разработке в EDT
1С:Fresh и другие облака
В сервисе клиент общается с сервером через веб-сервер по HTTP. Задержка между пользователем и сервером складывается из интернета и публикации. Поэтому в облаке разница между хорошим и плохим кодом видна сильнее всего:
| Сценарий | Локальная сеть | Облако, задержка ~80 мс |
|---|---|---|
| 1 вызов на действие | незаметно | ~0,1 с |
| 10 вызовов на действие | ~0,05 с | ~0,8 с |
| 200 вызовов в цикле | ~1 с | ~16 с |
Цифры ориентировочные: реальное время зависит от канала и объёма данных. Логика простая: в облаке время ответа почти целиком определяется числом вызовов, а не мощностью сервера.
Для доработок в облаке есть ещё ограничение: расширения и внешние обработки в сервисе проходят проверку перед подключением. Тяжёлые вызовы в цикле лучше убрать до неё, а не после жалоб пользователей.
1С:EDT
EDT — среда разработки, а не отдельная платформа. Механизм вызовов в ней тот же: директивы, флаги общих модулей, сериализация. Разница в удобстве: проверки кода EDT подсвечивают часть проблем ещё при написании, например вызов недоступного на клиенте метода. Отладка и замер производительности в EDT тоже есть, принцип работы прежний.
Частые вопросы
Чем вызов сервера отличается от обращения к базе данных?
Считается ли открытие формы вызовом сервера?
ПриСозданииНаСервере и передаёт форму на клиент. Поэтому подготовку данных удобно делать именно там.