Создайте для себя распределенную систему обмена мгновенными сообщениями

Java Netty
Создайте для себя распределенную систему обмена мгновенными сообщениями

предисловие

счастливого Нового года всем!

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

Старые читатели должны помнить, что я поделился статьей перед Национальным праздником в прошлом году.«Проектирование системы push-уведомлений на уровне миллиона сообщений»;Хотя я разместил в тексте некоторый псевдокод, некоторые друзья все еще надеются поделиться неким исполняемым исходным кодом напрямую; пришло время заполнить дыру после стольких лет.

Структура каталогов:

Эта статья представляет собой длинное предупреждение с высокой энергией; принесите хорошую скамейку семян дыни.

Итак, на основе предыдущих я улучшил кое-какой контент, давайте взглянем на введение в этот проект:

CIM(CROSS-IM)разработчикIM(即时通讯)система; в то же время она предоставляет некоторые компоненты, помогающие разработчикам создавать свои собственные горизонтально масштабируемыеIM.

с помощьюCIMВы можете выполнить следующие требования:

  • IMсистема обмена мгновенными сообщениями.
  • применять кAPPпромежуточное ПО для отправки сообщений.
  • IOTПО промежуточного слоя для прозрачной передачи сообщений в сценариях массового подключения.

Полный исходный код размещен на GitHub:GitHub.com/crossover J я…

демо

На этот раз в основном используется обмен мгновенными сообщениями, поэтому я специально записал две видеодемонстрации (групповой чат, приватный чат).

Щелкните ссылку ниже, чтобы просмотреть видеоверсию демонстрации.

YouTube Bilibili
Групповой чат приватный чат Групповой чат приватный чат

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

Архитектурный дизайн

Давайте взглянем на конкретный архитектурный проект.

  • CIMКаждый компонент вSpringBootПостроить.
  • использоватьNetty + Google Protocol BufferВыстраивайте низкоуровневые коммуникации.
  • RedisХраните информацию о маршрутизации, информацию об учетной записи, онлайн-статус и т. д. каждого клиента.
  • Zookeeperиспользуется дляIM-serverРегистрация и обнаружение службы.

Все в основном состоит из следующих модулей:

cim-server

IMсервер; для приемаclientСоединение, прозрачная передача сообщений, отправка сообщений и другие функции.

Поддерживается кластерное развертывание.

cim-forward-route

Сервер маршрутизации сообщений; используется для обработки маршрутизации сообщений, пересылки сообщений, входа пользователей в систему, выхода пользователей из системы и некоторых операционных инструментов (получение количества онлайн-пользователей и т. д.).

cim-client

IMКлиент; для терминала сообщений, используемого пользователем, одна команда может запускать и инициировать общение с другими (групповой чат, приватный чат); в то же время для простоты использования встроены некоторые общие команды.

блок-схема

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

  • клиент дляrouteИнициировать вход.
  • Войти успешно изZookeeperвыберите доступныйIM-serverВернитесь к клиенту и сохраните данные для входа и маршрутизации вRedis.
  • клиент дляIM-serverИнициируйте длительное соединение и сохраните пульс после успеха.
  • Проходить, когда клиент отключаетсяrouteОчистить информацию о состоянии.

Поэтому, когда мы развертываем себя, нам нужны следующие шаги:

  • Создание базового промежуточного ПОRedis、Zookeeper.
  • развертыватьcim-server, это настоящий сервер обмена мгновенными сообщениями, чтобы соответствовать требованиям производительности, он поддерживает горизонтальное расширение, нужно только зарегистрироваться на том жеZookeeperВот и все.
  • развертыватьcim-forward-route, который является сервером маршрутизации, через который должны проходить все сообщения. Поскольку он не имеет гражданства, его также можно использоватьNginxПрокси улучшают доступность.
  • cim-clientНастоящий клиент для пользователя; после запуска он автоматически подключается к серверу обмена мгновенными сообщениями для отправки и получения сообщений с консоли.

Дополнительные сведения об использовании см.Быстрый старт.

детальный дизайн

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

сервер обмена мгновенными сообщениями

Давайте сначала взглянем на сервер; он в основном реализует такие функции, как клиент онлайн и офлайн, а также доставку сообщений.

Первый — запустить службу:

из-заSpringBootвстроенный, поэтому его нужно запускать при запуске приложения.NettyСлужить.

отpiplineВидно, что использованиеProtobufКодек (конкретные пакеты анализируются в клиенте).

Зарегистрируйтесь, чтобы узнать

должен быть удовлетворенIMТребования к горизонтальному расширению сервера, поэтомуcim-serverНеобходимо опубликовать собственные данные в реестре.

Вот ссылка на то, что было опубликовано ранее«Получение регистрации и обнаружения службы»Есть конкретные вводные.

Поэтому после успешного запуска приложения ему необходимо зарегистрировать свои собственные данные дляZookeeperсередина.

Основная цель - преобразовать текущее приложениеip + cim-server-port+ http-portЗарегистрироваться.

На картинке выше два, которые я зарегистрировал в демо-среде.cim-serverЭкземпляр (поскольку он на сервере, отличается только порт).

Таким образом на стороне клиента (послушайте этоZookeeperузел) может знать доступную в настоящее время служебную информацию в режиме реального времени.

Авторизоваться

когда клиент проситcim-forward-routeПосле завершения бизнес-проверки (точно так же, как при ежедневном входе на другие веб-сайты) в интерфейсе входа (см. ниже) клиент инициирует длительное соединение с сервером, как показано в предыдущем процессе:

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

После того, как сервер его получит, ему нужноuserIDи текущийChannelСвязь каналов сохраняется.

При этом информация пользователя также кэшируется, т.е.userIDи имя пользователя.

не в сети

Когда клиент отключен, кэшированная информация должна быть очищена.

Также необходимо позвонитьrouteСопутствующая информация об интерфейсе (см. ниже конкретные интерфейсы).

Маршрутизация мгновенных сообщений

Как видно из схемы архитектуры, уровень маршрутизации является очень важной частью, он обеспечивает рядHTTPСервис берет на себя клиент и сервер.

В настоящее время существуют в основном следующие интерфейсы.

интерфейс регистрации

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

Дизайн здесь относительно прост, непосредственно используйтеRedisдля хранения информации о пользователе; информация о пользователе также толькоIDиuserNameВот и все.

Просто для удобстваRedisсерединаKVСохранено сноваVK,такIDиuserNameДолжно быть уникальным.

интерфейс входа

войти здесь иcim-serverВход в систему отличается и носит деловой характер,

  • После успешного входа необходимо определить, является ли это повторным входом (пользователь может запустить только один клиент).
  • После успешного входа необходимоZookeeperПолучить список услуг в (cim-server) и выбрать сервис для возврата клиенту по какому-то алгоритму.
  • После успешного входа в систему необходимо сохранить информацию о маршрутизации, то есть экземпляр службы, назначенный текущим пользователем, сохраняется вRedisсередина.

Чтобы понять, что только один пользователь может войти в систему, используйтеRedisсерединаsetдля сохранения данных для входа; используйтеuserIDв видеkey, повторные входы в систему не будут записываться.

Подобно HashSet в Java, его можно только дедуплицировать и сохранить.

Получить доступный экземпляр маршрутизации также проще:

  • начать сZookeeperЗаставьте все экземпляры службы выполнять внутренний кеш.
  • Опрос для выбора сервера (на данный момент есть только один алгоритм, который будет добавлен позже).

Конечно, чтобы получитьZookeeperВполне естественно, что перед запуском экземпляра службы вcim-serverУзел, который был ранее зарегистрирован.

Конкретный код выглядит следующим образом:

Также слушайте после запуска приложенияZookeeperУзел маршрутизации в , и внутренний кэш обновляются, как только происходят изменения.

Здесь мы используем кеш Guava, основанный наConcurrentHashMap, поэтому можно гарантировать, что清除、新增缓存атомарность.

Интерфейс группового чата

Это реальный интерфейс сообщений, эффект в том, что один клиент отправляет сообщение, а все остальные клиенты могут его получить!

Процесс должен заключаться в том, что клиент отправляет сообщение на сервер, а сервер его получает, как описано вышеSessionSocketHolderперебрать всеChannel(канал), а затем отправить сообщение.

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

Следовательно, уровень маршрутизации должен играть определенную роль.

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

Отношения маршрутизации находятся вRedisхранятся следующим образом:

так какRedisОднопоточный характер, когда объем данных большой, когда ключи используются для сопоставления всехcim-route:*data, из-за чего Redis не сможет обрабатывать другие запросы.

Так что здесь вместо этого используйте команду сканирования, чтобы перебрать всеcim-route:*.


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

существуетcim-serverРеализация в следующем:

cim-serverПосле получения сообщения канал userID будет запрошен во внутреннем кеше, после чего останется только отправить сообщение.

онлайн пользовательский интерфейс

Это вспомогательный интерфейс, который может запрашивать информацию о текущем онлайн-пользователе.

Реализация также очень проста, то есть дедупликация, которая сохраняет состояние входа пользователя перед запросом.set"Вот и все.

Интерфейс приватного чата

Причина, по которой говорят, что привлечение онлайн-пользователей является вспомогательным интерфейсом, на самом деле используется для помощи приватным чатам.

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

Что-то вроде этого:

В нашем сценарии предпосылкой приватного чата является привлечение онлайн-пользователей.userID.

Поэтому интерфейс приватного чата должен запрашивать местоположение получателя после получения сообщения.cim-serverИнформация об экземпляре, последующие шаги такие же, как и в групповом чате. вызвать экземпляр получателяHTTPИнтерфейс предоставляет информацию.

Просто групповой чат — это разница между обходом всех онлайн-пользователей и отправкой только одного приватного чата.

автономный интерфейс

Когда клиент находится в автономном режиме, нам нужно сохранитьRedisЧасть информации в нем удалена (информация о маршрутизации, статус входа).

клиент обмена мгновенными сообщениями

Часть логики в клиенте уже упоминалась выше.

Авторизоваться

Первым шагом является вход в систему, который нужно вызвать при запускеrouteинтерфейс входа, получитьcim-serverинформацию, а затем создать соединение.

image-20190102001525565

во время входа в системуrouteИнтерфейс определит, является ли это повторным входом в систему, и повторный вход приведет к непосредственному выходу из программы.

Далее следует использоватьrouteвозвращается интерфейсомcim-serverинформация об экземпляре (ip+port) для создания соединения.

Последним шагом является отправка сообщения о входе в систему на сервер, в котором клиент иChannelОтношение.

пользовательский протокол

некоторые из вышеперечисленных登录报文、真正的消息报文На самом деле они различимы в нашем пользовательском протоколе.

Поскольку он используетсяGoogle Protocol Bufferкодек, поэтому сначала посмотрите на исходный формат.

Фактически, в настоящее время в этом протоколе есть три поля:

  • requestIdможно понимать какuserId.
  • reqMsgэто реальная новость.
  • typeТо есть, категория сообщения, упомянутая выше.

В настоящее время существует в основном три типа, соответствующие различным предприятиям:

сердцебиение

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

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

Таким образом, когда сервер не получает деловое сообщение каждую минуту, он будет получатьpingПакет сердцебиения:

встроенные команды

Клиент также имеет несколько встроенных основных команд для простоты использования.

Заказ описывать
:q Выйти из клиента
:olu Получить всю информацию о пользователе онлайн
:all получить все команды
: Другие команды находятся в разработке. .

такие как ввод:qКлиент будет закрыт, и некоторые системные ресурсы будут закрыты одновременно.

при входе:olu(onlineUserсокращение от ) позвонитrouteдоступа ко всем пользовательским онлайн-интерфейсам.

Групповой чат

Пользоваться групповым чатом очень просто, нужно только ввести сообщение в консоли и нажать Enter.

затем позвонитrouteинтерфейс группового чата.

приватный чат

То же самое верно и для приватного чата, но предпосылка заключается в том, что вам нужно активировать ключевые слова; используйтеuserId;;消息内容Этот формат будет отправлять сообщение только определенному пользователю, поэтому обычно необходимо использовать:oluПриобретение команд удобно для онлайн-пользователей.

обратный вызов сообщения

Чтобы удовлетворить некоторые требования к настройке, например, сообщения должны быть сохранены и так далее.

Таким образом, после того, как клиент получит сообщение, он вызовет интерфейс, в котором реализация может быть настроена.

Итак, сначала создайтеcallerизbean,этоbeanсодержитCustomMsgHandleListenerИнтерфейс, вам нужно реализовать этот интерфейс только в том случае, если вам нужно справиться с ним самостоятельно.

настраиваемый интерфейс

Поскольку я сам мало что знаю о написании интерфейсов, я не уверен, что другие большие коровы будут. Таким образом, групповой чат, приватный чат, привлечение онлайн-пользователей, обратный вызов сообщений и другие услуги (и последующие услуги) в клиенте предоставляются в виде интерфейсов.

Интеграцию страниц также удобно делать позже, нужно только настроить эти интерфейсы, не нужно заботиться о конкретной реализации.

Суммировать

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

Дальнейшие планы:

Полный исходный код:

GitHub.com/crossover J я…

Если эта статья оказалась для вас полезной, пожалуйста, перешлите ее.