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

Вы можете найти Доблесть здесь

Так почему же определение наличия непрочитанного сообщения на канале — и, соответственно, на всем сообществе — такое исключение? Почему этот маленький кусочек головоломки стал инкапсулировать так много времени, затраченного мной на разработку?

Пользователи против подключений

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

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

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

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

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

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

О узлы

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

На изображении выше легко синхронизировать состояние между устройствами на узле B. Узел просто запускает событие в группу SignalR (SignalR — это система для связи в реальном времени между клиентами и серверами через Интернет) для пользователя, который затем должны получить любые авторизованные устройства для этого пользователя.

Однако узел А понятия не имеет, что происходит. Для этого есть несколько решений, каждое из которых имеет свои преимущества и недостатки. Во-первых, узлы могут отправлять HTTP-запросы, чтобы уведомлять друг друга об этих событиях. Это очень прямой способ отправки сообщения — он не затрагивает никакие узлы, кроме цели. Однако есть проблема.

Ну, если честно, их несколько. Использование HTTP-клиента для отправки запросов каждый раз, когда происходит синхронизированное событие, может очень быстро снизить производительность. Не говоря уже о том, что для каждого существующего клиента должен быть один запрос, что приводит к O (n).

Настоящая проблема заключается в том, что мы сделали предположение, что узлы знают, где существуют другие узлы и какие клиенты к ним подключены. Откуда узел B вообще знает, что нужно отправить запрос к A?

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

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

Введите Redis

Redis почти волшебен в мире разработки программного обеспечения. Если вы не знали, в Redis встроена мощная система Pub/Sub, которую можно использовать вместе с мощным кэшированием в памяти. Если мы решим хранить состояния подключения в Redis, мы можем также использовать его для отправки событий на определенные узлы. Круто, да?

Несмотря на то, что нам по-прежнему приходится дважды нырять и дважды нажимать на Redis — один раз, чтобы запросить узлы, частью которых является пользователь, и второй раз, чтобы запустить события для перехода к этим узлам, — нам удалось упростить архитектуру и повысить ее эффективность. . На самом деле мы могли бы отправить событие «пользователь» всем узлам и заставить узлы подписываться на всех пользователей, которые активно подключены, но есть недостатки. Прежде всего, для этого подхода потребуется много каналов, по одному на каждого активного пользователя. И хотя Redis Pub/Sub работает быстро, создание потенциально миллионов каналов быстро вызовет проблемы, связанные как со скоростью, так и с архитектурными ограничениями.

Шаг вперед

Он не идеален, и мы, вероятно, его оптимизируем. Но это хорошее начало в направлении системы распределенных узлов для приложения реального времени, и оно сделает все возможное, чтобы поддерживать доблесть.