NativeAPI – это (не) страшно. Как и зачем мы используем websocket в EmplDocs

22.11.24

Интеграция - WEB-интеграция

Небольшие компании часто избегают публикации базы для работы с HTTP-сервисами из-за связанных с этим сложностей и рисков. Но что, если отказаться от публикации информационной базы 1С на веб-сервере и перейти к взаимодействию с фронтэндом через WebSocket? Расскажем об особенностях разработки и использования NativeAPI-компонент для 1С и опыте интеграции 1С-бэкэнда с Angular-приложением без публикации информационной базы.

Меня зовут Константин Редькин, я занимаюсь разработкой бэкэнда для EmplDocs. Расскажу, как мы подружили наш 1С-бэкенд с Angular-приложением без публикации информационной базы.

 

 

Начнем с того, кто мы такие – что за EmplDocs.

Мы делаем жизнь сотрудникам легче. Мы – КЭДО с приколами, у нас есть не только согласование всяких приказов или заявлений, а еще много всякого разного вроде расчетников, командировок, графиков отпусков и даже работы со страховками.

Наше приложение EmplDocs состоит из:

  • Backend-части – это расширение для ЗУП, которое реализует HTTP-сервис.

  • Frontend-часть приложения, написанная на Angular – она подключается к HTTP-сервису в базе ЗУП и позволяет пользователю через красивый интерфейс проводить операции с заявками, документами и прочим.

  • И Node.js-приложения EmplDocs Cloud, в настройках которого мы прописываем, к какому эндпоинту должен стучаться фронтенд, чтобы пользователь мог работать через наш сервис. С помощью этого приложения мы рулим лицензиями и тем, чтобы у пользователей всегда была актуальная версия фронта – в общем, берем на себя всю работу по обслуживанию фронтенда.

 

 

Как мы вообще пришли к тому, что нам потребовалось писать свою компоненту для веб-сокетов? Скоро веб-сокеты в платформе будут, а мы свою компоненту делаем.

Дело в том, что из-за существующей архитектуры у нас возникла проблема в том, что:

  • Публиковать 1С-ную базу в мир никто не хочет.

  • Мало того, что стандартная публикация – это небезопасно, сделать ее безопасной вообще сложновато. У многих безопасники просто при упоминании «1С в мир» говорят: «Нет, спасибо, лучше через VPN либо как-нибудь еще».

  • Плюс мы посматриваем на маленьких клиентов, а у них иногда вообще нет сисадминов – им не хочется возиться с IIS или Apache, смотреть, чтобы был «белый» айпишник либо возиться с VPN.

Поэтому наш всеми любимый CTO Олег Филиппов говорит: «Давайте мы поменяем стратегию, и у нас не фронт будет стучать в 1С, а 1С будет стучать во фронт и обмениваться сообщениями с ним?» В итоге мы избавляемся от необходимости что-то куда-то надо публиковать – просто указываем ссылку, 1С коннектится, обменивается сообщениями, и получаем все то же самое, но без всех этих заморочек с настройкой безопасности для веб-сервера.

 

Меняем стратегию. Приходим к тому, что…

 

 

Но не все так просто. 1С-ный HTTP-сервис нам многое дает – он сам роутит запросы, сам рулит сеансами, он достаточно быстрый – он огромную часть работы берет на себя. Поэтому нам пришлось все это переделывать под наши реалии.

  1. Первое, к чему мы пришли – это то, что нам нужен middleware, к которому будет коннектиться 1С, чтобы ожидать сообщения, которые требуется обработать.

  2. Мы решили, что крупные on-premise клиенты останутся на HTTP-сервисах. Мы просто отдаем им докер-образы, куда встроен наш фронт и все нужные компоненты, они это у себя разворачивают и сами парятся с сетевым взаимодействием. Т.е. мы хотели оставить в расширении работу HTTP-сервиса в привычном его понимании.

  3. При этом у нас не должно было быть такого, что для WebSocket-варианта запросы мы обрабатываем одним образом, а для HTTP-варианта – другим образом. Поэтому нам пришлось создать прокси-обертку над объектом HTTPСервисЗапрос, которая будет преобразовывать входящие параметры в удобочитаемый вид и для WebSocket-варианта, и для обычной публикации через HTTP-сервис.

  4. Мы должны были провести рефакторинг обработчиков запросов, чтобы они все находились в одном модуле и их можно было вызвать единообразным способом.

  5. Нужно было сделать свой роутинг запросов. Мы не можем просто по URL определить, вызову какого именно метода конфигурации 1С он соответствует – поэтому нам пришлось самим парсить URL: смотреть, что это за метод, какие у него параметры. Все это разбираем и гоним в свои обработчики.

  6. Мы не целились в высокую производительность, потому что понимали, что крупные клиенты этим пользоваться, скорее всего, не будут. Потому что для них не проблема развернуть HTTP-сервис. Мы были нацелены на небольших клиентов. Будет отлично, если мы выдержим 50-200 пользователей на фронте – сколько запросов при этом будет в 1С, мы не считали.

  7. И мы думали, что нам делать с запросами – открывать ли нам под них отдельный сеанс 1С, который будет вечно держать WebSocket-соединение, обмениваться сообщениями и заниматься всей этой вакханалией.

 

 

Для реализации нашей прекрасной задумки мы рассматривали четыре варианта:

  • В первую очередь мы смотрели на протокол для сетевого взаимодействия AMQP. С ним работает довольно известная стабильная компонента Pink Rabbit – бери и пользуйся. Но поскольку у AMQP очень много того, что мы никогда не будем использовать, нам показалось, что он не подойдет.

  • Следующим вариантом было написать либо сервис, либо демона для таргетной операционной системы, с которым 1С будет обмениваться по какому-то протоколу. В качестве протокола мы рассматривали HTTP, потому что ничего лучше HTTP мы не придумали. Но эту идею мы тоже максимально быстро откинули, потому что это ничем не лучше 1С-ного веб-сервера.

  • Следующим вариантом был Long Polling, но по тестам у нас не получилось завести нормальный Long Polling, чтобы он достаточно быстро обрабатывал наши запросы, и чтобы оно вообще стабильно работало.

  • Мы пошли на сайт Инфостарта и нашли там WebSocket-компоненту. Посмотрели, как она работает, и решили, что WebSocket нам подойдет. К тому же мы посмотрели опубликованные планы развития платформы, увидели, что WebSocket скоро должен появиться в платформе, а значит в какой-то момент мы от своей компоненты уйдем, и нужно будет переписать лишь маленькую часть кода, чтобы наше решение работало дальше. А если учесть, что под капотом у WebSocket тот же самый HTTP-протокол, просто с некоторыми моментами про установку соединения – большого оверхеда быть не должно.

 

 

Как мы все-таки выкрутились из ситуации с постоянно открытым сеансом, который должен обрабатывать запросы пользователя?

Мы сделали регламентное задание, которое смотрит, сколько обработчиков соединений указано в настройках, и при необходимости запускает недостающие. Сейчас у нас рабочий вариант – около 3-5 обработчиков.

Обработчики живут определенный промежуток времени, потому что Nginx, который у нас выступает как реверс-прокси, через какое-то время разрывает соединение, и нам нужно переподключаться заново. Такой разрыв соединения может произойти через час или 24 часа, поэтому мы добавили ограничение на время жизни обработчиков. Причем это ограничение более-менее динамическое, чтобы обработчики умирали в разное время, и мы не держали в базе вечные фоновые задания. Потому что когда кто-то что-то захочет в базе обновить, придется вырубать все фоновые, а такого сценария нам хотелось избежать.

Компонента может течь. Мы, конечно, пробовали использовать современные возможности C++: RAII, автоматическое управление памятью и другие инструменты. Однако это не гарантирует защиты от утечек во внутренних библиотеках, на которых основана наша компонента. Перезапуская обработчик, мы очищаем память и устраняем возможные утечки.

Поскольку библиотека, которую мы использовали, взаимодействует по WebSocket-протоколу асинхронно, сообщение внутри компоненты попадает во внутреннюю очередь, откуда их потом вытягивает 1С. Если запросов будет слишком много, внутренняя очередь забьется, и 1С не успеет ее разобрать. Поэтому мы реализовали возможность сбросить очередь в компоненте.

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

 

 

Но не все так радужно. Компоненту, которую мы использовали на этапе MVP, мы брали на Инфостарте, но сейчас от публикации компоненты осталась только ссылка на веб-архив.

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

 

 

Заново переписывать и все перепридумывать нам не хотелось. И мы приняли волевое решение написать компоненту самостоятельно.

У нас была небольшая экспертиза в C++, и нас смотивировало видео на YouTube от «Веселого 1С», где Егор Иванов рассказывал про то, как с помощью его шаблона можно безболезненно в рамках современного C++ писать внешние компоненты со всеми современными фичами:

  • Благодаря пакетным менеджерам Conan и vcpkg вам больше не нужно возиться с тем, чтобы понять, какую версию библиотеки вообще нужно взять, а как быть с кроссплатформенностью. Все на себя берут пакетные менеджеры. Вам только нужно указать верхнеуровневые параметры – разрядность библиотеки, статическая или динамическая линковка и версия. У нас Conan.

  • Среда сборки – Cmake. Причем в стандартном 1С-шаблоне Cmake-файл и так прописан. У некоторых компонент я видел сборку через проект Visual Studio, но в современном мире C++, где Cmake является де-факто системой, которая будет управлять вашей сборкой, использовать Visual Studio для сборки – пожалуй, нет. Если когда-то решите писать компоненту, призываю вас, пользуйтесь Cmake.

  • Мы проверили, что по работе с памятью нас ничего не ограничивает. Посмотрели, что платформа 1С использует C++ 17, и решили тоже взять C++ 17.

    • Во-первых, он позволяет удобно работать с файловой системой. Плюс это уже устоявшиеся умные указатели, где больше не нужно вручную писать new и delete, как это было в период изучения C++ в универе, где забытый delete мог приводить к утечкам памяти.

    • Во-вторых, C++ 17 поддерживает контейнеры STL, вроде std::vector, которые работают для нашей задачи оптимально, благодаря внутренним оптимизациям скорости.

    • Также в C++ 17, как впрочем и во всём C++, реализован принцип Zero Cost Abstractions: абстракции не увеличивают накладные расходы. На этапе компиляции такие конструкции «схлопываются» в эффективный машинный код, что исключает излишнюю нагрузку. Для нас это оказалось подходящим решением.

Но не все так просто.

  • Несмотря на то, что мы живем в мире современного C++, не забывайте всегда перехватывать исключения в вашей внешней компоненте. При возникновении исключения оно поднимается по стеку вызовов до тех пор, пока не будет кем-то перехвачено. А если это неперехваченное исключение видит 1С, она просто рушит вам сеанс. Или, если используется изолированное подключение, она рушит тот процесс, который реализует работу с компонентами.

  • Учтите, что для всех внешних компонент, как и для всех 1С-ных объектов, вы должны выделять память через 1С-ный аллокатор платформы. Память для объектов платформы должна выделять и удалять только платформа. Иначе у вас потечет память и будут ошибки, которые вы устанете воспроизводить.

Второй QR-код на слайде – это описание API внешних компонент. Здесь описаны методы C++, которые должна экспортировать ваша библиотека, чтобы с ними могла взаимодействовать платформа. Здесь методы вроде установки языка, обработки внешних событий – много разного. Рекомендую почитать всем, кто будет когда-либо заниматься написанием компоненты.

 

Что получилось

 

 

Все ли так просто? Нет.

Мы написали компоненту, включили изолированный режим и начали тестировать.

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

Дело в том, что изолированное подключение внезапно очень сильно зависит от того, какой режим совместимости указан у вашей конфигурации. Если у вас недостаточный режим совместимости, то без явного указания того, что ваша компонента будет подключена изолированно, она подключается в неизолированном режиме. А значит, что любое исключение приводит к тому, что мы рушим rphost и все сеансы на нем.

Переписали, теперь подключается изолировано.

 

 

Теперь, если что-то падает, падает только процесс addnhost – тот, который реализует работу с компонентой. Что там внутри, не так важно, главное, что он не рушит rphost.

  • Важно, что не любая компонента может работать в изолированном режиме. Компоненты, которые сейчас есть в платформе – например, для сканера или работы с принтерами – вероятнее всего, изолированно подключиться не смогут. Потому что для них не реализован метод GetAttachedInfo, который сообщает платформе о том, что компоненту можно подключить изолированно. Метод GetAttachedInfo должен быть реализован, и у него должны быть соответствующие значения параметров.

  • И если у вас платформа ниже 8.3.21, где еще нет нужных типов, учитывайте такую ситуацию. Возможно, вам стоит уйти в сторону методов «Выполнить()» и «Вычислить()», чтобы у вас не было ошибки компиляции модуля, когда определенные типы, которые нужны компоненте для работы, в данный момент отсутствуют.

 

 

В итоге все получилось не очень радужно – есть еще проблемы, с которыми мы разбираемся:

  1. Это проблема прав фонового задания, потому что фоновые задания запускаются регламентным заданием, которое по умолчанию стартует под служебным пользователем. А поскольку у служебного пользователя права не такие, как у настоящего пользователя, которому мы выдаем наши группы доступа, с этим могут быть проблемы. Сейчас мы это фиксим – есть возможность запустить регламентное задание под определенным пользователем программно, и это, по идее, должно решить проблему.

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

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

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

  5. И последнее – файловые базы «идут мимо», потому что у нас работа построена на фоновых заданиях. А в файловой базе может одновременно выполняться не более одного фонового задания.

 

 

Мы протестировали производительность нашего решения на следующей инфраструктуре.

  • WebSocket сервер – виртуализация через Proxmax, Ubuntu 20.04 LTS; по памяти – 8 гигабайт DDR4 и процессор – Intel Xeon 4 ядра.

  • Сервер 1С – лицензия мини-сервера; 16 гигабайт DDR4; Intel Core i5 и SSD на 256 гигабайт.

Запущен один обработчик, просто чтобы было нагляднее.

Нужно учитывать, что вообще полноценно протестировать скорость работы нашего фронта довольно сложно, потому что:

  • есть кэширование браузера;

  • есть кэширование веб-сервера;

  • часть запросов может кэшироваться на уровне Cloudflare или Akamai;

  • кроме того, можно поэкспериментировать с переиспользованием сеансов для HTTP-сервиса – это тоже может ускорить работу. Причем можно рассматривать как автоматическое управление сеансами, так и ручное, где вы сами управляете сеансами. Возможно, это будет быстрее. Или не будет – как повезет.

 

 

Оказалось, что скорость работы через HTTP-сервис сопоставима со скоростью работы через нашу компоненту:

  • слева – результаты HTTP-сервиса 1С, который опубликован на IIS;

  • а справа – результаты последней версии нашей компоненты

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

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

Но пока идем сопоставимо – память не течет, не очень медленно, все отлично.

 

 

Что у нас с мониторингом?

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

  • В Cloud, который представляет собой nodejs-WebSocket-сервер, есть логирование nodejs. Оно также дублируется в Sentry – там есть алерты. Иногда люди сами посматривают, и, если что-то идет не так, мы всегда можем подтюнить WebSocket-сервер или быстро пофиксить какие-то ошибки, которые не ждут.

 

 

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

В целом у нас работает. Подрихтуем, подоформим код и будет лежать на GitHub.

Если у вас есть какие-то вопросы по EmplDocs, можете задать их на странице продукта.

 

Вопросы и ответы

 

Я не до конца понял, как перезаполняется очередь. Я так понимаю, что есть фронт со своим веб-сервером, есть middleware на какой-нибудь условной node.js и есть 1С. Получается, у middleware есть своя база данных?

Да, у middleware своя база данных. Сначала там был SQLite, потом от него в сторону MongoDB уехали.

Чтобы ничего не хранить, middleware кэширует эти запросы в зашифрованном виде.

Если он не получил информацию о том, что сообщение было успешно обработано, он потом еще два раза отправляет сообщение повторно на обработку. Если за два раза не обработали, то все – ошибка 500, что-то делайте с этим.

Сколько времени пришлось потратить на создание уже работающего прототипа?

На первую версию, где были отправка и получение – две-три недели.

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

Кто этим занимался, и какой у него опыт на C++ был до этого?

Занимался этим я. Как такового у меня опыта на плюсах никогда полноценного и не было. Я пилил на Qt дипломный проект, и все – в этом весь мой опыт на плюсах.

Кстати, если использовать шаблон для внешних компонент от Infactum, он вообще понятный, прозрачный.

Типовой от вендора тоже более-менее понятный, но он слишком низкоуровневый. Это не для 1С-ников, это для тех, кто знает, как на уровне API взаимодействия между платформой и компонентой работать с переиспользованием памяти, чтобы было по минимуму каких-то динамических аллокаций, копирований.

Если вы хотите с этим заморочиться – берите шаблон от вендора. Если вы хотите просто пилить компоненту, лучше взять шаблон Infactum или от lintest – у них два разных шаблона, в целом они плюс-минус сопоставимы.

 

*************

Статья написана по итогам доклада (видео), прочитанного на конференции INFOSTART EVENT.

См. также

Оптовая торговля Розничная торговля WEB-интеграция 1С:Управление торговлей 10 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Платные (руб)

Онлайн-заказ - это решение для автоматизации процесса оформления заказов на сайте в торговых организациях. Продукт обеспечивает легкое взаимодействие между компанией и клиентами через веб-интерфейс, интегрированный с 1С:Предприятие. Система позволяет снизить операционные расходы, повысить лояльность клиентов и оптимизировать работу отдела продаж.

57600 руб.

26.11.2024    1230    1    1    

4

Сайты и интернет-магазины WEB-интеграция Системный администратор Программист Пользователь Платформа 1С v8.3 Конфигурации 1cv8 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    18350    20    22    

18

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс 24. Разработка имеет двухстороннюю синхронизацию 1С и Bitrix24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (платформа начиная с 8.3.23): 1С:Управление торговлей, 1С:Управление Нашей фирмой 3, 1С:Комплексная автоматизация 2, Объединенное решение: Модуль 1С:CRM 3 (3.0.21.3) +1С:ERP Управление предприятием 2. При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

7200 руб.

04.05.2021    20557    13    19    

18

WEB-интеграция Программист Бизнес-аналитик Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Оптовая торговля, дистрибуция, логистика ИТ-компания Платные (руб)

Модуль "Экспортер" — это расширение для 1С, предназначенное для автоматизации процессов выгрузки данных. Оно позволяет эффективно извлекать, преобразовывать и передавать данные из систем 1С в интеграционную платформу Spot2D. Подсистема упрощает настройку, снижает количество ручных операций и обеспечивает удобный контроль данных.

14400 руб.

20.12.2024    317    2    0    

5

WEB-интеграция Программист Руководитель проекта Платформа 1С v8.3 Конфигурации 1cv8 1С:Франчайзи, автоматизация бизнеса Платные (руб)

Расширение значительно упрощает написание API на 1С. Веб программисты получают простой и понятный доступ к 1С. Описание API создаётся автоматически и представляется в виде удобном как для человека, так и для программной обработки.

24000 руб.

27.09.2024    2444    1    0    

3
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. partizand 139 22.11.24 18:17 Сейчас в теме
Нежелание публиковать понятно.
Непонятно, зачем публиковать в мир, если можно ограничится IP фронта.
2. BaphoBush 6 25.11.24 11:34 Сейчас в теме
(1) "В мир" = "за пределы локальной сети". Вайтлисты - одна из тех вещей, с чем возиться не хотелось.
3. Steelvan 307 03.12.24 16:51 Сейчас в теме
Вайтлисты - Белые списки (для русскоговорящих).
4. BaphoBush 6 03.12.24 16:52 Сейчас в теме
(3) Тогда уж "разрешающие списки"
5. Steelvan 307 16.12.24 20:23 Сейчас в теме
(4) Тогда уж "list of permission"

Вайтлисты = Белолисты (дословно именно так читают на забугорном)
Оставьте свое сообщение