Хабрахабр

Пишем мессенджер с открытым исходным кодом

tinode logo

И был у нее удивительный частный Интернет за заборчиком, где вместо URL-ов были "keywords": что-то среднее между адресом веб страницы и купленным ключевым словом в рекламе. Давным-давно в одной далекой стране была компания America Online. Компании боролись за интересные ключевые слова, как сейчас борются за домены, а реклама выглядела так: "посетите нас во всемирной сети по адресу www.example.com, или наберите AOL Keyword: 'banking'".

Сейчас роль Америки Онлайн играют основные мессенджеры: все они за заборчиками, несовместимы друг с другом, все изобретают свои keywords, желают схватить пользователя и уже никогда не отпускать. История имеет свойство повторяться. В результате невозможно послать сообщение даже из WhatsApp в Facebook Messenger, несмотря на то, что оба принадлежат одной компании. Компании не заинтересованы в открытости: более крупные игроки не желают делиться пользователями с более мелкими и уж тем более становиться открытыми. Да и пользователи ценят надежность и удобство выше абстрактной открытости, хотя многих раздражает, что часть друзей, например, в Telegram, часть в WhatsApp, а родители в Skype.

Ситуацию хочется изменить. А вот роль открытого интернета, к сожалению, сегодня не играет никто. И тут рассказ про Tinode. Если XMPP не справился, может быть кто-то другой сможет?

Что такое Tinode

Все клиентские приложения (ReactJS и Андроид) лицензированы под Apache 2. Tinode — мессенджер с полностью открытым исходным кодом на Github. 0. 0, для того, чтобы упростить создание коммерческих приложений на основе Tinode, сервер под GPL 3. Поставил — и все работает, как MySQL или Nginx. Цель проекта — создать федерированный мессенджер, который прост и удобен как для пользователей, так и для операторов. В долгосрочной перспективе цель проекта – создать открытую альтернативу существующим проприетарным мессенджерам, повторить в отношении мессенджеров то, что сделал Android в отношении операционных систем для мобильных телефонов.

Что он умеет

Поддержка множественных устройств.

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

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

Онлайн статус

Нужно, чтобы она "просто работала", предсказуемо и надежно. Трансляция онлайн/оффлайн статуса пользователя в мессенджерах воспринимается как что-то само собой разумеющееся, однако, это весьма тяжелая в реализации фича. В случае Tinode, сервер генерирует онлайн статус и рассылает по адресной книге, что, опять же, требует хранения контактов на сервере и их синхронизацию с клиентскими приложениями. Надежность работы исключила генерацию статуса на клиенте, как это реализовано в некоторых XMPP приложениях.

Простота протокола

Спецификация получилась очень компактной: 10 запросов клиента, 5 ответов сервера. Протокол хотелось сделать таким, чтобы кривая обучаемости была пологой – не нужно знать всего, чтобы начать. Например, по сравнению с 200+ страницами только core XMPP, не считая extensions, это почти записка на салфетке.

Протокол лишь требует определенную структуру данных, но не требует, чтобы они передавались по сети каким-то определенным образом. Представление данных отделено от сетевого протокола. Поддержка gRPC была реализована одним разработчиком за две недели, включая написание текстового клиента на Питоне. Сейчас сервер поддерживает JSON по websocket и long polling, c TLS и без, плюс gRPC по TCP. Добавление поддержки иных форматов данных и протоколов, например, MessagePack или Noise, вряд ли займет намного больше.

Расширяемость

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

Основной — это то, что позволяет Tinode выполнять основную функцию — пересылать сообщения. Было принято решение разделить функционал на три части — основной, сетевой и вспомогательный. Вспомогательный — то, что решает чью-то локальную задачу, например, поддержка конкретной базы данных в качестве бэкенда или какой-то метод авторизации, но никак не влияет на другие сервера или пользовательские приложения. Сетевой — функционал взаимодействия в серверами, как формат передаваемых данных и сетевой протокол. Сетевой функционал выделен, но также хранится в основном репозитории для того, чтобы по возможности избежать создания несовместимых серверов. Основной функционал реализован в основном коде. Вспомогательный реализован в виде плагинов — компилируемых Go интерфейсов (поддержка разных баз данных, разных авторизаторов, пуш нотификации, валидаторы по емейл или телефону, поддержка каптчи и т.п.) и gRPC endpoints (чатбот и поисковый интерфейс).

Прочее

  • Возможность, но не требование привязки счета к телефону или емейлу или ещё чему угодно.
  • ID пользователей, которые трудно угадать, и, соответственно, трудно разослать спам.
  • Tags, позволяющие реализовывать поиск людей как в WeChat (и, подобно WeChat, встроить в мессенджер службу знакомств) или разделить организацию на отделы как в Slack.
  • Возможность подключения пользователей без регистрации, необходимая, например, для организации службы поддержки через чат.
  • Интерфейс и пример подключения чатботов.
  • Планы создания каналов как в Telegram.

Почему Go?

Go (как и Erlang, но это уже другая история) идеально подходит для создания такого функционала т.к. Сервер для мессенджера по сути роутер: получает сообщение из одного канала, как-то его обрабатывает, затем передает в другой канал или каналы. содержит примитивы goroutine и chan, делающие организацию потоков и обмен данными между ними эффективным и простым.

Однако, при прочих равных, код скорее всего получится более сложным и потребует больших усилий для избежания дедлоков. Безусловно, роутер можно написать и на C/C++, и на Java.

А что потом?

Федерация

Так, чтобы любой желающий мог запустить свой Tinode сервер, который бы мог обмениваться сообщениями с любым другим сервером, точно так, как это возможно с емейлом. Одна из основных задач для Tinode на ближайший год — создание платформы для федерации. Сетевой обмен между сервером и клиентами идет по TLS websocket, что для внешного наблюдателя мало отличимо от простого HTTPS трафика. Уже сейчас возможна кластеризация серверов.

Однако, в будущем поиск чат-серверов будет осуществляться также, как это сделано в Bittorrent — при помощи DHT, распределенной хеш таблицы. Публичный DNS, вероятно, будет использоваться, по крайней мере первоначально.

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

Репутация и распределенное принятие решений

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

  • Криптографическая идентификация сервера-отправителя.
    Изначально, SMTP вообще не предполагал какой-либо идентификации отправителя. Не стоит снова наступать на эти грабли. Каждый сервер, желающий установить контакт, будет представлен криптографическим сертификатом. "Ну да", скажете вы, "я сейчас нагенерю 100500 сертификатов и каждый раз буду представляться новым чистым сервером". И будете правы. Поэтому следующий пункт.
  • Распределенный учет репутации.
    Когда к нам стучится новый, неизвестный сервер-отправитель, мы сделаем запрос к известным серверам с просьбой сообщить рейтинг нового сервера. И в зависимости от ответа установим, например, скорость, с которой новый сервер может посылать нам сообщения.

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

Шифрование

Чаты между двумя людьми, вероятно, будут шифроваться OTR. Ну а как же в наши дни без шифрования сообщений? Все известные схемы шифрования групповых чатов либо имеют значительный недостатки, либо тяжеловесны и сложны в реализации. С групповыми чатами пока непонятно. Также, не очевидно, насколько важно шифрование групповых чатов: "Если тайну знают двое – это уже не тайна, а если трое – это уже базар."

Что вы по этому поводу думаете?

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть