Это третья часть серии статей о разработке приложений чата в реальном времени на базе Socket.io и AWS Fargate на AWS.
- Создание чат-приложения Socket.io и его развертывание с помощью AWS Fargate (В первой части я просто упаковал чат-приложение с открытым исходным кодом из socket.io и развернул его на AWS как единое развертывание контейнера)
- Масштабирование чат-приложения в реальном времени на 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.
Выбранный мной рабочий процесс для автоматизации развертывания этого приложения выглядит следующим образом:
- Отправьте мой код в репозиторий git. Все, что мне нужно сделать, это
git commit
, а затемgit push
, а все остальное происходит автоматически. Мне не нужно прикасаться к AWS API или консоли AWS вручную. - Разверните базовую инфраструктуру. Автоматически создавайте правила VPC и сети с помощью шаблона cluster.yml.
- Параллельно создайте контейнер докеров с помощью AWS CodeBuild и создайте основные ресурсы приложения, такие как ElastiCache и таблицы DynamoDB. Эти шаги могут выполняться параллельно, потому что они не зависят друг от друга.
- Теперь, когда все ресурсы приложения готовы к работе, мы можем запустить контейнер в AWS Fargate.
Чтобы организовать эту сборку, я выбрал AWS CodePipeline. Я определяю этапы сборки и конвейера с помощью CloudFormation. Когда я развертываю этот единственный родительский стек CloudFormation, он создает конвейер в соответствии с моей спецификацией:
Конвейер работает быстро, занимает всего около 11 минут от начала до конца с новой средой, в которой еще ничего не развернуто. После первого запуска он становится еще быстрее, потому что базовый шаблон CloudFormation и шаблон ресурсов приложения чата уже развернут, поэтому единственное, что нужно обновить, - это CodeBuild и окончательное развертывание.
Заключение
Это приложение для чата постепенно становится более полнофункциональным, и самое лучшее, что он имеет 100% открытый исходный код, так что вы можете учиться на коде и использовать его методы самостоятельно. Вы можете найти исходный код на Github:
Репозиторий также включает инструкции о том, как самостоятельно развернуть приложение в своей учетной записи AWS, если вы хотите поэкспериментировать с ним.
Или вы можете просто проверить работающую копию текущего состояния приложения здесь:
В следующей статье я узнаю, как настроить локальную среду разработки для ваших приложений Docker: