Блог Amazon Web Services

Выбор сервисов сообщений для бессерверных приложений

Оригинал статьи: ссылка (James Beswick,  Senior Developer Advocate, AWS Serverless Team) часть 1 (текущая), часть 2, часть 3

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

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

В этой статье я сравню функционал основных сервисов сообщений, предлагаемых AWS, и покажу, как вы можете использовать их в бессерверных архитектурах. Также я покажу, как можно развернуть и использовать эти интеграции с помощью AWS Serverless Application Model (AWS SAM).

Примеры кода, используемые в данной статье, вы сможете найти и загрузить из репозитория GitHub. Файл README.md содержит инструкции по запуску и развёртыванию каждого из примеров кода.

Обзор

Три наиболее полезные паттерна обмена сообщениями, используемые разработчиками бессерверных приложений, – это очереди, модель «издатель – подписчик» (Pub/Sub) и шина событий. В AWS, эти паттерны реализованы в соответствующих сервисах: Amazon SQSAmazon SNS и Amazon EventBridge. Все эти сервисы являются полностью управляемыми и высоко-доступными, без необходимости управлять базовой инфраструктурой. Также все три могут интегрироваться с AWS Lambda, позволяя отправлять сообщения посредством AWS SDK и вызывать функции в качестве обработчиков сообщений. Каждый из этих сервисов играет важную роль в бессерверных архитектурах.

SNS позволяет надёжно обмениваться сообщениями между частями вашей инфраструктуры. Этот сервис использует надёжный механизм повторения отправки сообщений, когда конечные сервисы, куда отправляется сообщение, недоступны. В случае исчерпания максимального количества повторений отправки, заданного в политике, сервис может отправить эти сообщения в dead-letter queue для дальнейшей обработки. SNS использует топики для логического разделения сообщений на каналы отправки, и Lambda функция взаимодействует с топиками для обработки сообщений.

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

EventBridge – это бессерверная шина событий, упрощающая перенаправление сообщений между сервисами AWS, SaaS-провайдерами и вашими приложениями. EventBridge даёт возможность логически разделять потоки данных на основе шин событий, и позволяет задавать правила пересылки. Вы можете фильтровать и преобразовывать входящие сообщения на уровне сервиса, без дополнительного кода, а также доставлять события к различным целевым адресатам, включающим Lambda-функции.

Интеграция SQS очередей с помощью AWS SAM

Первый пример демонстрирует шаблон AWS SAM, определяющий бессерверное приложение с двумя Lambda-функциями и очередью SQS:

Вы определяете очередь SQS в шаблоне AWS SAM ресурсом AWS::SQS::Queue:

  MySqsQueue:
    Type: AWS::SQS::Queue

Для публикации сообщений в очередь функция-отправитель должна иметь права доступа на отправку сообщений. Используя шаблон AWS SAM для политики безопасности, вы можете применить политику, которая позволяет отправку сообщений в определенную очередь:

      Policies:
        - SQSSendMessagePolicy:
            QueueName: !GetAtt MySqsQueue.QueueName

Шаблон AWS SAM передаёт имя очереди в Lambda-функцию через переменную окружения. Функция использует метод sendMessage класса AWS.SQS для публикации сообщения:

const AWS = require('aws-sdk')
AWS.config.region = process.env.AWS_REGION 
const sqs = new AWS.SQS({apiVersion: '2012-11-05'})

// The Lambda handler
exports.handler = async (event) => {
  // Params object for SQS
  const params = {
    MessageBody: `Message at ${Date()}`,
    QueueUrl: process.env.SQSqueueName
  }
  
  // Send to SQS
  const result = await sqs.sendMessage(params).promise()
  console.log(result)
}

Когда очередь SQS получает сообщение, она отправляет его в Lambda-функцию «получателя». Для настройки такой интеграции в AWS SAM, функции-получателю назначаются права доступа SQSPollerPolicy. Источник событий для функции настроен на получение сообщений из очереди порциями по 10 штук:

  QueueConsumerFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: code/
      Handler: consumer.handler
      Runtime: nodejs12.x
      Timeout: 3
      MemorySize: 128
      Policies:  
        - SQSPollerPolicy:
            QueueName: !GetAtt MySqsQueue.QueueName
      Events:
        MySQSEvent:
          Type: SQS
          Properties:
            Queue: !GetAtt MySqsQueue.Arn
            BatchSize: 10

Сообщение из SQS передается на вход функции-получателя в качестве данных для обработки. Эти данные представляют собой массив сообщений размером до максимального настроенного «пакета» сообщений. Он содержит атрибут body со значением поля MessageBody, которое было установлено функцией-отправителем. Вы можете увидеть значения этих атрибутов в логе CloudWatch для функции:

Интеграция топиков SNS с помощью AWS SAM

Второй пример демонстрирует шаблон AWS SAM, определяющий бессерверное приложение с тремя Lambda-функциями и топиком SNS:

Вы можете определить SNS-топик и подписанные на него Lambda-функции в ресурсе AWS::SNS::Topic:

  MySnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription:
        - Protocol: lambda
          Endpoint: !GetAtt TopicConsumerFunction1.Arn    
        - Protocol: lambda
          Endpoint: !GetAtt TopicConsumerFunction2.Arn

Для обеспечения необходимых прав для сервиса SNS нужно определить AWS::Lambda::Permission для каждой из подписанных на топик функций:

  TopicConsumerFunction1Permission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !Ref TopicConsumerFunction1
      Principal: sns.amazonaws.com

Шаблон политики безопасности SNSPublishMessagePolicy предоставляет права на отправку сообщений в топик для функции-отправителя. В Lambda-функции, метод publish в классе AWS.SNS управляет публикацией сообщения:

const AWS = require('aws-sdk')
AWS.config.region = process.env.AWS_REGION 
const sns = new AWS.SNS({apiVersion: '2012-11-05'})

// The Lambda handler
exports.handler = async (event) => {
  // Params object for SNS
  const params = {
    Message: `Message at ${Date()}`,
    Subject: 'New message from publisher',
    TopicArn: process.env.SNStopic
  }
  
  // Send to SQS
  const result = await sns.publish(params).promise()
  console.log(result)
}

Сообщение из SNS передается на вход функции-получателя в качестве данных для обработки. Это массив сообщений, содержащих атрибуты subject и message, полученные из функции-отправителя. Вы можете увидеть значения этих атрибутов в логе функции в CloudWatch:

Различия в конфигурации между SQS и SNS

SQS-очереди и SNS-топики имеют разную функциональность, но оба сервиса могут публиковать сообщения в результирующие Lambda-функции.

Сообщения в SQS хранятся в очереди до 14 дней или пока не будут успешно обработаны подписчиком.

SNS не сохраняет сообщения, если отсутствуют подписчики на топик. В такой ситуации пришедшее сообщение будет отброшено.

SNS-топики могут рассылать сообщения многочисленным целевым адресатам, это называется fan-out. Вы можете использовать этот подход для распараллеливания обработки сообщений по нескольким Lambda-функциям или отсылать сообщения в несколько целевых сред (например, тестовую среду или среду разработки). SNS-топик может иметь до 12,5 миллионов подписчиков, обеспечивая высоко-масштабируемые возможности по рассылке сообщений. Целевыми адресатами могут быть конечные точки HTTP/S, текстовые сообщения SMS, мобильные уведомления SNS, емейлы, SQS и Lambda-функции.

В шаблонах AWS SAM вы можете получить свойства очередей и топиков, такие как ARN и имена, используя следующие встроенные функции:

Amazon SQS Amazon SNS
Тип канала Очередь Топик
Получение ARN !GetAtt MySqsQueue.Arn !Ref MySnsTopic
Получение имени !GetAtt MySqsQueue.QueueName !GetAtt MySnsTopic.TopicName

Интеграция EventBridge с помощью AWS SAM

Третий пример демонстрирует шаблон AWS SAM, определяющий бессерверное приложение с двумя Lambda-функциями и правилом EventBridge:

В каждом аккаунте AWS уже существует шина сообщений «по умолчанию». Для определения правила фильтрации сообщений в шине событий используйте ресурс AWS::Events::Rule:

  EventRule: 
    Type: AWS::Events::Rule
    Properties: 
      Description: "EventRule"
      EventPattern: 
        source: 
          - "demo.event"
        detail: 
          state: 
            - "new"
      State: "ENABLED"
      Targets: 
        - Arn: !GetAtt EventConsumerFunction.Arn
          Id: "ConsumerTarget"

Правило описывает паттерн сообщения посредством определения совпадающих атрибутов JSON. Сообщение, совпавшее с паттерном в правиле, будет перенаправлено в список целевых адресатов. Для работы сервиса EventBridge нужно предоставить права для вызова Lambda-функций из списка целевых адресатов:

  PermissionForEventsToInvokeLambda: 
    Type: AWS::Lambda::Permission
    Properties: 
      FunctionName: 
        Ref: "EventConsumerFunction"
      Action: "lambda:InvokeFunction"
      Principal: "events.amazonaws.com"
      SourceArn: !GetAtt EventRule.Arn

Шаблон AWS SAM использует права доступа IAM для предоставления привилегий функции-отправителю на отправку сообщений в шину событий:

  EventPublisherFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: code/
      Handler: publisher.handler
      Timeout: 3
      Runtime: nodejs12.x
      Policies:
        - Statement:
          - Effect: Allow
            Resource: '*'
            Action:
              - events:PutEvents  

Функция-отправитель использует метод putEvents класса AWS.EventBridge, который возвращает результат после гарантированного сохранения сообщения в EventBridge:

const AWS = require('aws-sdk')
AWS.config.update({region: 'us-east-1'})
const eventbridge = new AWS.EventBridge()

exports.handler = async (event) => {
  const params = {
    Entries: [ 
      {
        Detail: JSON.stringify({
          "message": "Hello from publisher",
          "state": "new"
        }),
        DetailType: 'Message',
        EventBusName: 'default',
        Source: 'demo.event',
        Time: new Date 
      }
    ]
  }
  const result = await eventbridge.putEvents(params).promise()
  console.log(result)
}

Сообщение из EventBridge передаётся на вход функции-получателя в качестве данных для обработки. Это массив сообщений, содержащих атрибуты subject и message, полученные из функции-отправителя. Вы можете увидеть значения этих атрибутов в логе CloudWatch для функции:

Сравнение SNS с EventBridge

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

EventBridge предлагает две новых возможности, которые недоступны в SNS.

Первая – это интеграция с SaaS-провайдерами, которая позволяет вам авторизовать SaaS-провайдеров для отправки сообщений из их шины сообщений EventBridge в партнёрские шины сообщений в вашем аккаунте AWS. Таким образом, больше нет необходимости настраивать webhook или механизмы опросов на изменения (polling). Это обеспечивает высоко-масштабируемый способ загружать SaaS-сообщения напрямую в ваш аккаунт AWS.

Вторая – это Schema Registry, позволяющая обнаруживать и управлять схемами OpenAPI для событий. EventBridge может определять схему на основе прошедших через него событий, используя механизм обнаружения схем (schema discovery). Этот механизм может быть использован для генерации привязок кода (code bindings) напрямую в ваше IDE для типобезопасных языков, таких как Python, Java и TypeScript. Это, в свою очередь, помогает ускорять процесс разработки и автоматизировать генерацию классов и кода на основе обрабатываемых событий.

Таблица сравнивает основной функционал обоих сервисов:

Amazon SNS Amazon EventBridge
Количество целевых адресатов 10 миллионов (soft) 5 адресатов на правило
SLA доступности 99,9% 99,99%
Сервисные лимиты

100 тысяч топиков.

12,5 миллионов подписок на топик.

100 шин сообщений.

300 правил на шину сообщений.

Пропускная способность отправки сообщений Зависит от Региона. Регулируемая квота. Зависит от Региона. Регулируемая квота.
Преобразование входных данных Нет Да – смотрите детали.
Фильтрация сообщений Да – смотрите детали. Да, включая совпадение по IP-адресам – смотрите детали.
Максимальный размер сообщения 256 KB 256 KB
Оплата за 64 KB за 64 KB
Формат Raw или JSON JSON
Получение сообщений от AWS CloudTrail Нет Да
Адресаты HTTP(S), SMS, SNS Mobile Push, Email/Email-JSON, SQS, Lambda-функции. 17 адресатов, включая AWS LambdaAmazon SQSAmazon SNSAWS Step FunctionsAmazon Kinesis Data StreamsAmazon Kinesis Data Firehoseточки доступа REST API в API Gateway и кластеры Amazon Redshift.
Интеграция с SaaS-провайдерами Нет Да – смотрите список интеграций.
Интеграция с Schema Registry Нет Да – смотрите детали.
Поддержка dead-letter queues Да Да
FIFO порядок сообщений Да – смотрите детали. Нет
Публичная доступность Можно создавать публичные топики Нет
Ценообразование $0.50/миллион запросов + цена за доставку (варьируется) + цена за отправку данных в Интернет (data transfer out). Цена за SMS варьируется. $1.00/миллион сообщений. Бесплатно для сообщений AWS. Нет цены за доставку.
Размер сообщения,  за который происходит оплата 1 сообщение = 64 KB 1 сообщение = 64 KB
Уровень бесплатного пользования AWS Да Нет
Межрегиональное взаимодействие Можно подписать Lambda функцию на топик Amazon SNS из любого Региона. Адресаты должны быть в том же Регионе. Можно отправлять сообщения между Регионами в другие шины сообщений.
Политика повтора отправки
  • Для SQS/Lambda: экспоненциальная политика повторений в течение 23 дней.
  • Для SMTP, SMS и мобильных уведомлений: экспоненциальная политика повторений в течение 6 часов.
«Хотя-бы-один-раз» (at-least-once) доставка сообщения адресатам, включая повторения с экспоненциальной политикой до 24 часов.

Выводы

Сервисы сообщений являются важной частью бессерверных приложений, и AWS предоставляет возможность использования очередей, модели «издатель – подписчик» (Pub/Sub) и шины событий. Этот пост рассматривает основной функционал сервисов SNS, SQS и EventBridge, и как они позволяют решать задачи для ваших приложений.

Я показал три примера приложений, которые отправляют и получают сообщения из трёх сервисов сообщений. Мы рассмотрели примеры кода AWS SAM для развёртывания этих ресурсов в ваших приложениях.

И, наконец, мы сравнили сервисы сообщений между собой.

Чтобы узнать больше о разделённых (decoupled) архитектурах, смотрите серию обучающих матералов по EventBridge. Чтобы ознакомиться с обучающими материалами по бессерверным технологиям, смотрите https://serverlessland.com.