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

Когда вы входите на такой веб-сайт, как Facebook, куда в первую очередь устремляются ваши глаза? Топ вашей ленты? Баннер историй?

Возможно нет.

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

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

Если вы следили, это четвертая часть вводной серии по WebSockets. Каждая часть основывается на предыдущей, поэтому, если вы присоединяетесь к нам сейчас, я настоятельно рекомендую прочитать первые три части.

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

Развернуть в облаке

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

git fetch 
git checkout part-four 
npm run deploy

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

  • Лямбда-функция для поиска и публикации для определенных пользовательских подключений.
  • Очереди недоставленных сообщений (DLQ) для ошибок доставки/обработки событий
  • CloudWatch alarm для уведомления о сбое
  • Обновления нашей спецификации асинхронного API, определяющие новые входные и выходные события.

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

Найдите соединение пользователя

С помощью WebSocket, который мы создали в предыдущем посте, мы добавили лямбда-авторизатор в конечную точку $connect, которая сохранила userId подключающегося пользователя в базе данных.

Мы можем использовать эту информацию, чтобы найти открытые соединения для пользователя, чтобы отправлять им push-уведомления. Когда приходит новое событие Send User Push Notification EventBridge, мы можем запросить в базе данных все соединения для этого userId и отправить им сообщение.

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

{ 
  "pk": "<connectionId>", 
  "sk": "connection#", 
  "GSI1PK": "<userId>", 
  "GSI1SK": "user#", 
  "ipAddress": "<connecting ip address>", 
  "connectedAt": "<epoch connected at time>", 
  "ttl": "<time to live before connection is removed>" 
}

Глобальный вторичный индекс (GSI) позволяет нам запрашивать все записи в базе данных, которые имеют GSI1PK из userId. Это позволяет нам получить подключения к WebSocket, чтобы мы могли передать сообщение из события. См. лямбда-функцию.

Типы пользовательских уведомлений

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

Действенные уведомления

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

Push-уведомления, которые мы отправляем пользователям через наш WebSocket, содержат две части данных:

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

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

{ 
  "message": "The XYZ report has finished processing and is ready for review.", 
"callback": "https://www.gopherholesunlimited.com/jobs/736ajdff7/results" 
}

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

Уведомления об обратной связи

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

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

{ 
  "message": "Calculating average time between status changes..." 
}

Обратите внимание, что в этом уведомлении нет callback. Он строго информативен и не предусматривает никаких действий.

Назначение уведомления об отзыве — убедить конечного пользователя, что система что-то делает.

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

Обработка ошибок с помощью push-уведомлений

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

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

  1. Ошибка доставки события
  2. Ошибка обработки события

Ошибки доставки событий возникают, когда EventBridge не удается поместить событие в нашу очередь SQS или когда событие не может быть передано из SQS в лямбду-выражитель. Это может быть вызвано сбоем системы или неправильной конфигурацией. Независимо от того, какова основная причина, мы отправляем ее в DLQ для мониторинга и сортировки. Как только он окажется в DLQ, мы сможем выяснить, что происходит в системе, и попытаться исправить это.

Чтобы отправить сообщение о сбое доставки события в DLQ, мы обновляем наше правило EventBridge, чтобы оно нацеливалось на очередь в случае сбоя доставки.

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

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

Как узнать, когда есть ошибки

Отправка ошибок в DLQ — это одно, но как узнать, что что-то требует вашего внимания?

Чтобы знать, когда что-то не так, мы внедрили сигнал тревоги CloudWatch, который отслеживает DLQ. Всякий раз, когда в DLQ содержится более 1 элемента, активируется тема SNS и уведомляет заинтересованные стороны о системном сбое.

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

Что делать с ошибками в DLQ

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

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

Заключение

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

С WebSocket API добавление этих типов уведомлений очень просто. Теперь вам решать, как реализовать это в своем приложении. Вы можете включить их в свои Пошаговые функции AWS как часть рабочего процесса или сделать их пунктом назначения после завершения асинхронной лямбда-функции.

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

Я рекомендую вам развернуть этот стек в своей учетной записи AWS и поэкспериментировать с ним. На данный момент мы создали готовый к эксплуатации микросервис WebSocket. Так что берите его и начинайте использовать!

Удачного кодирования!