Почему в толстом клиенте 1С можно выполнять запросы на клиенте и как это работает изнутри?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на управляемых формах в системе «1С:Предприятие 8» программисты часто сталкиваются с директивами компиляции, разделяющими код на клиентский и серверный. Особый интерес и множество споров вызывает поведение толстого клиента в клиент-серверном режиме работы базы данных. Многих удивляет тот факт, что в толстом клиенте под директивой &НаКлиенте система позволяет без ошибок создать объект Запрос, выполнить его и получить данные напрямую из базы данных.

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

1. Трехзвенная архитектура «1С» и миф о выполнении на клиенте

Среди разработчиков 1С (особенно тех, кто начинал с обычных форм) распространено заблуждение, что в толстом клиенте (как и в обычном приложении) все вычисления и запросы «выполняются на клиенте». На самом деле, при работе в клиент-серверном варианте платформа всегда использует классическую трехзвенную архитектуру:

  1. Клиентское приложение: Процесс 1cv8.exe (толстый клиент) или 1cv8c.exe (тонкий клиент), запущенный на рабочем месте пользователя.
  2. Сервер приложений 1С:Предприятия: Кластер серверов, состоящий из рабочих процессов rphost.exe.
  3. Система управления базами данных (СУБД): MS SQL Server, PostgreSQL, IBM DB2 или Oracle Database.

Даже если мы пишем код внутри процедуры с директивой &НаКлиенте в толстом клиенте, прямого физического доступа к СУБД у клиентского компьютера нет. У толстого клиента отсутствуют встроенные драйверы СУБД для прямого подключения к таблицам базы данных (если только разработчик вручную не инициализирует внешнее COM-соединение типа ADODB.Connection, что является совершенно другой технологией).

Все запросы к базе данных в обязательном порядке проходят через сервер приложений 1С. Когда в толстом клиенте на стороне клиента вызывается метод Запрос.Выполнить(), платформа осуществляет так называемый неявный вызов сервера (implicit server call).

2. Пошаговый механизм выполнения запроса на клиенте

Проанализируем детально по шагам, что происходит под капотом платформы, когда мы выполняем следующий код под директивой &НаКлиенте в толстом клиенте:


&НаКлиенте
Процедура ВыполнитьЗапросНаКлиенте(Команда)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Номенклатура.Ссылка КАК Ссылка,
        |	Номенклатура.Наименование КАК Наименование
        |ИЗ
        |	Справочник.Номенклатура КАК Номенклатура";
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        Сообщить(Выборка.Наименование);
    КонецЦикла;
    
КонецПроцедуры

При вызове этого кода на клиентской машине происходят следующие скрытые процессы:

  1. Инициализация и сериализация: Клиентское приложение подготавливает структуру запроса (текст запроса на встроенном языке 1С и его параметры). Эти данные упаковываются (сериализуются) в специальный транспортный формат передачи данных 1С.
  2. Передача на сервер приложений: По сети TCP/IP сериализованный пакет отправляется рабочему процессу кластера серверов 1С (rphost.exe).
  3. Трансляция в SQL: Получив пакет, сервер приложений 1С десериализует его. Специальный компонент сервера — транслятор запросов — переводит запрос со встроенного языка 1С на диалект SQL, соответствующий используемой СУБД (например, T-SQL для MS SQL Server или PL/pgSQL для PostgreSQL).
  4. Выполнение на СУБД: Сервер приложений 1С отправляет сгенерированный SQL-запрос в СУБД. СУБД выполняет запрос на своих мощностях, извлекает строки из таблиц базы данных на диске или из кэша оперативной памяти и возвращает сырой результат обратно серверу приложений 1С.
  5. Формирование результата и обратная сериализация: Сервер 1С принимает данные от СУБД, преобразует их во внутренний тип платформы РезультатЗапроса, полностью упаковывает всю выборку в сетевой пакет и отправляет обратно на клиентский компьютер.
  6. Десериализация на клиенте: Процесс толстого клиента 1cv8.exe принимает пакет, воссоздает в оперативной памяти объект РезультатЗапроса, после чего управление возвращается в код разработчика, и начинается обход выборки.

Таким образом, физически запрос к базе данных всегда выполняет СУБД по команде сервера 1С. Если открыть утилиту мониторинга СУБД (например, SQL Server Management Studio или консоль pgAdmin) и посмотреть активные транзакции, то в качестве источника запроса мы всегда увидим сетевое имя и IP-адрес сервера приложений 1С (где крутится rphost.exe), а не имя компьютера конечного пользователя.

3. Почему в тонком клиенте это запрещено, а в толстом разрешено?

Попытка выполнить аналогичный код в тонком клиенте (1cv8c.exe) приведет к ошибке компиляции: тип Запрос на клиенте будет попросту недоступен. Разработчики платформы пошли на это ограничение намеренно по двум ключевым причинам:

Облегчение дистрибутива и потребления памяти. Тонкий клиент проектировался как легковесное приложение, способное быстро запускаться, работать на мобильных устройствах, слабых офисных ПК или даже внутри веб-браузера. Чтобы сделать его «тонким», из его исполняемого кода были исключены многие тяжеловесные библиотеки платформы: трансляторы запросов, объектные модели метаданных, менеджеры тяжелых типов данных (таких как СправочникОбъект или ДокументОбъект). Толстый клиент (1cv8.exe), напротив, содержит абсолютно все библиотеки платформы в своем локальном исполняемом коде, поэтому технически он «умеет» работать с объектом Запрос на своей стороне.

Архитектурный контроль. Позволив разработчикам использовать запросы на клиенте в толстом клиенте, фирма «1С» столкнулась с тем, что программисты начали массово писать неоптимальный код, перегружающий сеть. Полный запрет использования запросов и объектной техники на клиенте в тонком клиенте — это жесткий архитектурный барьер, заставляющий разработчиков явно разделять логику приложения на клиентскую (интерфейсную) и серверную (бизнес-логику и работу с БД).

4. Последствия выполнения запросов на клиенте: почему это критическая ошибка?

Хотя толстый клиент управляемого приложения технически позволяет писать запросы под директивой &НаКлиенте, делать это в реальных конфигурациях категорически запрещено стандартами разработки «1С». Рассмотрим основные проблемы, к которым ведет такая практика:

5. Как правильно переписать запрос на серверный контекст

Давайте проанализируем, как правильно оптимизировать код и перенести работу с базой данных на серверную сторону. Рассмотрим правильный шаблон реализации:


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

&НаСервереБезКонтекста
Функция ПолучитьДанныеНоменклатурыНаСервере()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 100
        |	Номенклатура.Ссылка КАК Ссылка,
        |	Номенклатура.Наименование КАК Наименование
        |ИЗ
        |	Справочник.Номенклатура КАК Номенклатура";
    
    // Выгружаем результат в массив структур для минимизации трафика передачи
    РезультатТаблица = Запрос.Выполнить().Выгрузить();
    
    МассивРезультатов = Новый Массив;
    Для Каждого Строка Из РезультатТаблица Цикл
        СтруктураСтроки = Новый Структура("Ссылка, Наименование", Строка.Ссылка, Строка.Наименование);
        МассивРезультатов.Добавить(СтруктураСтроки);
    КонецЦикла;
    
    Возврат МассивРезультатов;
    
КонецФункции

Обратите внимание на использование директивы &НаСервереБезКонтекста. Этот контекст является наиболее легковесным, так как при вызове такой функции с клиента на сервер не передаются данные самой формы (ее реквизиты), что экономит сетевой трафик и ускоряет работу системы.

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

← На главную