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

С Hasura вы можете МОМЕНТАЛЬНО создавать приложения для чата прямо из своих моделей данных без необходимости использования какой-либо другой инфраструктуры с использованием потоковых подписок. Преимущества использования стриминговых подписок на Hasura:

  • Никаких дополнительных требований к инфраструктуре, кроме Hasura и Postgres. Работает с любым клиентом GraphQL.
  • Проверка подлинности на уровне модели, которая позволяет отправлять правильные события нужным клиентам на основе сеанса и свойств данных.
  • Масштабирование считывает так же, как вы масштабируете чтение Postgres, а масштабирование записи — так, как вы масштабируете прием данных Postgres.
  • Простое масштабирование Hasura для горизонтального масштабирования клиентов веб-сокетов. Проверено для обработки 1 млн одновременных клиентов GraphQL, передающих потоки из 10 000 каналов с 1 млн событий, получаемых каждую минуту, на одном экземпляре Postgres RDS.

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

Создание моделей данных для приложения чата

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

Для этого мы создаем две модели данных:

  • messages: Здесь хранятся сообщения чата.

  • channel_users: Здесь хранится сопоставление каналов и пользователей. Это то, что мы будем использовать для моделирования разрешений нашего приложения.

Мы также можем создать связь между messages и channel_users, используя channel_id в качестве связующего поля. Мы можем сделать это либо с помощью внешних ключей, либо вручную — мы будем использовать ручные отношения ниже и назовем это отношение recipients.

Добавьте монотонный идентификатор в модель сообщений

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

Добиться монотонных идентификаторов в Postgres сложно из-за сложного контроля параллелизма. Даже в режиме сериализуемой транзакции нет необходимости, чтобы две транзакции, которые фиксируются в порядке t1, а затем t2, обязательно имели идентификаторы, которые также следуют одному и тому же порядку — сериализуемость означает только наличие некоторого последовательного порядка, не обязательно порядка фиксации. . Следовательно, нам нужно сделать идентификатор, который точно монотонен с отметкой времени фиксации. Мы можем сделать это в Postgres через рекомендательные блокировки. Вот триггер, который вы можете добавить (через Run SQL в консоли) в свою таблицу, чтобы он генерировал монотонные идентификаторы:

CREATE sequence serial_chats;

CREATE FUNCTION add_monotonic_id()
RETURNS trigger AS $$
DECLARE
nextval bigint;
BEGIN
  PERFORM pg_advisory_xact_lock(1);
  select nextval('serial_chats') into nextval;
  NEW.id := nextval;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER add_monotonic_id_trigger
BEFORE INSERT ON messages
FOR EACH ROW
  EXECUTE FUNCTION add_monotonic_id();

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

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

Нам необходимо обеспечить, чтобы пользователи могли читать и писать только в те каналы, участниками которых они являются. Мы можем добавить разрешение Hasura к модели channels, создав роль, а затем добавив фильтр строк, который проверяет, что отношение recipients должно иметь x-hasura-user-id из входящего запроса.

Пробуем стриминговые подписки

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

В одной вкладке откройте потоковую подписку со следующим запросом и ролью user и немного x-hasura-user-id:

subscription {
  messages_stream(cursor: {initial_value: {id: 1} }, batch_size: 5) {
    id
    message
    author_id
  }
}

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

Ознакомьтесь с исходным кодом вышеуказанной модели данных здесь.

Попробуйте полный пример приложения с демо-версией

Мы создали приложение для чата в реальном времени, использующее функцию потоковой подписки в Hasura.

Это выглядит примерно так:

Что делает приложение, так это извлекает последние 10 сообщений из таблицы сообщений, а затем передает все новые сообщения с помощью недавно добавленного Streaming API.

Мутация GraphQL продолжает работать в фоновом режиме для заполнения онлайн-пользователей. В пользовательском интерфейсе отображается индикатор ввода, для которого выполняется мутация GraphQL и выполняется подписка для получения последнего ввода пользователя.

Модель данных для этого использует таблицы user и message.

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

Первоначально опубликовано на https://hasura.io 4 октября 2022 г.