http-сервисы - один из самых прогрессивных способов интеграции сторонних систем в 1С, простой и основанный на широко применяемом протоколе. Однако и он не лишен недостатков:
- Нам не известно, что у него под капотом, мало информации о сравнениях производительности и быстродействия с аналогами, мало изучено поведение сервисов на длительных больших нагрузках
- Интересное лицензирование a.k.
- Выбор лишь между IIS и Apache для веб-сервера. В самих IIS/Apache ничего плохого нет (разве что тяжеловаты), особенно если учесть, что под них сделаны не только сервисы, но и веб-клиент. Однако, подобная "прибитость гвоздями" ограничивает возможности разработчика в поиске оптимальных решений для своих задач
Но, как оказалось, есть еще одна альтернатива, которая позволяет немного (и даже с пользой) разнообразить стэк технологий для интеграции с внешним миром. А именно - прикрутить к 1С node.js.
Далее я буду идти по порядку: начиная от стандартного 1С->http-сервис->IIS и закончу на максимальном отходе от канонов, чтобы показать все в сравнении. Будет много букв и много моих любимых графиков с k6 (сервис для стресс-тестирования веб-приложений)
Классика
Начнем с простого: 1С IIS. Не так давно я публиковал статью о своем проекте, где 1С выступает в роли бэка для веб-приложения. Вот она:
Там я рассказывал, в том числе, и о его быстродействии - собственно быстродействии http-сервисов на IIS. Однако, спустя некоторое время, я пришел к выводу, что приведенные мной данные не соответствуют действительности, если не сказать больше. Дело в том, что для теста я использовал один, постоянно повторяющийся, POST запрос с одним и тем же JSON внутри.
Так выглядит JSON, отправляемый на сервис. Он может показаться небольшим, но по нему происходит:
1. Поиск пользователя по chat_id (запрос к справочнику)
2. Поиск книги по text: 7547G (запрос к справочнику)
3. Поиск, читал ли данный пользователь уже данную книгу (запрос к МЗ)
4. Отправка нужной страницы (поиск по табличной части элемента из п.2, http-запрос)
5. Запись прогресса - первая страница прочитана (МЗ.Прочитать, МЗ.Записать)
6. Запись общего дневного прогресса пользователя для дашборда в профиле (МЗ.Прочитать, МЗ.Записать)
Идентичность данных в запросах позволила 1С переиспользовать сеансы, а, как следствие, очень сильно порезала нагрузку на сервер, так как соединений плодилось гораздо меньше и процесс кластера не трясло. В реальной жизни такой ситуации, разумеется, быть не может - никто не будет посылать одни и те же данные. Поэтому, для новых тестов я сделал 25 разных запросов. И тут все завертелось...
Вот как это было тогда. Да, не очень быстро, но тут я даже мог на 4 области объяснить, что происходит.
Этот же тест теперь:
Remember this guy? Feel old yet?
Ну начнем с простого. Пиковое и общее количество запросов увеличилось, т.е. за то же время было обработано больше. И это даже было бы хорошо, если бы все шло, как и шло в начале. Проблема, как уже очевидно, во второй части теста, где систему начало трясти под нагрузкой. Классическая ситуация из разряда
1С, потеряв возможность переиспользовать сеансы, моментально наплодил их на две страницы панели администрирования, что позволило ему забирать аж за 60 запросов в секунду. Потом он еще пожил две с половиной минуты и умер как герой. Тут же сразу стоит прояснить несколько моментов, дабы все было понятно:
- Тестирование происходит на очень слабом железе - показатели низкие. Это буквально mini pc, который я использую как домашний сервер (подробнее в статье выше). Здесь это не играет роли: главное, что железо во всех тестах одинаковое, а смысл как раз в сравнении разных способов интеграции в одинаковых условиях.
- Данные отправляются те же, что и в тесте из предыдущей статьи (скрин JSON и описание выше), но некоторые данные от запроса к запросу были изменены. Непосредственно обработка логики внутри 1С от этого не изменилась.
- Максимальное количество одновременных http-соединений в пуле можно ограничить. Однако во-первых, тогда будут большие потери по timeout при нагрузке, а во-вторых это скучно и не интересно. Тем более, если оставить их достаточно много, в сущности, ничего и не поменяется.
После первого теста мне стало интересно: насколько все может стать плохо, если понапрягать сервер подольше. Ответ:
Очень плохо
Этот тест примерно такой же, но в 3 раза дольше - 9 минут. Однако даже невооруженным глазом видно, что и 1/6 этого времени было достаточно, чтобы у сервера порвало парус без надежды на дальнейшее плаванье. Вот как, в то же время, это выглядело внутри.
Максимум отъели сам рабочий процесс и процесс кластера. Но IIS тоже ухватил себе немало, это надо запомнить. В целом по итогам теста: количество обрабатываемых запросов в секунду страшно скакало между 5 и 60, 16 запросов не пришли вообще, среднее время отклика 7 секунд, что даже многие терпеливые отправители могут принять за timeout. Прискорбно. Но пришло время пробовать альтернативные решения.
Node.JS
Node.JS - популярная современная платформа для бэка, работающая на движке V8 - том самом, что обрабатывал js 5 минут назад для загрузки этой статьи в вашем браузере на хромиуме. Но мы здесь не ради него самого: я покажу немного js кода дальше, но смысл скорее именно в связи с 1С, а не самой работе с Node. Использовать же для этого всего мы будем старое доброе COM-соединение.
Если кратко и по пунктам:
- Есть известный многим, но не всем, Automation Server, он же внешнее соединение, он же V83.ComConnector. С его помощью мы в Node.JS поднимем соединение с базой.
- ...
- PROFIT
А, ну так, вообще-то, и все. Есть сервер Node.JS, при старте которого мы поднимаем COM. Сервер слушает - COM висит. Как только на порт приходит http-запрос, Node.JS вызывает по соединению функцию с параметром (желательно фоновую). Остается лишь найти способ подружить прогрессивную современную веб платформу работающую на языке для кнопок и формочек с древним хардкорным API для древних нативных виндовых программ.
Winax
Единственной, похожей на правду библиотекой, обещающей работу с COM в Node.JS оказалась winax (название в npm) или node-activex (название на GitHub)
https://github.com/durs/node-activex
https://www.npmjs.com/package/winax
Если у вас еще нет самого node.js, то, разумеется, необходимо установить его c офф. сайта
https://nodejs.org/en/download
Вместе с ним установится и npm. Для проекта необходимо создать отдельную папку, а в ней файл app.js, где будет код нашего сервера.
Далее, необходимо открыть командную строку в папке проекта и с ее помощью установить/подключить winax. Делается это командой
npm install winax
На этом этапе могут возникнуть ошибки. Скорее всего они будут связаны с отсутствием в системе двух необходимых вещей: python и Visual C++ Build tools (или просто Build tools, в зависимости от того, какого года версию вы выберите). Их необходимо установить, но в тексте ошибки будет написано как. Лично у меня проблема возникла только с Build tools, потому что на Windows Server 2012 не работает новый Visual Studio, пришлось брать 17 года.
После установки winax необходимо сформировать простой скрипт сервера, например такой
const http = require("http"); // Подкючаем стандартную библиотеку http
const winax = require('winax'); // Подкючаем winax
const url = require('url'); // Подкючаем стандартную библиотеку url
//Создаем новый объект V83.ComConnector
const con = new ActiveXObject('V83.ComConnector', { activate: true });
//Выполняем Connect - получаем объект соединения. Далее у него через точку я обращаюсь
//к Обработке(eng. DataProcessors) под названием NodeProcessor
const processor = con.Connect('srvr ="AIONIOTISCORE";Ref ="AioPg"').DataProcessors.NodeProcessor;
//Стандартная оболочка сервера
http.createServer(function(request,response){
//localhost:3000/node
if(request.url === "/node"){
//Сборка тела post запроса по частям
let data = "";
request.on("data", chunk => {
data += chunk;
});
request.on("end", () => {
console.log(data);
response.end();
//Вызов функции из модуля менеджера обработки NodeProcessor
processor["MyCrazyFunction"](data);
});
}
}).listen(3000, "localhost",function(){
console.log("Start 3000");
});
Данный скрипт вызывает функцию MyCrazyFunction из модуля менеджера обработки NodeProcessor с одним параметром, куда я передаю тело запроса - например JSON. В данном варианте я ничего в ответ не возвращаю. Если нужно вернуть ответ, то
//Вместо
response.end();
//Вызов функции из модуля менеджера обработки NodeProcessor
processor["MyCrazyFunction"](data);
//Будет
let answer = processor["MyCrazyFunction"](data);
response.end(answer);
Вообще с возвращением значений все сложнее. Методы, которые ничего возвращать не должны (у меня это методы телеграм бота, но вообще может быть любой односторонний обмен, например) можно обернуть в фоновое задание - COM-соединение у нас все-таки одно. А вот если нужно что-то вернуть, то одно COM соединение может с этим не справляться. Я лично вижу выход только в создании нескольких COM соединений, сопоставимых примерно по количеству с ожидаемой нагрузкой, а в Node.JS уже использовать их каруселью: запихнуть в массив и вызывать через счетчик по индексу.
Сервер запускается командой
node app.js
Тестируем
Теперь надо посмотреть, есть ли во всем этом смысл. Мой код сервера не такой как в примере - у меня он вызывает функцию, которая была написана для 1Сного http-сервиса. Вызывает он ее создавая фоновое задание (как http-сервис создает сеансы) и в самом коде сервера больше разной маршрутизации. Короче говоря - логика максимально приближенная к тестируемой в прошлый раз на 1C + IIS.
Короткий тест (3 мин)
Во-первых: было выполнено больше запросов, а среднее время отклика - немного меньше
Единственный показатель, превалирующий у 1C+IIS - пиковое количество запросов в секунду. Как видите, обработать больше запросов за то же время это не помогло. Сам же график более плавный: ноду не бросает, время отклика просто растет со временем под нагрузкой.
Так это выглядит внутри:
Ноде вообще все равно - 75% ресурсов за процессами 1С. Единственная проблема - количество фоновых. Их выходит все равно больше, чем было бы http-сеансов, из-за чего увеличивается нагрузка от процесса кластера.
Длинный тест (9 минут)
Уже по графику короткого теста было видно, что Node.js ведет себя стабильнее и на длинной дистанции это скажется еще сильнее. Однако тут все, конечно, тоже не идеально (помним про слабое железо, и тем не менее)
Обработанных запросов больше, среднее время ответа меньше аж в 2 раза и нет ни одного необработанного запроса. Epic Win. Внутри все было примерно похоже на коротки тест: диспетчер загрузку ЦП > 100% все равно не покажет.
Казалось бы, можно уже и подводить итоги, рассказывать про плюсы и минусы. Но у меня припасен еще один козырь в рукаве. Внимательный читатель уже даже мог заметить этот непонятно чем занимающийся процесс IIS в скрине короткого теста для Node.JS. А кто-то даже мог справедливо указать на тот факт, что IIS, так то, публикует все свое добро в интернет, пока нода страдает фигней на своем localhost:3000. И это правда.
Дело в том, что пока это все было в рамках максимально сомнительного эксперимента, мне не очень хотелось ломать свой IIS ради другого поставщика reverse-proxy способного выкинуть мой localhost:3000 на внешний IP. Поэтому я, не мудрствуя лукаво, сделал это через тот же самый IIS. Но с учетом того, что даже так Node в моем случае оказался быстрее, грех было не попробовать его в полную силу
Nginx
Nginx - "простой, быстрый и надёжный сервер, не перегруженный функциями". Инфографика с хабра, которая говорит лучше любых слов
Знающие люди мне, конечно, сейчас расскажут, что nginx тоже не панацея (это правда), но забегу немного вперед - с ним действительно (и ожидаемо) стало быстрее.
Поставить и настроить nginx, хотя бы на том ламерском уровне, который мне сегодня необходим, достаточно просто.
Нужно скачать zip с офф. сайта и распаковать
https://nginx.org/ru/download.html
Зайти в папку conf и поправить nginx.conf под свои нужды. В моем случае это был только пункт server внутри пункта http
server {
listen 443 ssl;
server_name api.athenaeum.digital;
ssl_certificate ../ssl/domain.tld.chained.crt;
ssl_certificate_key ../ssl/domain.tld.key;
ssl_protocols TLSv1.2;
ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
ssl_prefer_server_ciphers on;
location /node
{ proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade; }
}
Очень простой прокси с api.athenaeum.digital на localhost:3000. Запускаем и тестируем. Я пропустил в данном случае 3-х минутное тестирование и сразу запустил на 9
Выглядит неплохо.
Из всех трех тестов 1С + COM + Node.JS с nginx в качестве реверс прокси показали самый лучший результат: самое большое количество обработанных запросов за 9 минут, самое низкое среднее время отклика.
Итого
Конечно, эти тесты не абсолютны: например, Apache, IIS и nginx по разному использую мощные многоядерные процессоры. Но тут уже видно, что у этого есть потенциал. В качестве вывода предлагаю разобрать, где Node.js в связке с 1С можно сказать "да", а где - "нет":
Нет:
- Он не может полноценно заменить Apache и IIS для 1С: веб клиент никто не отменял + дополнительно нужно разбираться в js
- Не совсем понятен оптимальный способ возвращения значений в ответ на запрос: в то время, как 1С поднимает столько сеансов сколько нужно, однопоточный node.js требует правильной работы с неблокирующими вызовами и Event Loop, а для поднятия дополнительных COM соединений с 1С вообще нужно придумывать отдельный механизм (который при любом раскладе становится ничем не лучше сервисов в плане лицензий)
- Сложность в отладке внешних соединений, через которые все работает.
- Необходимость отдельно следить за состоянием сервера, перезапускать после обновления базы для поднятия нового соединения.
Да:
+ Хорошее быстродействие, производительность и стабильность
+ Простота в настройке и легковесность: node.js и nginx довольно легкие по ресурсам, а их настройка на базовом уровне достаточно проста.
+ При необходимости записи сразу в несколько мест (помимо 1С), сделать это проще и производительнее на стороне node.js, чем принимать http-сервисами в 1С, а затем средствами 1С передавать далее. Туда же можно и скинуть начальную обработку данных.
+ На node.js можно использовать WebSocket (наверняка и еще много других приколов, нереализуемых средствами 1С)
Думаю, идеальное место применения - односторонний обмен, когда большой массив данных необходимо передать в 1С по http. Хотя это тоже поверхностный взгляд. Кто знает, может и пугающий меня возврат ответа для знающего человека окажется пустяковой задачей? Пишите в комментариях свои мысли. Спасибо за внимание!
P.S Реализация с примером реального использования уже есть/скоро будет в репозитории Two-Digit Athenaeum (статья на Инфостарте). А еще у меня, кстати, есть и другие статьи ;)
Мой GitHub: https://gitub.com/Bayselonarrend Лицензия MIT: https://mit-license.org