Обмен сообщениями «точка-точка» в масштабируемом приложении?

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

HLD, насколько я понимаю:- Допустим, друг 1 и друг 2 находятся в сети. Друг 1 установил веб-соединение HTTP с веб-сервером 1, а друг 2 установил веб-соединение HTTP с веб-сервером 2. Друг 1 отправил сообщение другу 2.

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

Я считаю, что здесь можно использовать распределенные пользовательские очереди Java для распространения сообщения с одного сервера на другой. Как только сообщение поступает на один сервер, оно помещается в распределенную очередь (распределенная очередь из-за балансировки нагрузки и высокой доступности) с содержимым сообщения, fromUserId, toUserId. В очереди будет слушатель, который увидит идентификатор пользователя только что появившегося сообщения и найдет, на каком веб-сервере идентификатор пользователя является активным. Если пользователь активен, выведите сообщение и отправьте его клиенту, в противном случае сохраните его в БД, чтобы его можно было извлечь после подключения к сети. Чтобы увидеть, какой пользователь активен на каком сервере, мы можем поддерживать древовидную карту с идентификатором пользователя в качестве ключа и значением в качестве имени сервера для эффективного поиска.

Вероятно, фактический дизайн должен быть более сложным/масштабируемым, чем описанный выше. Хотите знать, правильное ли это направление для масштабируемого чат-мессенджера?

Также я считаю, что нам нужно иметь несколько распределенных очередей вместо одной для такого масштабируемого приложения. Но если у нас есть несколько распределенных очередей, как система обеспечит доставку сообщений FIFO по распределенным очередям?


person user3198603    schedule 28.01.2017    source источник


Ответы (2)


Хотели бы знать, правильное ли это направление для масштабируемого чат-мессенджера?

Разработка этого приложения с использованием очередей сообщений имеет следующие преимущества:

  • Разъединение клиент-сервер и уменьшение количества сбоев: очереди могут изящно справляться с пиками трафика, просто временно увеличивая размер очереди, который вернется к нормальному состоянию, пока трафик снова нормальный (или любые временные сбои были исправлены)
  • В приложении для обмена сообщениями клиенты (мобильные устройства) могут находиться в автономном режиме в течение длительного времени. В результате синхронный дизайн не будет работать, поскольку клиенты могут быть недоступны для доставки сообщений. Однако при асинхронном дизайне, как и в случае с очередями сообщений, ответственность за доставку сообщений лежит на стороне клиента. В результате клиент может опрашивать новые сообщения, как только он подключается к сети.

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

Но если у нас есть несколько распределенных очередей, как система обеспечит доставку сообщений FIFO по распределенным очередям?

Многие очереди с открытым исходным кодом (rabbitMQ, activeMQ) или коммерческие (AWS SQS) поддерживают порядок FIFO. Однако гарантии FIFO внутри очереди недостаточно, поскольку сообщения, отправленные одним клиентом, могут быть доставлены в очередь в другом порядке из-за проблем с асинхронностью в сети (если только вы не используете одну нераспределенную очередь и TCP). что гарантирует доставку заказанного товара).

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

person Dimos    schedule 31.01.2017
comment
Спасибо Домос. Пара дополнительных вопросов о выделенной очереди для каждого пользователя.However, with an asynchronous design as with message queues, the responsibility of message delivery is on the client side. As a result, the client can poll for new messages as soon as it gets online - Я считаю, что нам нужно сохранить сообщение в БД, если получатель находится в автономном режиме, иначе размер очереди будет продолжать расти в памяти. - person user3198603; 01.02.2017
comment
The only thing to have in mind is that this design would require a separate queue for each user, so the number of queues would scale linearly with the number of the application's users (which could be a significant financial & scalability issue). - Вы имеете в виду, что как только появится сообщение, мы создадим очередь для каждого пользователя (я полагаю, здесь вы имеете в виду пользователя-получателя, а не пользователя-отправителя. Верно?) Будет ли эта очередь существовать вечно или она умрет, если пользователь выйдет из сети? Также я думаю, что эта очередь приема может быть создана, когда любой пользователь подключается к сети или отправитель отправляет сообщение получателю. ... - person user3198603; 01.02.2017
comment
При входе в сеть каждый пользователь будет проверять, создана ли для него очередь, если да, просто подпишитесь на эту очередь, в противном случае создайте очередь и подпишитесь. Верно ? - person user3198603; 01.02.2017
comment
Что если вместо создания очереди для каждого пользователя вы поставите в очередь те сообщения, которые не могут быть доставлены из-за того, что пользователь не в сети? - person Aldana; 01.02.2017
comment
1. Не обязательно, например, Amazon SQS не имеет ограничения на размер очереди. Он может расти бесконечно. 2. Я имею в виду одну очередь на аккаунт вашего приложения. Это связано с тем, что даже в групповых чатах вы хотите, чтобы сообщение было доставлено столько раз, сколько пользователей участвует в чате. Итак, в этом случае каждое сообщение будет доставлено в очередь каждого участника чата. В обычном чате сообщение будет доставлено одному получателю. - person Dimos; 01.02.2017
comment
3. Очередь может быть создана один раз, когда пользователь создает учетную запись. Эта очередь будет использоваться для доставки всех сообщений для этого пользователя (для всех чатов, в которых он участвует). 4. Наверняка есть и другие возможные конструкции. Была предложена одна очередь для каждого пользователя, потому что сообщения очереди читаются только один раз. Например, если у вас есть очередь на чат, только первый пользователь прочитает сообщение (а не все участники). - person Dimos; 01.02.2017
comment
@Dimos 1. Not necessarily, for example Amazon SQS has not limit for the queue size. It can grow infinitely. Будет ли реализация очереди, такая как Amazon SQS (или любая другая), быстрее, чем хранилища БД, такие как oracle и т. Д.? Мое понимание: - Возможно, реализация на основе очереди будет быстрее, потому что она находится в хранилище памяти, но в оракуле ее нужно извлекать с жесткого диска, хотя в обоих случаях вызов должен проходить по сети и извлекать ее. Это правильно ? .... - person user3198603; 03.02.2017
comment
Как только сообщение доставлено, я считаю, что нам нужно сохранить это сообщение в БД также для обработки случая, когда пользователь хочет прочитать какое-то прошедшее сообщение (после прокрутки вверх). не так ли? Или Queue также может решить это требование. Я думаю, что нет. - person user3198603; 04.02.2017
comment
После того, как сообщение доставлено, оно должно быть сохранено на мобильном устройстве. Однако то, что вы упоминаете, применимо в случае, если вы хотите, чтобы пользователь мог получать сообщения даже при удалении переустановки приложения. - person Dimos; 04.02.2017
comment
However, what you are mentioning is applicable in case you want the user to be able to retrieve the messages even when uninstalling the re-installing the application . Это правильно. В этом случае нам также необходимо сохранить сообщение в БД. Вероятно, после того, как сообщение будет прочитано получателем, оно может поместить его в отдельную очередь, а пакетное задание может сохранить его оттуда в БД? - person user3198603; 04.02.2017
comment
Также вы согласны с анализом того, что реализация на основе очереди будет быстрее, потому что она находится в хранилище памяти, но в оракуле ее необходимо извлекать с жесткого диска, хотя в обоих случаях вызов должен проходить по сети и извлекать ее. Второй фактор, который делает решение очереди более масштабируемым, заключается в том, что мы можем отправить сообщение клиенту, но в случае БД клиент должен вытащить его, что слишком много вызовов на сервере через регулярный интервал времени. - person user3198603; 04.02.2017
comment
@Dimos Не могли бы вы ответить на мои последние два комментария? - person user3198603; 06.02.2017
comment
Probably once message is read from receiver it can put it in separate queue and batch job can save it to DB from there, да, нужна еще одна очередь. Я бы посоветовал не использовать очереди, которые хранят сообщения только в памяти. Большинство очередей теперь также сохраняют сообщения на диске. Превосходство над базами данных происходит благодаря шаблонам доступа (не памяти по сравнению с диском). Лучшая масштабируемость в основном связана с тем, что очереди распределяются намного проще, чем базы данных, и они гораздо более эластичны. Проверьте youtu.be/zwLC5xmCZUs?t=1494, чтобы понять, о чем я говорю. - person Dimos; 06.02.2017
comment
когда вы говорите Most of the queues now persist messages in disk as well. The superiority to databases comes thanks to the access patterns (not memory vs disk)., вы имеете в виду, что очереди теперь в течение нескольких дней могут хранить данные в обоих местах одновременно, то есть в памяти и в БД? Я считаю, что эта очередь должна помещать данные в БД асинхронно. - person user3198603; 07.02.2017
comment
Не обязательно. Два наиболее часто используемых брокера очередей сообщений (ActiveMQ, rabbitMQ) предоставляют механизмы синхронного сохранения. Проверьте rabbitmq.com/confirms.html и activemq.apache.org/async-sends.html - person Dimos; 07.02.2017
comment
@Dimos я прочитал обе ссылки, но у меня есть вопрос. Я думаю, вы говорите о длительных очередях. Мой вопрос: будет ли долговременная очередь сначала хранить сообщение в памяти, а через некоторое настраиваемое время будет сохраняться в БД? Или он сохранит его как в памяти, так и в БД, как только придет сообщение. - person user3198603; 08.02.2017
comment
Это зависит от механизма, который вы используете. В этом разница между синхронными вызовами и асинхронными вызовами. Синхронные вызовы сохраняют сообщение на диск до ответа клиенту, а асинхронные вызовы отвечают непосредственно перед сохранением на диск. Вам нужно будет внимательно прочитать документацию для получения более подробной информации о конкретных продуктах очереди. - person Dimos; 08.02.2017
comment
@Димос Спасибо. Последний вопрос. Знаете ли вы, действительно ли WhatsApp или любое другое крупное приложение для чата использует модель создания выделенной очереди в памяти для каждого пользователя при создании учетной записи пользователя? Я имею в виду, что у приложения Whats более 0,6 миллиарда пользователей, поэтому просто интересно, действительно ли они создали 0,6 миллиарда очередей? - person user3198603; 10.02.2017
comment
Я не знаю для конкретных приложений. Но я предполагаю, что они не выделяют одну очередь для каждого пользователя (или даже для каждой группы чата). Вероятно, они приняли более гибкие дизайнерские решения. Вы можете просмотреть их инженерные блоги и узнать, что они уже раскрыли там некоторые свои дизайнерские решения. - person Dimos; 10.02.2017
comment
@Dimos, у вас есть ссылки на их инженерный блог? Я много дней пытался гуглить, но не нашел хорошей ссылки на проектное решение, кроме одного ресурса высокого уровня, т.е. quora.com/What-is-WhatsApps-server-architecture - person user3198603; 11.02.2017
comment
blog.whatsapp.com Вы также можете ознакомиться с этим блогом opensourcery.co.za/2009/04/ 19/ - person Dimos; 11.02.2017

 Would like to know if this is the right direction for scalable chat messenger?

Я бы, наверное, предпочел немного другой подход. Ваши идеи верны, но я хотел бы добавить немного больше к тому же. Мне довелось создать такой чат-мессенджер несколько лет назад, и он должен был быть очень похож на watsapp. Я уверен, что когда вы гуглили, вы бы сталкивались с расширяемым протоколом обмена сообщениями и присутствия XMPP. мы использовали openfire в качестве сервера, поддерживающего соединения. Концепция, которую вы объяснили, где

Say Friend 1 and Friend 2 are online . Friend 1 has established HTTP web connection to web server 1 and Friend 2 has established HTTP web connection to web server 2. Friend 1 send the message to Friend 2.

называется федерацией, и openfire может работать в федеративном режиме. Прочитав ваши комментарии, я наткнулся на одну очередь на точку пользователя. Я уверен, что вы уже знаете, что этот подход не является масштабируемым, так как он очень ресурсоемкий. Хорошим подходом было бы использование фреймворка Актера, такого как akka. Каждый актер похож на легкий поток в Java, и у каждого актера есть почтовый ящик. поэтому в этом случае об обмене сообщениями позаботятся.

Таким образом, ваш сценарий преобразуется в Друг 1, открывает соединение с сервером openfire xmpp и инициализирует актера Друга 1. Когда он набирает сообщение, оно передается в папку «Входящие» актера Друга 1 (у каждого актера в akka есть почтовый ящик в памяти). Это передается на сервер xmpp. У сервера есть собственная база данных, и, поскольку он объединен с другими серверами xmpp, он попытается найти, находится ли друг 2 в сети. Сервер xmpp будет хранить сообщение в своей базе данных до тех пор, пока друг 2 не подключится к сети. Как только друг 2 устанавливает соединение с любым сервером xmpp, создается актер друга 2, и его присутствие распространяется на все остальные серверы, а сервер xmpp 1 уведомляет актера друга 2. Входящие сообщения актера друга 2 теперь будут получать сообщение

Опционально: Существует также возможность получения квитанции о доставке. Как только друг 2 прочитает сообщение, другу 1 может быть отправлена ​​квитанция о доставке, чтобы указать статус сообщения, т. е. прочитано, не прочитано, доставлено, не доставлено и т. д.

person Raveesh Sharma    schedule 03.02.2017
comment
Как вы сказали Each actor is like a light weight thread in java and each actor has an inbox. , я думаю, что наличие почтового ящика для каждого актера похоже на выделение очереди для каждого пользователя. Оба будут в памяти модели. Как и очередь, папка «Входящие» для хранения сообщения также потребляет память. Вы видите здесь какую-то принципиальную разницу? Поэтому я считаю, что предложенная вами модель более или менее аналогична модели очереди сообщений с некоторыми отличиями в реализации. - person user3198603; 11.02.2017
comment
2. В случае модели очереди при доставке сообщения от отправителя система будет искать местоположение очереди получателя (из некоторых метаданных, созданных, когда пользователь подключается к сети) и доставляет его. Я уверен, что будет готовый фреймворк, который может обеспечить это. Я считаю, что вы говорите, что Akka, работающая в федеративном режиме, предоставляет аналогичную функцию. - person user3198603; 11.02.2017
comment
вы правы в обоих отношениях. почтовый ящик похож на оболочку, реализованную поверх ConcurrentLinkedQueue. и почтовый ящик может быть как для каждого актера, так и для общего доступа между актерами, что похоже на федерацию. - person Raveesh Sharma; 13.02.2017
comment
@Raveesh, вы говорите о хранении сообщений на сервере с использованием некоторой структуры данных, такой как ConcurrentLinkedQueue. Не кажется ли вам, что это плохая идея, потому что, если сервер выйдет из строя еще до того, как попытается доставить сообщение один раз, сообщение будет потеряно? Разве это не должна быть распределенная очередь вне сервера? - person Pratz; 25.11.2017