Как мы выбирали идеальный протокол для мессенджера в ЕЦП.МИС (Медицинской информационной системе)

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

У нас в «РТ МИС» уже был мессенджер для ЕЦП.Медицинская Информационная Система</p><p></p>" data-abbr="МИС">МИС. Ну, как «мессенджер» – некий самописный сервис на Node.js и хранением сообщений в БД для общения врачей и групповых уведомлений типа «Терапия! Тортики в ординаторской, успевайте».

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

Основные задачи для мессенджера

  • текстовый чат пользователь – пользователь, обычно – это врач – врач или врач – медсестра;

  • различные уведомления пользователей, например, врача о появлении результатов анализов или прибытии пациента в приемное отделение;

  • текстовый чат в контексте пациента или случая лечения, когда врач может задать какие-то уточняющие вопросы другим врачам с привязкой к случаю лечения или пациенту;

  • передача файлов в сообщениях;

  • работа с историей сообщений (поиск, просмотр, отметки о прочтении).

    В перспективе

  • различные сценарии уведомления, например, уведомление врачу при смене статуса талона на запись в поликлинику, рассылки по должностям, структурным подразделениями;

  • «пейджер» – push-уведомления врачу на его личное мобильное устройство в случае, если врач не подключен к внутренней сети;

  • аудио-видео конференции (консилиум);

  • телемедицинские консультации, в том числе при участии пациента, авторизованного через Госуслуги;

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

Вперед, на поиски!

Когда речь заходит о каких-то системах сообщений, «олдфаги» вспоминают IRC и ICQ. Если надо «модно и молодежно» – речь заходит о Discord и Slack. Поклонники приватности берут Matrix. Все остальные используют Telegram. Вроде бы бери и пользуйся. Но для наших целей это все абсолютно неприменимо: медицинская информация, с которой работают врачи, – это такой забористый коктейль из персональных данных и медицинской тайны, что требует особых подходов, в частности:

  • практически вся работа идет в защищенном контуре, куда нет доступа посторонним сервисам;

  • персональные данные и другая чувствительная информация должна храниться и обрабатываться в РФ;

  • нежелательно использовать каких-то иностранных поставщиков.

В общем, все это резко ограничивает набор возможных решений.

А чего, собственно, хочется от мессенджера?

  • открытость – отсутствие vendor lock in, открытый код;

  • контроль – возможность развернуть self-hosted инсталляцию;

  • наличие реализаций (клиентов) под основные языки (платформы), используемые у нас: Java, PHP, Node.js, Python;

  • стабильность - технология должна пройти фазу «хайпа»;

  • развитие – технология не должна быть «мертвой»;

  • шифрование – хорошо, но не в первую очередь, вся работа идет в защищенном контуре;

  • интеграция – возможность встраивания в существующие системы, в частности, авторизация и список пользователей;

  • расширяемость – некий подход для создания расширений в протоколе/ПО без «глобальных костылей».

Где-то тут мы стали понимать, что Телеграм, Дискорд и прочий Слак нас не спасут. Нужно переходить в область Open Source. Задачу осложняло то, что нужно было интегрироваться с нашими существующими системами и сервисами, а еще требовалась передача различной специфики по случаю лечения (врач, срочность, тип диагноза и т.д.). Можно было бы придумать свой формат сообщений – обмениваться json-ами и передавать всю необходимую специфику в полях, но хотелось не терять возможности работы с какими-то «стандартными» клиентами без наших доработок для облегчения интеграции сторонних модулей.

Что делать? Продолжать пилить что-то свое или все же есть выход?

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

Беглый поиск показал активное развитие различных децентрализованных протоколов, например, Matrix, Signal. Но по ряду параметров, в частности, возможность работы с историей, это нам не подходило. Что-то стало откровенной экзотикой (OSCAR). Или более относилось в категорию «мессенджер» чем протокол (Mattermost). И тут кто-то вспомнил про XMPP. На самом деле у нас уже был опыт использования XMPP, но в качестве… корпоративного мессенджера. Как раз в тот период, когда ICQ уже перестала быть популярной, а что-то более продвинутое еще не набрало нужной популярности. В последствии, уже в «наше время» мы вторично пытались его использовать, но несмотря на интересные фишки, которые там появились, наши технические специалисты не смогли (или не захотели) толком все настроить и XMPP проиграл гонку какому-то платному решению.

Краткое введение в XMPP

XMPP

eXtensible Messaging and Presence Protocol – «расширяемый протокол обмена сообщениями и информацией о присутствии», ранее известный как джа́ббер. Открытый, основанный на XML, свободный для использования протокол для мгновенного обмена сообщениями и информацией о присутствии в режиме, близком к режиму реального времени. Изначально спроектированный легко расширяемым протокол, помимо передачи текстовых сообщений, поддерживает передачу голоса, видео и файлов по сети.

JID

Jabber Identifier строится по тому же принципу, что адрес электропочты: имя@домен. Может быть записан в краткой форме имя@домен (bare JID) или в полной (full JID) имя@домен/ресурс. Ресурс служит для того, чтобы можно было различить нескольких клиентов, подключенных к одной учетной записи. У каждого клиента ресурс должен быть уникальным. Тогда мы можем выбирать послать сообщение только одному клиенту или всем сразу. JID может быть не только у пользователя, но и у чат-комнаты, подписки и т.д. Для ресурса вводится понятие «приоритета» – если сообщение будет отправлено на краткий JID то оно будет доставлено тому клиенту, приоритет которого выше (или всем, если приоритет у всех одинаковый).

Станза (строфа)

Законченный элемент XML-потока, который содержит определённую управляющую информацию:

  • информация о присутствии (Presence) — информационные пакеты специального вида, которые содержат в себе информацию о том, подключен ли в данный момент определенный JID к сети Jabber, а также передаёт его статус, статусное сообщение и приоритет;

  • IQ (Info/Query) – особый вид стансов, реализующий механизм типа «запрос-ответ». Интерпретация IQ-станс позволяет «сущности» сделать запрос и получить ответ от другой «сущности». Тип данных, передающихся в запросе или ответе определяет пространство имён (namespace) дочернего элемента по отношению к IQ;

  • сообщение (Message) – используется для обмена сообщениями между пользователями. Выглядит примерно так:
    <message from="doctor_maria@example.ru/Desktop" to="doctor_anna@example.ru" type="chat"><body>Привет, как дела?</body></message>

Ростер (список контактов)

Разбитый на группы список Jabber-адресов ваших собеседников (контактов). Хранится на сервере и передаётся клиенту по запросу. Сервер также обрабатывает запросы на добавление, удаление контакта из списка, а также смены группы для конкретного контакта.

XEP (расширения)

XMPP Extension Protocol — расширение протокола XMPP. Например, XEP-0045 – многопользовательский чат, XEP-0084 – поддержка аватарок пользователей, XEP-0107 – статус пользователя (user mood). XEP описывают как какие-то базовые вещи (XMPP Core), так и множество продвинутого и очень интересного функционала.

Вообще, расширения – одна из самых интересных особенностей XMPP, когда, используя кирпичики описанные выше (различные стансы), мы описываем необходимый нам функционал. При желании можно сделать и собственное расширение. В настоящий момент насчитывается порядка двух сотен действующих XEP.

Использования XMPP

Вконтакте

Открытое тестирование XMPP, июль 2010

Отключение сторонних XMPP-клиентов, июль 2013

Одноклассники

В Одноклассниках появилась поддержка Jabber, октябрь 2011

NSA

NSA использует тот же протокол что хакеры и активисты, декабрь 2014

Facebook

В связи с переходом на Platform API 2.0 прекращается поддержка XMPP Chat API, июль 2015

WhatsApp

Используется FunXMPP – доработанная версия, октябрь 2017

Eve Online

Перешли с собственного решения на ejabberd, февраль 2018

Cisco Meeting Server

Используется XMPP, июль 2019

Zoom

Чат Zoom основан на стандарте XMPP, апрель 2020

Для всех этих случаев можно выделить одну картину (особенно характерную для больших порталов): быстрый старт сервисов, используя XMPP, а затем, когда вступает в игру коммерческая составляющая и стоит задача привязать пользователя к порталу, уже рождаются какие-то собственные решения, возможно, остающиеся в своей массе основанными на XMPP.

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

Выбираем сервер

Самыми часто упоминаемыми серверами XMPP являются (в скобках – язык реализации):

  • Ejabberd (Erlang)

  • Prosody (Lua)

  • Openfire (Java)

Когда вы читаете про десятки и сотни тысяч пользователей, которых держит один XMPP-сервер, скорее всего, речь идет о Ejabberd. Но мы сразу понимали, что возможны доработки, а специалистов по Erlang среди нас не было. Поэтому выбор пал на Openfire от компании Igniterealtime, кстати, автора одного из самых популярных XMPP-клиентов для Android – Smack.

Детали нашей реализации

XMPP сервер – Openfire https://www.igniterealtime.org/projects/openfire/.

Клиент для фронтэнда – Strophe.js https://github.com/strophe/strophejs.

Клиент для Java сервисов и Android – Smack https://www.igniterealtime.org/projects/smack/.

Интеграция с хранилищем пользователей и системой авторизации – реализована через плагины Openfire.

Для оптимизации работы с нашим веб-приложением на PHP реализовали отправку сообщений через плагин с REST API – иначе каждый раз авторизовываться получается накладно по времени и ресурсам. Дополнительная фишка плагина – поддерживается отправка сообщений в json, включая наши дополнительные поля:

{
	"from": "Отправитель",
	"to": "Получатель",
	"headers": {
		"urgency": {
			"@xmlns": "http://rtmis.ru/protocol/xmpp/common",
			"value": 3
		},
		"disease": {
			"@xmlns": "http://rtmis.ru/protocol/xmpp/disease",
			"diag": {
				"@code": "X57",
				"value": "Лишения неуточненные"
			},
			"phase": {
				"@id": 1,
				"value": "Ранняя"
			}
		}
	},
	"body": "Пациент находится в приемном отделении"
}

Кроме того, сообщения в этом плагине складываются в очередь – дополнительный плюс для масштабируемости.

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

Общие впечатления по XMPP и Openfire

XML-природа протокола пусть избыточна, но строга и удобна.

XEP описывают много «вкусных» вещей, но надо внимательно смотреть, что реализовано для конкретных клиента и сервера. Список для Openfire: http://download.igniterealtime.org/openfire/docs/latest/documentation/protocol-support.html. Для нас, в целом, этот список оказался достаточным.

Понятие «ресурса» («устройства») – может быть применено очень широко, например, у нас в качестве «устройства» может выступать боковая панель уведомлений для веб-приложения, основное окно чата в том же веб-приложении, мобильное устройство, приложение – «пейджер» и т.д.

К достоинствам Openfire можно отнести:

  • активную разработку;

  • много готовых плагинов https://www.igniterealtime.org/projects/openfire/plugins.jsp;

  • хорошие возможности для кастомизации: с помощью плагинов и расширений можно настроить авторизацию, обработку пакетов, маршрутизацию и многое другое.

Из недостатков:

  • не очень удачно реализован механизм плагинов, реализующих собственное REST API. По всей видимости, авторы изначально не особо рассчитывали на такое применение, поэтому получилось то, что получилось;

  • отсутствует автоматическая чистка истории в комнатах – удаляем скриптом из БД;

  • при старте Openfire подгружается ВСЯ история по ВСЕМ комнатам – приводит  к резкому росту потребления памяти, решилось ограничением глубины истории;

  • по умолчанию неиспользуемые комнаты удаляются. Долго искали в чем причина, пока не нашли что это регулируется опцией «Disable MUC room unloading for this service» в свойствах службы группового чата. Здесь же можно настроить после скольких дней неиспользуемая комната будет удалена, а также загружать или нет все комнаты при старте.

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

Заключение

Если нужно быстро поднять корпоративный централизованный мессенджер или интегрировать его в существующий продукт – XMPP и Openfire отличный вариант для старта. Чат, групповой чат – все работает «из коробки».

Нужно внимательно смотреть какие XEP реализует используемый сервер и клиент.

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

Перспективы (планы на следующий этап):

  • переход на использование PubSub вместо группового чата для уведомлений;

  • «Пейджер» и push-уведомления для врачей – с отправкой обезличенных данных по незащищенным сетям;

  • авторизация через Госуслуги;

  • интеграция Openfire с Jitsi (https://jitsi.org/) для аудио-видео конференций;

  • интеграция Openfire с Minio/IPFS для хранения больших файлов, в том числе записей конференций.

Источник: https://habr.com/ru/company/rostelecom/blog/711692/


Интересные статьи

Интересные статьи

Привет! Меня зовут Саша Шутай, я тимлид в AGIMA. В прошлой статье я рассказывал, что делать, если на проекте Bitrix сожительствует с Vue.js и поисковые боты не видят контента сайта. А в этой помогу ра...
Выгрузка пользователей из 1C ЗУП в Битрикс24 или правдивая история о том как настроить интеграцию 1С-Битрикс24 с ЗУП без 1С-ника.В жизни так бывает, причём бывает чаще чем хотелось бы, хоть в целом и ...
Эта статья для тех, кто собирается открыть интернет-магазин, но еще рассматривает варианты и думает по какому пути пойти, заказать разработку магазина в студии, у фрилансера или выбрать облачный серви...
За годы игры в одну мобильную ММОRPG у меня накопился некоторый опыт по ее реверс-инжинирингу, которым я хотел бы поделиться в цикле статей. Примерные темы: Разбор формата сообщений между се...
Сегодня мы рассмотрим протокол BGP. Не будем долго говорить зачем он и почему он используется как единственный протокол. Довольно много информации есть на этот счет, например тут. Итак, что т...