使用Step Function编排AWS Lambda[AWS Lambda教程-SST系列]

以一个例子来帮助大家理解AWS Step Functions的典型使用场景并使用SST进行开发。以一个简单的压测Demo为例来进行演示:多个AWS Lambda在一起完成一个任务,首先 getApiInfo读取压测用的一批参数,然后传递给callApiParallel进行具体的压测请求任务,每个请求循环执行50次,全部的任务是并行执行,压测任务完成后进行结果的收集。

具体的如图所示:

前面几种主流的Serverless开发框架(以AWS Lambda为例)介绍了使用框架简化AWS Lambda的选型和优点。其中AWS SAM目前不能很好的支持AWS Step Function,serverlessSST支持的不错,都能非常轻松的帮我们完成Step Function的开发。

AWS Step Functions简介

AWS Step Functions是一项无服务器编排服务,它可以方便的将多个AWS Lambda的连接在一起,集中起来编排为一个整体,最后组成一个统一的业务服务。Step Functions基于状态机和任务,在Step Functions中,工作流被称为状态机,它是一系列事件驱动的步骤,而任务状态代表其他AWS服务(例如AWS Lambda)执行的工作单元。我们知道AWS Lambda是一种serverless计算服务,它基于实践驱动理念设计,适用于需要快速纵向扩展并在不需要时缩减至零的应用程序场景,基本全部的外部事件都可以和Lambda无缝都集成并触发Lambda的执行,比如无状态的HTTPS请求、IoT设备产生的行为事件,文件处理(图片处理或者转码)等。

为什么有时要用AWS Step Functions

我们的架构一般都是要设计为SOLID (面向对象设计),单个Lambda函数虽然可以处理多个不同的逻辑但是最好还是符合单一职责原则开闭原则,也就是一个Lambda函数专门完成一个或者一类功能,多个lambda函数共同组成全部功能的整体。这样无论哪个功能的TPS有变化,对应的Lamdba函数都可以独立的水平扩展而不影响其他的lambda函数,避免了资源的浪费、避免因为软件质量问题扩散到整个服务群中造成的更大范围的影响。

但是对一个业务系统而言,多个Lambda的协调管理的难度和SOLID是一个很难调和的问题,lambda拆分的越细(最细一个api或者功能一个lambda)那么越符合SOLID但是管理却越难,一般拆分的粒度需要自己把握,取得一个平衡。为了简化大量Lambda间关系管理和协调的的工作,AWS Step Functions给我们提供一个非常好的选择,它帮助我们轻松的完成了多Lambda的管理。

使用SST简化Step Function和AWS Lambda的开发

代码样例在Github serverless-way

编写模拟代码

创建getApiInfo.ts作为Step Function的第一个任务,用于指示Step Function开始运行

javascript 复制代码
import {APIGatewayProxyHandlerV2} from "aws-lambda";
import {SFNClient, StartExecutionCommand} from "@aws-sdk/client-sfn";

export const handler: APIGatewayProxyHandlerV2 = async (event) => {
    const client = new SFNClient({}); //Create the Step Function client

    // Send a command to this client to start the state machine which ARN is specified
    await client.send(
        new StartExecutionCommand({
            stateMachineArn: process.env.STATE_MACHINE,
        })
    );
    return {
        statusCode: 200,
        body: "Start machine started",
    };
};

创建getApiInfo.ts作为Step Function的第二个任务,用于模拟获取压测任务的参数

typescript 复制代码
import {APIGatewayProxyHandlerV2, Handler} from "aws-lambda";
import * as console from "console";

export const handler: Handler = async (event: APIGatewayProxyHandlerV2) => {
    console.log("getApiInfo started.")

    return {
        "callApis": [
            {"A": "A1"},
            {"B": "B1"},
            {"C": "C1"},
            {"D": "D1"},
            {"E": "E1"},
        ]
    };
};

创建callApiParallel.ts作为Step Function的第三个任务,用于模拟执行压测任务

typescript 复制代码
import {Handler} from "aws-lambda";
import * as console from "console";

export const handler: Handler = async (event) => {
    console.log("callApiParallel started.")
    return {
        statusCode: 200,
        body: "callApiParallel executed.",
    };
};

创建statisticCalledResults.ts作为Step Function的第四个任务,用于模拟执行压测任务完成后的数据统计

typescript 复制代码
import {Handler} from "aws-lambda";
import * as console from "console";

export const handler: Handler = async (event) => {
    console.log("statisticCalledResults started.")
    return {
        statusCode: 200,
        body: "statisticCalledResults executed.",
    };
};

SST管理Step Function编排逻辑

创建MyStack.ts来管理Step Function编排逻辑

javascript 复制代码
import {Api, Function, StackContext} from "sst/constructs";
import {LambdaInvoke} from "aws-cdk-lib/aws-stepfunctions-tasks";
import {Chain, JsonPath, Map, StateMachine} from "aws-cdk-lib/aws-stepfunctions";

export function MyStack({stack}: StackContext) {
    // Step Function的第二个任务,用于模拟获取压测任务的参数
    const getApiInfo = new LambdaInvoke(stack, "getApiInfo", {
        lambdaFunction: new Function(stack, "getApiInfo-func", {
            handler: "packages/functions/src/getApiInfo.handler",
        }),
    });

    // Step Function的第三个任务,用于模拟执行压测任务
    const callApiParallel = new LambdaInvoke(stack, "callApiParallel", {
        lambdaFunction: new Function(stack, "callApiParallel-func", {
            handler: "packages/functions/src/callApiParallel.handler",
        }),
    });

    // Step Function的第四个任务,用于模拟执行压测任务完成后的数据统计
    const statisticCalledResults = new LambdaInvoke(stack, "statisticCalledResults", {
        lambdaFunction: new Function(stack, "statisticCalledResults-func", {
            handler: "packages/functions/src/statisticCalledResults.handler",
        }),
    });

    // Step Function管理,创建一个Map状态机并行执行的任务,最大并发50
    const map = new Map(stack, "MapCompute", {
        maxConcurrency: 50,
        inputPath: JsonPath.stringAt('$.Payload'),
        itemsPath: JsonPath.stringAt('$.callApis')
    });

    // 创建一个step function的state链,串联前面的map任务和最后的统计任务
    const stateDefinition = Chain.start(getApiInfo)
        .next(map.iterator(callApiParallel))
        .next(statisticCalledResults);

    // 用stateDefinition定义Step function的逻辑
    const stateMachine = new StateMachine(stack, "ApiParallelCallerStepMachine", {
        definition: stateDefinition,
    });

    // Step Function的第一个任务,用于指示Step Function开始运行,当用户访问/start-machine时触发整个step function的启动
    const api = new Api(stack, "apiStartMachine", {
        routes: {
            "GET /start-machine": {
                function: {
                    handler: "packages/functions/src/startMachine.handler",
                    environment: {
                        STATE_MACHINE: stateMachine.stateMachineArn,
                    },
                },
            },
        },
    });

    // To grant the permission to our route to start our state machine
    api.attachPermissionsToRoute("GET /start-machine", [
        [stateMachine, "grantStartExecution"],
    ]);

    //To show the API endpoint in the output
    stack.addOutputs({
        ApiEndpoint: api.url,
    });
}

整体的结构如图

部署

bash 复制代码
# 安装sst框架组件
npm install sst --save-exact
# 部署
npx sst deploy
# 实验完成后移除
npx sst remove

当我们deploy的时候,SST会自动进行编译、打包、资源创建、部署工作 部署完成后会给我们本次Demo项目的可访问链接,方便我们调用它来进行PoC论证 当我们访问这个链接的/start-machine路径时就会触发Step Function的运行,我们可以在AWS Lamdba控制台或者Step Function自己的控制台上看到我们创建的Step Function,并实时的观察到它所处的状态。比如我下面的的图时运行完成的样子。

AWS Lamdba控制台: Step Function控制台:

参考

相关推荐
唐僧洗头爱飘柔952710 分钟前
(Go基础)变量与常量?字面量与变量的较量!
开发语言·后端·golang·go·go语言初上手
monkey_meng27 分钟前
【Rust Crate之Actix Web(一)】
开发语言·后端·rust
2401_857026231 小时前
SpringBoot环境下的共享汽车管理策略
spring boot·后端·汽车
2401_857622661 小时前
共享汽车管理:SpringBoot技术实现与优化
spring boot·后端·汽车
夜色呦1 小时前
SpringBoot助力的共享汽车业务优化系统
spring boot·后端·汽车
代码小鑫1 小时前
A15基于Spring Boot的宠物爱心组织管理系统的设计与实现
java·开发语言·spring boot·后端·毕业设计·宠物
2401_871290582 小时前
Scala 中的Set
开发语言·后端·scala
Afanda…3 小时前
Git超详细教程
运维·后端·gitee
黄俊懿4 小时前
【架构师从入门到进阶】第一章:架构设计基础——第五节:架构演进(缓存到微服务)
分布式·后端·缓存·微服务·架构·系统架构·架构设计
一位资深码农5 小时前
面试题分享11月7日
后端