Некоторые подходы к архитектуре приложений в Amazon
Tags: architecture, amazon, aws, soa
Published: 2024-05-15 10:21:38

Amazon использует [SOA](https://aws.amazon.com/what-is/service-oriented-architecture/?nc1=h_ls) (Service-oriented Architecture) подход к архитектуре приложений. Бэкенд Amazon состоит из огромного числа сервисов/компонент, каждая из который реализует ту или иную бизнес функцию. Эти сервисы разрабатываются и поддерживаются отдельными [Two-Pizza Teams](https://docs.aws.amazon.com/whitepapers/latest/introduction-devops-aws/two-pizza-teams.html). Каждая компонента живет в своем отдельном репозитории и имеет свой отдельный pipeline для деплоймента в продакшн. Каждый такой деплоймент пайплайн имеет несколько этапов:

- Компиляция и прогон unit тестов
- деплоймент на тестовый сервер и прогон интеграционных и performance тестов
- Деплоймент на один из продакш серверов, автоматический мониторинг ключевых метрик с автоматическим ролбэком, в случае обнаружения проблем
- Волны деплоймента. После мониторинг на одном сервере, деплоймент на 5, 10, 100, 1000, 10000 серверов и т.д.
- Продакшен сервера находятся в auto-scaling группах, которые позволяют добавлять или убавлять сервера автоматически в зависимости от нагрузки.

В Amazon используется CI/CD и каждый commit сразу идет в продакшен. Время между коммитом и деплойментом в прод - несколько часов.

Скорость разработки, а также минимализация багов и скорость решения продакшен проблем очень важны в Amazon. Для этого Amazon широко использует, уже разработанные внутри компании, инфраструктурные компоненты, в том числе и доступные всем желающим - AWS. Но также есть огромное число чисто внутренних решений, которые пока не доступны в виде managed сервиса на AWS.
Эти кирпичики можно использовать, как конструктор Lego для быстрого создания масштабируемых компонент.
Вот лишь небольшая часть use case'ов, которые часто используются при разработке.

### Мониторинг


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwd7eodsm97xxkr01r1u.png)

Все компоненты производят метрики, которые эмитятся во внутренний мониторинг сервис. Он позволяет строить графики, производить анализ, делать дашборды, создавать алармы, автоматически создавать таски/тикеты, если какие-то метрики ведут себя не так как ожидается и многое другое. Это очень помогает делать более безопасный релиз новой функциональности, а также мониторить компоненты в продакшене и быстро находить причину проблемы. Метрики могут быть число техническими, вроде метрик производительности, так и чисто бизнес метрики.
Кроме внутреннего серсива для мониторинга, также широко используется доступный всем [AWS Cloudwatch](https://aws.amazon.com/cloudwatch/):

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpm8tnq3vejrso9wf1vp.png)

Иногда, таски создаются напрямую из кода компоненты. Если вывалилось какое-то критическое исключение, то прямо из catch-stement вызывается API, которое создает новую таску или дополняет ее новым стектрейсом, если такая же таска уже была создана ранее (чтобы не создать тысячи тасков с одной и той же проблемой):

```java
try {
   ....
} catch (SomeCriticalException e) {
    taskAPI.createTask(e);
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suh5y04w4i584hxs4re3.png)

Иногда, для более детального анализа, репортится большое число бизнес данных. Эти данные могут использоваться для анализа или для работы бизнес логики других компонент. Такие данные обычно отправляются в очередь типа [AWS SQS](https://aws.amazon.com/sqs/), из которой периодически данные считываются и дампятся в SQL таблицу (Data Warehouse). Для визуализации могут использоваться тулы типа [AWS Quicksight](https://aws.amazon.com/pm/quicksight/):

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/os1i2yhkp4cfndhum1vg.png)

В качестве Data Warehouse обычно используется [Amazon Redshift](https://aws.amazon.com/redshift). Вместо кастомный джобы, которая читает из SQS еще используют стрим типа [Kinesis](https://aws.amazon.com/kinesis/):


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ch0u3h3k9kcdn7m059h3.png)



Иногда используется Elasticsearch для анализа метрик и логов. Возможная архитектура в AWS: логи попадают в stream (Например [AWS Kinesis](https://aws.amazon.com/pm/kinesis) или Kafka), оттуда в Elasticsearch и анализируется в Kibana:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62te4fsmcl0fcruvs6oy.png)
Elasticsearch также позволяет сетапить алармы и по ним создавать тикеты.

### Межкомпонентное взаимодействие
Чаще всего, для вызова другой компоненты в Amazon используется RPC. Для этого там есть свой внутренний framework/library:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0st6mmr3i7qdi5wo0gf6.png)

Каждый вызов обернут в кучу слоев для достижения Resilience: 

- **Retry Strategy**. Если вызов компоненты закончился ошибкой, то можно попробовать вызвать сервис еще раз через некоторое время, а не возвращать ошибку в качестве результата. Но делать это нужно только для временных ошибок (transient exceptions), вроде ошибки сети и т.д. Чтобы не получить Retry Storm. Также можно увеличивать время между попытками ([Exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)), чтобы не перегрузить сервер если на нем большая нагрузка.
- **Circuit Breaker**. Если большой процент вызовов компоненты заканчивается ошибкой, то можно перестать его вызывать до тех пор, пока сервис не восстановится.
- **Cache**. Можно использовать локальный кэш (Например, [Google Guava Cache](https://www.baeldung.com/guava-cache)) или внешний кэш ([AWS ElastiCache](https://aws.amazon.com/pm/elasticache)), для того, чтобы временно кэшировать результат вызова, чтобы не перегружать внешний сервис вызовами с одними и теми же параметрами.
- **Throttling/Rate Limiting**. Если число вызовов в единицу времени превышает определенный порог, то можно бросать ошибку, чтобы не перегружать сервис. В таком случае, на стороне клиента будет видно большое число throttling exceptions и нужно увеличивать квоту/добавлять сервера, чтобы поддержать увеличенный трафик. А также Retry Strategy или Cirsuit Breaker смогут временно уменьшить нагрузку на сервис.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0l6va4mbet1by2hmstig.png)

Если нам не нужно процессить все в режиме реального времени, а нужна асинхронная обработка данных, то используются очередь. В Amazon это чаще всего [AWS SQS](https://aws.amazon.com/sqs/). Одна компонента пишет в очередь, а другая читает:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/go4be81ni7mreq6qnr8q.png)
Иногда, нам нужно поддержать множество consumers одних и тех же данных. Тогда можно использовать [AWS SNS](https://aws.amazon.com/sns/). Это **pub-sub** сервис. Одна компонента (**publisher**) будет отправлять нотификации в топик в AWS SNS, а другие компоненты (**consumers**) могут подписаться на этот топик и слушать его.
Для асинхронной обработки, можно подписать множество SQS очередей для каждого клиента. Когда каждый клиент сможет получать теже данные, что и другие клиенты:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7adpzkdvjh555lqbkcc.png)

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fz3ntxxi5z9osqs4dtor.png)
Для быстрого доступа, данные помещаются в NoSQL базу ([AWS Dynamo DB](https://aws.amazon.com/dynamodb/)).
Исходные данные, обычно, находятся в Data Warehouse (например, [Amazon Redshift](https://aws.amazon.com/redshift)).

Это далеко не все паттерны, которые часто используются в создании компонент в Amazon, но одни из самых расспространенных.