...

Проблема длительных запросов в скриптах

Тема в разделе "Low-code book Производительность", создана пользователем ELMA365, 16 май 2025 в 11:41.

  1. ELMA365

    ELMA365 Moderator

    Кейсы:
    1. Блокировка очереди скриптов при больших нагрузках из-за долгого ожидания ответа от внешней системы.
    2. Блокировка очереди скриптов при увеличенном таймауте выполнения.

    TL;DR:

    • Сокращайте время выполнения каждого отдельного скрипта.
    • Используйте обратный вызов вместо длительного ожидания.
    • Создавайте выделенные микросервисы.

    Архитектура выполнения серверных скриптов

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


    Длительное ожидание в скриптах

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

    [​IMG]

    Здесь мы не будем разбирать проблемы скриптов, которые долго выполняются из-за большого количества операций на платформе. Они рассмотрены в предыдущей статье.


    Таймаут и очередь исполнения

    Для серверных скриптов существует таймаут исполнения. Подробнее о параметрах таймаутов и их указании в поставке On‑Premises можно прочитать в статье «Ограничение серверных сценариев по времени». По умолчанию таймауты выполнения и ожидания в очереди равны 1 минуте. Это значит, что на выполнение всего скрипта от момента его запуска выделяется максимум 2 минуты.

    Рассмотрим пример длительного запроса во внешнюю систему:
    Код:
    
    let response await fetch(“https://bank-service/api/check-scoring”, {
        
    method‘POST’,
        
    bodyJSON.stringify(client_data)
    });
    Если сервер bank-service не ответил за 1 минуту, то выполнение скрипта прерывается из-за превышения таймаута.


    Почему увеличение таймаута не поможет

    Может показаться, что проще всего увеличить таймаут на выполнение скрипта, чтобы учитывать длительное время ответа от сервера. Но это также вызывает проблемы.

    Допустим, вы увеличили таймаут до 10 минут, и одновременно запустилось много скриптов с внешними запросами (больше, чем может выполнять ваш пул). Весь пул исполнения блокируется, пока не придут ответы от сервера или не истечёт таймаут в 10 минут. В это время не смогут выполняться другие серверные скрипты, в том числе серверные скрипты виджетов. Это может крайне негативно сказаться на пользовательском опыте при работе с настроенными формами и страницами.


    Как решить проблемы с длительными запросами

    Рассмотрим возможные решения проблем, связанных с долгим выполнением скриптов.


    Запрос без ожидания ответа

    Самое простое решение, которое часто игнорируют — не ожидать ответ от сервера. Бывает так, что на самом деле ответ от сервера не нужен, но код всё равно написан с ожиданием, потому что «всегда так делали». Если вы не получаете из ответа данные для анализа, то можно убрать ожидание await, и логика работы не изменится.

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


    Разделить запрос для уменьшения нагрузки

    Другой вариант решения — разделить запрос на несколько частей. Это позволяет сократить время ожидания для каждого отдельного запроса. Чаще всего этот способ подходит, если вы передаёте для обработки массив данных, который можно разделить на части.

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

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

    [​IMG]


    Метод обратного вызова

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

    [​IMG]

    Допустим, вы настроили интеграцию платформы с учётной системой 1С. Вам нужно запустить проводку документов за месяц, затем подсчитать промежуточные остатки по регистрам. Эта операция в сумме может занять несколько десятков минут.

    Вы можете доработать пользовательские веб-методы в 1С, чтобы после выполнения всех работ по нужному адресу отправлялся запрос с данными. Тогда ожидать ответ после первоначального запроса не требуется — данные автоматически придут из внешней системы, когда будут готовы.

    Для обработки данных можно использовать:
    1. Действие в БП типа Скрипт с моделью выполнения Обратный вызов.
    2. Пользовательский метод API, который обновит нужный элемент приложения.
    3. Стандартный API системы и блок для ожидания смены статуса элемента в процессе.


    Опрос состояния

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

    [​IMG]

    Этот способ можно использовать, если:
    • Сторонний сервис позволяет передавать и фильтровать данные по внешним идентификаторам. Пример скрипта:
    Код:
    
    // Инициализируем обработку
    let document_data = {
        
    external_id Context.data.document.id,
        
    // Другие параметры
        // ...
    };
    fetch(“https://document-service/api/validate-doc”, {
        
    method‘POST’,
        
    bodyJSON.stringify(document_data)
    });


    // В отдельном скрипте в цикле проверяем состояние документа
    let response await fetch(“https://document-service/api/get-doc?external_id=” + Context.data.document.id);
    let doc await response.json();
    Context.data.doc_status doc.status;
    • Сторонний сервис сразу возвращает токен, который можно использовать для его опроса. Пример скрипта:
    Код:
    
    // Инициализируем обработку
    let document_data = {
        
    some_data Context.data.document_data,
        
    // Другие параметры
        // ...
    };
    let tokenRes await fetch(“https://document-service/api/validate-doc”, {
        
    method‘POST’,
        
    bodyJSON.stringify(document_data)
    });
    Context.data.validate_token await tokenRes.text();


    // В отдельном скрипте в цикле проверяем состояние документа
    let response await fetch(“https://document-service/api/validate-status?token=” + Context.data.validate_token);
    Context.data.doc_status await response.text();

    Микросервис

    Если ни один из предыдущих вариантов не подходит, вы можете создать отдельный переносимый сервис. В нём можно настроить вызов нужного метода, логику взаимодействия и более длительный таймаут.

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

    [​IMG]

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