Как реализовать работу 1С с альтернативными веб-серверами через собственные модули расширения или Native API

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

Стандартная архитектура публикации баз 1С:Предприятие предполагает использование либо Apache (с модулем wsap24.dll), либо IIS (с модулем wsisapi.dll). Однако в высоконагруженных системах или при необходимости специфической настройки маршрутизации разработчики часто задумываются о замене этих компонентов на более гибкие решения, такие как Nginx или серверы, написанные на Golang. Рассмотрим подробнее, как устроено это взаимодействие и с какими трудностями можно столкнуться при попытке "расковырять" интерфейсы 1С.

Архитектура стандартных модулей расширения 1С

Для начала проанализируем ситуацию: штатные библиотеки wsap24.dll и wsisapi.dll не являются самостоятельными обработчиками. Они выступают в роли адаптеров между API конкретного веб-сервера и внутренним протоколом кластера 1С. Разберем основные задачи этих модулей:

  1. Трансляция HTTP-запроса: Модуль получает структуру запроса от веб-сервера (например, request_rec в Apache), извлекает заголовки, тело и параметры.
  2. Сериализация: Данные упаковываются во внутренний бинарный формат 1С (часто называемый v8-протоколом).
  3. Передача в rphost: Через вспомогательные библиотеки (например, v8p.dll) запрос отправляется рабочему процессу 1С.
  4. Ожидание и возврат ответа: Модуль блокирует поток веб-сервера до получения ответа от 1С, после чего транслирует его обратно в HTTP-ответ.

Основная сложность прямой эмуляции этих библиотек заключается в их жесткой привязке к заголовочным файлам Apache или интерфейсам ISAPI. Попытка запустить wsap24.dll вне среды Apache потребует создания сложной "песочницы", имитирующей Apache Runtime (APR), что крайне трудозатратно.

Реализация собственного веб-сервера на Golang через Native API

Рассмотрим альтернативный подход: создание собственной внешней компоненты (ВК) на языке Golang, которая будет нести "на борту" полноценный веб-сервер и передавать данные в 1С. Выясним причину, почему это решение кажется привлекательным: Go обладает отличной производительностью в сетевых задачах и позволяет собрать всё решение в одну DLL.

Разберем пример реализации серверной части на стороне Go:


func StartWeb() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if AddInFunc == nil {
            fmt.Println("Компонента не инициализирована!")
        } else {
            // Вызываем Callback функцию, которая отправит событие в 1С
            C.invoke(C.closure(AddInFunc))
        }
        
        // В этой точке возникает главная архитектурная проблема:
        // как дождаться ответа от кода 1С, чтобы записать его в Response?
        w.Write([]byte("Запрос принят 1С, но ответ еще не готов."))
    })
    
    port := "1111"
    if err := http.ListenAndServe(":"+port, nil); err != nil {
        fmt.Println(err)
    }
}

На стороне C++ (прослойка для Native API) передача события в 1С выглядит следующим образом:


VOID CALLBACK Event() {
    if (!pAsyncEvent)
        return;
    wchar_t *who = L"CustomWebServer", *what = L"HttpRequest";
    wchar_t *wsdata = L"Method: GET, Path: /api/data";
    
    // Отправка асинхронного события в 1С
    pAsyncEvent->ExternalEvent(who, what, wsdata);
}

Проблема синхронности и механизм ВнешнегоСобытия

Проанализируем ситуацию, с которой сталкиваются все разработчики при использовании ExternalEvent. Метод ВнешнееСобытие в 1С работает через очередь сообщений Windows. Это означает, что:

Для полноценного HTTP-сервера это критично, так как клиент ожидает HTTP-ответ (200 OK, 404 и т.д.) немедленно. Если мы отправим ExternalEvent, поток Go пойдет дальше и завершит HTTP-сессию раньше, чем 1С успеет обработать данные.

Варианты решения проблемы возврата данных

Чтобы обойти ограничения асинхронности, проанализируем следующие способы:

  1. Цикл ожидания (Polling): В коде Go после отправки события мы можем запустить цикл, который проверяет состояние определенной переменной или канала. В это время 1С, обработав запрос, вызывает метод нашей ВК (например, УстановитьРезультат(Данные)), который заполняет эту переменную и "отпускает" поток ожидания в Go.
  2. Использование HTTP-сервисов 1С через Reverse Proxy: Это наиболее стабильный промышленный вариант. Вместо того чтобы эмулировать wsap24.dll, мы ставим Nginx перед стандартным Apache. Nginx берет на себя SSL, фильтрацию запросов и балансировку, а Apache выступает лишь в роли легковесного адаптера для передачи запроса в 1С.
  3. Использование Java-сервлетов: Платформа 1С поддерживает работу через Java-серверы приложений (Tomcat). Исследование библиотеки com.1c.v8.web.server.jar может дать ключ к пониманию того, как сформировать запрос к кластеру напрямую, минуя бинарные модули Apache.

Рекомендации по реализации на Go

Если вы все же решили пилить нативную компоненту на Go, разберем по шагам, как минимизировать накладные расходы:

Шаг 1: Используйте cgo для связи Go и Native API. Помните, что DLL на Go может быть довольно объемной (от 10-15 МБ), так как включает в себя среду выполнения (runtime).

Шаг 2: Реализуйте в ВК метод, который 1С будет вызывать для возврата результата. Например, ВернутьОтвет(ИдентификаторЗапроса, ТелоОтвета).

Шаг 3: На стороне Go используйте sync.Map для хранения каналов (channels) под каждый входящий запрос по уникальному ID. После отправки события в 1С, Go-рутина блокируется на чтении из канала:


// Псевдокод ожидания ответа в Go
respChan := make(chan string)
requestsMap.Store(requestId, respChan)

C.invoke_event(requestId) // Отправляем в 1С через ВК

// Ждем ответа из 1С (с таймаутом)
select {
case response := <-respChan:
    w.Write([]byte(response))
case <-time.After(30 * time.Second):
    w.WriteHeader(504)
}

Вывод: Прямая эмуляция wsap24.dll практически невозможна без глубокого реверс-инжиниринга закрытых протоколов 1С. Однако использование Nginx как прокси или написание собственной ВК на Golang с механизмом блокирующих каналов — это вполне рабочие стратегии для создания кастомных интерфейсов взаимодействия с платформой.

← На главную