Это третья часть серии статей о разработке приложений чата в реальном времени на базе Socket.io и AWS Fargate на AWS.

  1. Создание чат-приложения Socket.io и его развертывание с помощью AWS Fargate (В первой части я просто упаковал чат-приложение с открытым исходным кодом из socket.io и развернул его на AWS как единое развертывание контейнера)
  2. Масштабирование чат-приложения в реальном времени на AWS с помощью Socket.io, Redis и AWS Fargate (во второй части я сделал приложение горизонтально масштабируемым за балансировщиком нагрузки приложения, поставил за ним ElastiCache Redis для межпроцессного взаимодействия и настроил триггеры автомасштабирования)

В конце второй части архитектура приложения выглядела так:

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

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

  • Система учетных записей пользователей, позволяющая пользователям входить в систему и сохранять отправляемые сообщения.
  • Чуть менее привлекательный интерфейс
  • Правильный конвейер CI / CD для более быстрой и простой итерации в приложении

Улучшение интерфейса с помощью Vue.js

Первое, что я хотел, - это освежить интерфейс на стороне клиента. В исходном демонстрационном приложении с открытым исходным кодом, на котором я построил, просто использовался некоторый базовый jQuery для управления HTML, но это не очень масштабируемое с точки зрения добавления функций. Я провел небольшое исследование популярных в настоящее время фреймворков HTML + JavaScript и решил выбрать Vue.js для создания своего внешнего интерфейса. Я выбрал этот фреймворк в первую очередь потому, что он казался очень доступным, с простыми для понимания методами, и он кажется намного более стабильным по сравнению с постоянным оттоком экосистемы React.

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

Vue.js использует три вещи:

  • Произвольный объект JavaScript, хранящий состояние
  • HTML-шаблон
  • Расположение DOM в вашем HTML-документе

Всякий раз, когда объект JavaScript обновляется, Vue будет оценивать шаблон, чтобы выяснить, нужно ли изменить DOM, и если да, то внесет изменения в DOM, чтобы отразить обновления. Поэтому мне просто нужно написать что-то вроде этого:

Затем в свой HTML я добавляю такой компонент набора текста:

Vue.js автоматически сгенерирует HTML-код для компонента <typing>, поместит его в указанное мной место в документе и автоматически обновит HTML-код по мере изменения структуры данных всякий раз, когда я вызываю функцию addTyper().

Это позволяет мне легко преобразовать простой дизайн HTML и CSS в динамическое приложение. Мне просто нужен тонкий слой кода, чтобы получить данные с сервера и поместить их в локальный объект JavaScript, а затем Vue.js берет на себя ответственность за обновление HTML, как я хотел.

Я очень быстро смог превратить этот базовый фрагмент HTML и CSS в настоящее приложение, подключенное к моему внутреннему серверу socket.io. Вы можете проверить полный код фронтенд-приложения или просто посмотреть снимок экрана ниже:

Добавление сохраняемости сообщений с DynamoDB

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

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

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

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

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

Еще одно приятное преимущество использования AWS DynamoDB заключается в том, что авторизовать мой код для взаимодействия с базой данных чрезвычайно просто. Я просто даю своей задаче, выполняемой в AWS Fargate, роль IAM, которая разрешает ей взаимодействовать с таблицей DynamoDB, а AWS SDK выполняет аутентификацию.

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

Моя диаграмма архитектуры теперь выглядит так:

  • Автоматическое масштабирование контейнерных задач в Fargate
  • Amazon ElastiCache используется в качестве шины сообщений между задачами Fargate
  • DynamoDB используется как надежное хранилище для учетных записей пользователей и сообщений.

Недостаток: автоматическое развертывание

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

  • cluster.yml (Создание VPC, основных сетевых правил, групп безопасности, балансировщика нагрузки и т. Д.)
  • resources.yml (Создание ElastiCache, DynamoDB и роли IAM для приложения)
  • chat-service.yml (Запустить задачу в кластере Fargate, подключенном к балансировщику нагрузки)

В дополнение к развертыванию этих трех шаблонов мне также нужно создать свой контейнер докеров с помощью Dockerfile и отправить его в реестр Amazon Elastic Container Registry. Сборка - особенно интересная часть, потому что нам нужно где-то делать сборку на компьютере. Одна из моих целей с этой архитектурой - не запускать экземпляры самостоятельно. Поэтому я буду использовать AWS CodeBuild для сборки. CodeBuild запустит докер внутри контейнера докеров, который выполняет мою сборку и отправляет построенный контейнер докеров в Amazon ECR.

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

  1. Отправьте мой код в репозиторий git. Все, что мне нужно сделать, это git commit, а затем git push, а все остальное происходит автоматически. Мне не нужно прикасаться к AWS API или консоли AWS вручную.
  2. Разверните базовую инфраструктуру. Автоматически создавайте правила VPC и сети с помощью шаблона cluster.yml.
  3. Параллельно создайте контейнер докеров с помощью AWS CodeBuild и создайте основные ресурсы приложения, такие как ElastiCache и таблицы DynamoDB. Эти шаги могут выполняться параллельно, потому что они не зависят друг от друга.
  4. Теперь, когда все ресурсы приложения готовы к работе, мы можем запустить контейнер в AWS Fargate.

Чтобы организовать эту сборку, я выбрал AWS CodePipeline. Я определяю этапы сборки и конвейера с помощью CloudFormation. Когда я развертываю этот единственный родительский стек CloudFormation, он создает конвейер в соответствии с моей спецификацией:

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

Заключение

Это приложение для чата постепенно становится более полнофункциональным, и самое лучшее, что он имеет 100% открытый исходный код, так что вы можете учиться на коде и использовать его методы самостоятельно. Вы можете найти исходный код на Github:



Репозиторий также включает инструкции о том, как самостоятельно развернуть приложение в своей учетной записи AWS, если вы хотите поэкспериментировать с ним.

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



В следующей статье я узнаю, как настроить локальную среду разработки для ваших приложений Docker: