[PYTHON] Asynchronous API that combines API Gateway and Step Functions was the strongest story

Is it the strongest or easy?

Common as a serverless configuration API Gateway & Lambda

** API Gateway has a 29 second limit **, ** Lambda has a 15 minute limit **

While making full use of Lambda's performance To fill that gap ** I want to make an asynchronous API with as simple a configuration as possible ** I think there is.

What you need at that time

--API for kicking Lambda --API to determine if the kicked Lambda is complete and receive a response

I think it is, but the Step Functions below have both **

Step Functions

In Step Functions Make workflows visually and handsome, such as linking microservices Can be configured.

** Official example: ** image.png

https://aws.amazon.com/jp/blogs/news/new-aws-step-functions-build-distributed-applications-using-visual-workflows/

It's dangerous I think I can do anything

By defining Lambda as a Task You can incorporate it into this workflow.

By the way, this is the one I will make this time (cute)

image.png

Create API Gateway

image.png

I will make it like this

APIfor start-execution to kick the aboveLambda API for describe-execution to receive response from Lambda (The name can be anything)

** Both are POST **

Serverless Framework

https://github.com/ChaseSan/async-api-sample

I will actually make it with SLS It's much easier than making it with CFn, You need to include serverless-step-functions as a plugin

serverless.yml


plugins:
  - serverless-step-functions

The function is created like this ** Ramen timer **

serverless.yml


functions:
  async-api:
    handler: app.lambda_handler
    name: async-api-${self:provider.stage}
    environment:
      TIMER: 30

app.py


import os
from time import sleep
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)


def lambda_handler(event, context):
    logger.info(f"event: {event}")

    seconds = int(os.environ.get("TIMER"))
    sleep(seconds)

    return {"message": "The ramen is boiled. If you don't eat it quickly, it will grow."}

The definition of Step Functions looks like this When using the serverless-step-functions plugin Also write the API Gateway definition inside the stateMachines block

serverless.yml


stepFunctions:
  stateMachines:
    state-machine:
      name: state-machine-${self:provider.stage}
      events:
        - http:
            path: ${self:custom.basePath}/start-execution
            method: post
            action: StartExecution
            iamRole:
              Fn::GetAtt: [AsyncApiRole, Arn]
            request:
              template:
                application/json: |-
                  {
                    "stateMachineArn":"arn:aws:states:#{AWS::Region}:#{AWS::AccountId}:stateMachine:state-machine-${self:provider.stage}"
                  }
        - http:
            path: ${self:custom.basePath}/describe-execution
            method: post
            action: DescribeExecution
            iamRole:
              Fn::GetAtt: [AsyncApiRole, Arn]
            response:
              template:
                application/json: |-
                  {
                    "input": $util.parseJson($input.json('$.input')),
                    #if($input.path('$.output') != "")
                      "output": $util.parseJson($input.json('$.output')),
                    #end
                    "status": $input.json('$.status')
                  }
      definition:
        StartAt: async-api-task
        States:
          async-api-task:
            Type: Task
            Resource:
              Fn::GetAtt: [async-api, Arn]
            End: true

** Points **

--In start-execution, describe StartExecution in ʻevents.http.action. --ʻEvents.http.request.template describes Arn of StateMachine to be executed in Json format. --In describe-execution, describe DescribeExecution in ʻevents.http.action. --Description the response in Json format in ʻevents.http.request.template

{
    "response": $input.json('$'),
}

Anyway, all the contents of DescribeExecution will be returned. It would be better to return only what you need in terms of interface (probably)

move

Let's deploy and move sls deploy image.png Try it with the endpoint that appears in the console

start-execution image.png Use the obtained ʻexecutionArn` to hit the following API

describe-execution image.png

Something has returned. status is RUNNING. It seems that ramen hasn't been made yet.

Let's hit it again after a while image.png

Oh, status became SUCCEEDED, and you told me that the ramen was completed.

that's all

No, it ’s really easy. Without this, I would have to make full use of Dynamo and SQS and implement it myself (hell).

Recommended Posts

Asynchronous API that combines API Gateway and Step Functions was the strongest story
The story that XGBoost was finally installed
A story that Seaborn was easy, convenient and impressed
PHP and Python samples that hit the ChatWork API
The story that the return value of tape.gradient () was None
The story that Japanese output was confused with Django
The story that my pull request was incorporated into Scipy
The story that the new drawing library "HiPlot" was pretty good
This and that for using Step Functions with CDK + Python
Zabbix API this and that
The story that the version of python 3.7.7 was not adapted to Heroku
The story that the Homebrew environment was blown away when Anaconda was installed
The story that the guard was confined when the laboratory was converted to IoT