Блог Amazon Web Services

Эксплуатация Lambda: дизайн приложений – масштабирование и параллелизм: часть 2

Оригинал статьи: ссылка (James Beswick, Senior Developer Advocate)

В серии из трёх постов «Эксплуатация Lambda» я раскрою важные темы для разработчиков, архитекторов и системных администраторов, которые управляют и обслуживают AWS Lambda-приложения.

Часть 1 Первая часть показывает, как работать с лимитами сервиса (Service Quotas), когда запрашивать их расширение, и как проектировать приложения с учётом этих лимитов.

Этот пост раскрывает аспекты масштабирования, а также двух видов параллелизма: «по-запросу» (on-demand) и Provisoned Concurrency.

Масштабирование и параллелизм в Lambda

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

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

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

Во всех AWS аккаунтах лимит по-умолчанию для параллелизма функции на регион равен 1000.

Это изменяемый лимит, который можно увеличить путём обращения в AWS Support Center.

Пример масштабирования функции по запросу (ondemand)

В данном примере, Lambda получает 10000 синхронных запросов из Amazon API Gateway и обработка каждого запроса занимает 15 секунд. Лимит параллелизма для аккаунта равен 10000. Следующий пример демонстрирует четыре сценария:

В каждом варианте, все запросы приходят в одно и тоже время в рамках минуты, когда они были запланированы:

  1. Все запросы приходят одновременно: 3000 запросов будут обработаны новыми экземплярами функций; 7000 будут отброшены из-за тротлинга.
  2. Запросы приходят на протяжении двух минут: 3000 запросов будут обработаны новыми экземплярами функций в первую минуту; оставшиеся 2000 — отброшены. Во вторую минуту: новые  500 экземпляров будут созданы и 3000 переиспользованы; 1500 – отброшены.
  3. Запросы приходят на протяжении трёх минут: 3000 запросов будут обработаны новыми экземплярами функций в первую минуту; оставшиеся 333 — отброшены. Во вторую минуту: новые  500 экземпляров будут созданы и 3000 переиспользованы; все запросы будут обработаны. В третью минуту, оставшиеся 3334 запросов будут обработаны существующими экземплярами.
  4. Запросы приходят на протяжении четырёх минут: в первую минуту, 2500 запросов будут обработаны новыми экземплярами функций; эти же экземпляры будут переиспользованы в остальные три минуты для обработки всех оставшихся запросов

Пример масштабирования функции при Provisioned Concurrency

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

Provisioned Concurrency – это функционал Lambda, который заранее подготавливает параллельные среды выполнения (экземпляры) функции, что помогает решить следующие две проблемы. Во-первых, если ожидаемый трафик превышает первичный объём параллелизма, предоставляемый Lambda, Provisioned Concurrency может обеспечить необходимый параллелизм для обработки трафика. Во-вторых, если ваши рабочие нагрузки чувствительны к задержкам, не превышающим двухзначных значений миллисекунд, Provisioned Concurrency решает проблемы «холодного запуска», которые вы можете получить при масштабировании «по-умолчанию».

Provisioned Concurrency – это настройка, доступная для указанной опубликованной версии или псевдонима Lambda функции, и не требует пользовательского кода или изменений логики функции, а также совместима с функционалом конфигурации VPC и Lambda-слоёв (layers). Чтобы узнать больше о том, как Provisioned Concurrency оптимизирует производительность для Lambda-ориентированных приложений, смотрите Tech Talk видео.

Используя те же сценарии с 10,000 запросами, функция настроена на Provisioned Concurrency 7,000:

  1. Для сценария 1, 7000 запросов обработаны пре-подготовленными средами выполнения без эффекта «холодного старта». Остальные 3000 запросов будут обработаны новыми средами, запущенными по запросу.
  2. Для сценариев #2-4, все запросы будут обработаны пре-подготовленными средами в рамках той минуты, когда пришли эти запросы.

Интеграция между сервисами и асинхронная обработка

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

API Gateway имеет лимит тротлинга по-умолчанию равный 10,000 запросам в секунду, который можно увеличить по запросу. Обычные очереди SQS поддерживают практически неограниченную пропускную способность для API запросов типа SendMessage.

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

При асинхронных обработках нет возможности вернуть результат обработки обратно на вызывающую сторону. Поэтому, исходный запрос API Gateway получает подтверждение, что сообщение было сохранено в SQS, которое и возвращает клиенту. Существует несколько подходов, как можно вернуть результат обработки вызывающей стороне. Один из вариантов был описан выше – использование DynamoDB таблицы для сохранения ID транзакции и статуса, который периодически запрашивается (poll) вызывающей стороной через отдельную точку вызова API Gateway, пока не будет получен статус окончания обработки. Также, можно использовать webhooks через Amazon SNS или WebSockets через AWS IoT Core для возврата результата.

Использование асинхронного подхода позволяет упростить обработку непредсказуемых объёмов трафика и сделать его более предсказуемым, хотя, возможно, это подойдёт не для всех сценариев использования.

Зарезервированный параллелизм

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

  1. Зарезервированный параллелизм вычитается из общего лимита параллелизма для AWS аккаунта и региона. Тогда, функция всегда будет иметь зарезервированный объём параллелизма для своих собственных нужд.
  2. Зарезервированный параллелизм ограничивает максимальное количество экземпляров функции. Синхронные запросы, приходящие за пределами зарезервированного объёма, будут отброшены с тротлинг кодом ошибки (429).

Таким образом вы можете ограничивать предельное количество одновременно обрабатываемых запросов для вашей рабочей нагрузки. Для Lambda функций, работающих в асинхронном режиме, или на базе внутреннего поллинга через интеграции S3, SQS, или DynamoDB, зарезервированный параллелизм ограничивает количество одновременно обрабатываемых запросов. В таких случаях, сообщения гарантированно сохраняются во внутренних очередях, пока Lambda функция не сможет продолжить обработку. Это позволяет сглаживать обработку при внезапных скачках трафика.

Например, Lambda функция получает сообщения из очереди SQS и записывает в таблицу DynamoDB. Её зарезервированный параллелизм имеет значение 10, и размер пакетной обработки – 10. Очередь SQS внезапно получает 1,000 сообщений. Lambda функция создаёт 10 экземпляров, каждый обрабатывающий по 10 сообщений из очереди. Хотя понадобится больше времени на обработку всех сообщений, такой подход позволяет обеспечить постоянный объём используемых write capacity units (WCUs) в таблице DynamoDB.

Чтобы узнать больше, читайте Managing AWS Lambda Function ConcurrencyиManaging concurrency for a Lambda function”.

Выводы

Эта статья описывает масштабирование и параллелизм в Lambda и её разное поведение при режимах on-demand и Provisioned Concurrency. Также она показывает как использовать интеграции с другими сервисами и паттерны асинхронной обработки в приложениях на базе Lambda. Наконец, мы рассказали как работает зарезервированный параллелизм и как его применять при проектировании ваших приложений.

Третья часть будет обсуждать выбор и использование сред выполнения, сетевой инфраструктуры и настроек VPC, и разные режимы вызовов функции.