Пошаговые функции быстро становятся стандартом де-факто для построения рабочих процессов в AWS. Откройте для себя хитрость их настройки с помощью REST API.

Когда несколько лет назад я впервые начал работать с безсерверными технологиями, пошаговые функции были чем-то вроде загадки.

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

А как насчет вызовов Lambda между учетными записями в рабочих процессах? Где здесь вступают в действие пошаговые функции?

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

Но это изменилось.

Я убежден, что команда Step Functions не спит. Каждую неделю они выпускают высококачественные и ценные функции, а сообщество разработчиков AWS постоянно пишет о них. Это прекрасное время, чтобы познакомиться с тем, что обязательно станет путем будущего.

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

Есть много случаев использования, когда вам нужен синхронный ответ. Если вы хотите использовать пошаговые функции в этих сценариях, вам придется использовать синхронные экспресс-рабочие процессы. Итак, сегодня мы рассмотрим, что нужно для создания конечной точки в REST API, которая использует экспресс-процесс в качестве серверной части.

Почему ступенчатые функции важнее лямбда?

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

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

Наш сегодняшний вариант использования — отличный пример того, когда использование пошаговых функций лучше, чем использование лямбда. Мы собираемся создать API, который позволит пользователям регистрироваться для получения ключа API для созданного нами микросервиса.

Андрес Морено лучше всего описывает это в своей статье:

«При создании API вы хотите обеспечить безопасность, чтобы избежать ненадлежащего использования и дополнительных затрат из-за непреднамеренного использования», — Андрес Морено

Аутентификация абсолютно необходима в любом проекте. Даже доказательство концепции (POC) должно быть защищено каким-то уровнем безопасности. Итак, мы собираемся создать простой API, который находится рядом с нашими сервисами и позволяет пользователям создавать ключи API и регистрироваться для них.

Регистрация ключа API состоит из нескольких шагов:

На нашем счастливом пути есть четыре отдельных шага. Не говоря уже о каких-либо валидациях и состояниях проверки, которые нам нужно встроить.

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

Вместо этого переход на управляемый конечный автомат обеспечивает более удобочитаемость и лучшее долгосрочное управление нашим рабочим процессом.

Построить государственную машину

При создании конечного автомата мне нравится начинать с консоли AWS и использовать Workflow Studio для его создания.

Я могу перетаскивать интеграцию с AWS SDK напрямую в конечный автомат и передавать выходные данные из одного вызова во входные данные для другого.

На начальном этапе разработки я создаю конечные автоматы как Стандартные рабочие процессы, чтобы я мог графически наблюдать за выполнением моих тестов разработки и легко выявлять проблемы. Рабочие процессы Express ограничены отображением результатов журнала, что немного усложняет устранение неполадок на начальном этапе разработки.

Чтобы построить наш счастливый путь, мы можем добавить четыре вызова API в рабочий процесс и некоторые начальные проверки:

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

Компенсационные действия

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

Если вызов SDK по какой-либо причине завершается сбоем в нашей конечной машине, мы хотим убедиться, что удалили все созданные ресурсы. Ключи API и планы использования — это двери в ваш API, поэтому, если мы их случайно оставляем, мы оставляем неотслеживаемую запись в систему.

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

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

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

Обратите внимание, что в этом автомате нет лямбда-выражений. Благодаря большому релизу в прошлом году Step Functions может напрямую интегрироваться с AWS SDK, что значительно ускоряет ваши конечные автоматы, поскольку нет холодных запусков Lambda.

Чтобы просмотреть конечный автомат целиком, вы можете просмотреть его на GitHub.

Подключить шлюз API к функциям шага

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

Я большой сторонник использования Open API Spec (OAS) для определения ваших бессерверных API. В нашем примере мы будем использовать VTL для подключения шлюза API к Step Functions через нашу OAS.

x-amazon-apigateway-integration: 
  credentials: 
    Fn::Sub: ${AddApiKeyRole.Arn} 
  uri: 
    Fn::Sub:   arn:${AWS::Partition}:apigateway:${AWS::Region}:states:action/StartSyncExecution 
  httpMethod: POST 
  type: aws 
  requestTemplates: 
    application/json: 
      Fn::Sub: 
        - |- 
          #set($context.responseOverride.header.Access-Control-Allow-Origin = '*') 
          #set($body = "{""detail"" : $input.json('$')}") 
          { 
            "input": "$util.escapeJavaScript($body)", 
            "stateMachineArn": "${StateMachine}" 
          } 
        - { StateMachine: { "Ref": "AddApiKeyStateMachine" }}

AWS x-amazon-apigateway-integration Расширение Open API позволяет нам определять входные данные, поступающие из запроса API, и преобразовывать их в форму, требуемую конечным автоматом.

С этой конфигурацией созданный нами конечный автомат запустится и вернет ответ. Таким образом, мы должны сопоставить различные ответы с допустимыми http-ответами. Для этого мы можем добавить к расширению x-amazon-apigateway-integration раздел ответы.

responses: 
  200: 
    statusCode: 201 
    responseTemplates: 
      application/json: | 
        #set($context.responseOverride.header.Access-Control-Allow-Origin = '*') 
        #set($inputRoot = $input.path('$')) 
        #set($output = $util.parseJson($input.path('$.output'))) 
        { 
          #if("$output.apiKey" != "") 
            "apiKey": "$output.apiKey" 
          #end 
          #if("$inputRoot.error" == "NameExists") 
            #set($context.responseOverride.status = 400) 
            "message": "$inputRoot.cause" 
          #end 
          #if("$inputRoot.error" == "UnhandledError") 
            #set($context.responseOverride.status = 500)
            "message": "$inputRoot.cause" 
          #end 
        }

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

Это сопоставление возьмет три возможных результата из нашего конечного автомата и сопоставит их с соответствующими http-ответами.

  • Успех — если конечный автомат запускается и возвращает свойство apiKey в ответе, мы возвращаем код состояния 201 и значение сгенерированного ключа API.
  • Имя существует. Если кто-то уже зарегистрировался для получения ключа API и использовал то же имя, которое ввел вызывающий абонент, мы возвращаем код состояния 400, указывающий, что вызывающему абоненту необходимо внести изменения.
  • Необработанная ошибка. Если что-то пойдет не так, чего мы не ожидали во время обработки нашего рабочего процесса, мы вернем код состояния 500, указывающий, что что-то пошло не так на стороне сервера, и вызывающая сторона может подождать или попробовать снова.

Ошибки NameExists и UnhandledError, которые мы проверяем, добавляются специально в наш конечный автомат как состояния сбоя. Вы можете добавить любой тип состояния отказа и явно проверить, возвращают ли они разные ответы вызывающей стороне.

Запустите тест скорости

Теперь, когда конечный автомат завершен и сопоставления OAS созданы, мы можем запустить тест на развернутом API, чтобы увидеть, насколько он быстр на самом деле.

Для этого мы будем использовать Postman для запуска теста, чтобы увидеть время отклика.

1004 мс — не самая быстрая конечная точка, но это довольно быстро, учитывая все, что она делает. Этот пост иллюстрирует, как выполнять операции полностью синхронно.

В моем последнем посте обсуждались полностью асинхронные конечные точки с подходом к API с приоритетом хранения. Есть случай, когда вы получаете идеальный баланс, используя как синхронную, так и асинхронную обработку с вашими конечными точками.

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

Заключение

Как и во всем, в программном обеспечении, которое вы создаете, есть компромиссы. Вы можете перейти на полностью синхронный режим и получить полные проверки, возвращаемые пользователю с более медленным временем отклика, или вы можете вернуть частичный ответ и попросить пользователя проверить результат асинхронного задания.

Баланс — это все. Выясните, какое решение является лучшим для вас и ваших потребителей. Асинхронность набирает все большую популярность, и вы можете делать такие вещи, как внедрить WebSockets, чтобы улучшить взаимодействие с пользователем. Плюс слово 2022 года для разработки программного обеспечения.

Но у вас также есть важные конечные точки, которые должны быть синхронными. Как генерация ключа API.

Безопасность всегда стоит ожидания.

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

Удачного кодирования!