Разработчики, переходящие с обычных форм на управляемые, часто замечают странную особенность: «пустой» вызов серверной процедуры в управляемом приложении (УП) занимает значительно больше времени, чем в обычном приложении (ОП). Если в обычном приложении на локальном сервере этот процесс занимает около 1–2 мс, то в управляемом приложении время ожидания возрастает до 10–15 мс. На первый взгляд кажется, что это системная ошибка или неоптимальность, но на самом деле за этим стоят фундаментальные архитектурные различия платформ. Разберем подробно, почему так происходит и что именно делает платформа в эти миллисекунды.
Проанализируем результаты тестов, проведенных на локальных машинах, где влияние сетевых задержек минимизировано (localhost). При выполнении 1000 циклов вызова пустого метода из общего модуля мы увидим следующую картину:
Важно отметить, что разница между Тонким клиентом и Толстым клиентом в режиме управляемого приложения практически отсутствует. Это подтверждает, что задержка кроется не в типе клиентского исполняемого файла, а в самой логике работы управляемого интерфейса и протокола обмена данными.
Чтобы понять, куда уходит время, проанализируем лог Техножурнала (ТЖ) для одного «пустого» вызова в управляемом приложении. Рассмотрим ключевые события, которые фиксирует платформа:
45:46.074000-0,VRSREQUEST,4,process=1cv8,Method=POST,URI='/e1cib/modules/call?id=urn%3Amodule%3Amd%3A...'
45:46.074002-1,SCALL,4,process=1cv8,IName=IVResourceRemoteConnection,MName=send...
45:46.074005-1,SCALL,4,process=1cv8,IName=ISeanceContextStorage,MName=getSeanceParameterSer...
45:46.077015-15,CALL,2,process=rphost,Context=ОбщийМодуль.Сервер.Модуль.Тест...
Разберем по шагам, что означают эти записи и почему они важны для понимания производительности:
VRS. Платформа формирует виртуальный HTTP POST запрос. Даже если вы работаете по протоколу TCP, внутри него «заворачивается» полноценная структура с заголовками (Headers), URI ресурса и телом запроса. Это необходимо для унификации работы тонкого и веб-клиентов.getSeanceParameterSer и seanceParametersCommit показывают, что при каждом вызове сервера платформа синхронизирует Параметры сеанса. Платформа должна убедиться, что состояние сеанса на клиенте и сервере идентично. В обычном приложении этот механизм работает более «лениво» или использует общую память, что экономит время.zstd или lz4) и подготавливает среду исполнения на стороне rphost.Выясним причину, почему директива &НаСервере работает значительно медленнее, чем &НаСервереБезКонтекста. В управляемом приложении реализована Stateless-архитектура (без сохранения состояния на сервере между вызовами в привычном понимании).
Когда мы используем &НаСервере:
В «тяжелых» формах время такого «путешествия» может составлять не 15 мс, а 200–500 мс, даже если процедура не содержит ни одной строки кода. Использование &НаСервереБезКонтекста исключает передачу данных формы, оставляя только системный оверхед протокола (те самые 5–10 мс).
Проанализируем ситуацию в обычном приложении. Толстый клиент в обычном режиме работает как Stateful-приложение. Он использует более низкоуровневые механизмы RPC (Remote Procedure Call) или прямой маршалинг объектов, если клиент и сервер находятся на одной машине.
В обычном приложении платформа «доверяет» состоянию сеанса и не выполняет такую жесткую проверку и синхронизацию параметров при каждом чихе, как это делает управляемый интерфейс, спроектированный для работы в нестабильных интернет-сетях. В ОП вызовы идут практически «напрямую» в память процесса, что и дает заветные 1–2 мс.
Выяснив, что задержка в 15 мс — это фундаментальное ограничение платформы, связанное с безопасностью, универсальностью и синхронизацией сеанса, мы должны изменить подходы к разработке:
&НаСервереБезКонтекста. Это сократит время вызова в 2–3 раза по сравнению с контекстным вызовом.ПовторноеИспользованиеЗначений с кэшированием на время сеанса или использование временных хранилищ.Рассмотрим пример оптимизации кода. Вместо вызова в цикле:
// Плохая практика для УП
Для Каждого Строка Из ТаблицаТоваров Цикл
Результат = СерверныйМодуль.ПроверитьОстаток(Строка.Номенклатура); // 100 итераций * 15 мс = 1.5 сек!
КонецЦикла;
Разберем правильный подход:
// Хорошая практика для УП
СписокНоменклатуры = ТаблицаТоваров.ВыгрузитьКолонку("Номенклатура");
СоответствиеОстатков = СерверныйМодуль.ПроверитьОстаткиСписком(СписокНоменклатуры); // 1 вызов * 15 мс = 0.015 сек!
Резюме: Разница в скорости серверных вызовов между ОП и УП — это не баг, а плата за возможность работы через Web, за безопасность данных и гарантированную целостность сеанса. Понимание механики работы VRSREQUEST и ISeanceContextStorage позволяет разработчику писать более производительный код, избегая лишних транзакций между клиентом и сервером.