14
16

More than 1 year has passed since last update.

API GatewayとStep Functionsを組み合わせた非同期APIが最強だった話

Last updated at Posted at 2020-10-10

最強というかカンタン?

サーバーレスの構成として一般的な
API Gateway&Lambdaですが

API Gatewayは29秒制限
Lambdaは15分制限があるわけですね

Lambdaの性能を目一杯使いつつ、
そのギャップを埋めるため
なるべくシンプルな構成で非同期APIにしたい ってこと
あると思います。

そのときに必要なのは

  • LambdaをキックするためのAPI
  • キックしたLambdaが完了したか判断し、レスポンスを受け取るためのAPI

だと思いますが、下記Step Functionsにはその両方が備わっています

Step Functions

Step Functionsでは、
マイクロサービスの連携などワークフローを視覚的でイケメンな感じに
構成することができます。

公式例:
image.png

やばいですね
なんでもできちゃいそうです

TaskとしてLambdaを定義することで
このワークフローにじゃかじゃか組み込んでいけちゃうんですね

ちなみに今回作るのはこれです(かわいい)

image.png

API Gatewayをつくる

image.png

こんなかんじでつくります

start-executionが上述のLambdaをキックするためのAPI
describe-executionがLambdaからレスポンスを受け取るためのAPI
(名前はなんでもいいです)

両方POSTです

Serverless Framework

んじゃSLSで実際に作っていきます
CFnで作るより格段に楽なんですが、
プラグインとして serverless-step-functionsserverless-pseudo-parameters を入れる必要があります

serverless.yml
plugins:
  - serverless-step-functions
  - serverless-pseudo-parameters

関数はこんな感じでつくります
ラーメンタイマーです

serverless.yml
provider:
  name: aws
  runtime: python3.8
  stage: ${opt:stage, 'dev'}
  region: ap-northeast-1
  memorySize: 128
  timeout: 900

custom:
  basePath: ramen

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": "らーめんが ゆであがりました はやく たべないと のびて しまいます"}

Step Functionsの定義はこんな感じに
serverless-step-functionsプラグインを使う場合
API Gatewayの定義もstateMachinesブロック内に書きます

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: |-
                  {
                    "input": "$util.escapeJavaScript($input.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

ポインツ

  • start-executionにはevents.http.actionStartExecutionを記載
    • events.http.request.templateに実行するStateMachineのArnをJson形式で記載
  • describe-executionにはevents.http.actionDescribeExecutionを記載
    • events.http.request.templateにレスポンスをJson形式で記載
{
    "response": $input.json('$'),
}

とかにすればDescribeExecutionの中身が全部返るんだけど
必要なものだけ返してあげたほうが、インターフェース的にヤサシイでしょう(たぶん)

動かす

じゃあデプロイしてうごかしましょう
sls deployっと
image.png
コンソールに出たエンドポイントでお試しします

start-execution
image.png
これで取得したexecutionArnをつかって次のAPIを叩きます

describe-execution
image.png

なんか返ってきましたね。
statusRUNNINGです。
まだラーメンできていないようです。

少し間をおいて、もう一度叩いてみましょう
image.png

お、statusSUCCEEDEDになり、ラーメンができあがったことをおしえてくれましたね

以上

いやーめちゃめちゃ楽ですね
これがなかったらDynamoとかSQSとかをフル活用して自前実装しないといけないとこでした(地獄)

14
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
16