You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何搭建API Gateway->SQS->Lambda架构并实现Lambda返回响应?

问题描述

我想要搭建一套 API Gateway -> SQS -> Lambda 的架构,实现Lambda处理完成后向客户端返回API Gateway响应。目前已经完成API Gateway到SQS的配置,当SQS有消息时会触发Lambda函数,但不知道如何让Lambda将处理结果返回给客户端。


现有配置代码(CDK)

SQS配置

const sqs = new sqs.Queue(this, 'sqs', {
   visibilityTimeout: cdk.Duration.seconds(60),
   queueName: 'sqs',
 });
sqs.grantSendMessages(customRole);

const sqsIntegration = new apigw.AwsIntegration({
   service: 'sqs',
   integrationHttpMethod: "POST",
   path: `${cdk.Aws.ACCOUNT_ID}/${sqs.queueName}`,
   options: {
     credentialsRole: customRole,
     passthroughBehavior: apigw.PassthroughBehavior.NEVER,
     integrationResponses: [def.IntegrationResponse(200)],
     requestParameters: {
       'integration.request.header.Content-Type': `'application/x-www-form-urlencoded'`,
     },
     requestTemplates: {
       "application/json": `Action=SendMessage&MessageBody={"route": "$input.params('route')", "seat": "$input.params('seat')", "body": "$util.parseJson($input.body)"}`
     }
   }
});

Lambda配置

const lambdaFn = new lambda.Function(this, 'lambdaFn', {
   functionName: 'lambdaFn',
   handler: 'lambdaFn',
   memorySize: 1024,
   role: customRole,
   runtime: lambda.Runtime.GO_1_X,
   timeout: cdk.Duration.seconds(60),
   code: lambda.Code.fromAsset('cmd/lambdaFn')
});

lambdaFn.addEventSource(new SqsEventSource(sqs, {
   enabled: true
}));

API Gateway方法配置

apigw.addMethod('POST', sqsIntegration, {
   requestParameters: {
     'method.request.querystring.seat': true,
     'method.request.querystring.route': true
   },
   methodResponses: [
     def.MethodResponse(200, ApiResponseModel),
     def.MethodResponse(400, ApiResponseModel),
     def.MethodResponse(415, ApiResponseModel),
   ],
   requestValidatorOptions: {
     validateRequestBody: false,
     validateRequestParameters: true,
     requestValidatorName: 'api-sqs-validator',
   }
});

解决方案

当前架构中,API Gateway将消息发送到SQS后会立即返回响应给客户端,Lambda处理SQS消息是异步执行的,因此客户端无法直接获取Lambda的处理结果。要实现Lambda向客户端返回响应,需要调整架构或引入异步响应机制,以下是两种可行方案:

方案一:异步请求+轮询获取结果

步骤1:添加DynamoDB存储结果

创建DynamoDB表,用于存储请求ID和Lambda的处理结果,支持客户端通过请求ID轮询状态。

const resultTable = new dynamodb.Table(this, 'RequestResultTable', {
  partitionKey: { name: 'requestId', type: dynamodb.AttributeType.STRING },
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
  tableName: 'RequestResultTable'
});

// 授予Lambda读写DynamoDB的权限
resultTable.grantReadWriteData(lambdaFn);

步骤2:修改API Gateway集成,携带请求ID

调整SQS集成的请求模板,生成唯一requestId并传入SQS消息,同时将requestId返回给客户端:

// 修改SQS集成的requestTemplates
requestTemplates: {
  "application/json": `Action=SendMessage&MessageBody={"requestId": "$context.requestId", "route": "$input.params('route')", "seat": "$input.params('seat')", "body": "$util.parseJson($input.body)"}`
}

// 修改integrationResponses,返回请求ID和待处理状态
integrationResponses: [
  def.IntegrationResponse(202, {
    responseTemplates: {
      "application/json": `{"requestId": "$context.requestId", "status": "pending"}`
    }
  })
]

步骤3:修改Lambda逻辑,写入处理结果

Lambda处理SQS消息时,完成业务逻辑后将结果写入DynamoDB关联requestId:

package main

import (
  "context"
  "encoding/json"
  "github.com/aws/aws-lambda-go/events"
  "github.com/aws/aws-lambda-go/lambda"
  "github.com/aws/aws-sdk-go-v2/aws"
  "github.com/aws/aws-sdk-go-v2/config"
  "github.com/aws/aws-sdk-go-v2/service/dynamodb"
  "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

type SQSMessageBody struct {
  RequestId string          `json:"requestId"`
  Route     string          `json:"route"`
  Seat      string          `json:"seat"`
  Body      json.RawMessage `json:"body"`
}

type ProcessingResult struct {
  Status string      `json:"status"`
  Result interface{} `json:"result,omitempty"`
  Error  string      `json:"error,omitempty"`
}

func handler(ctx context.Context, sqsEvent events.SQSEvent) error {
  cfg, err := config.LoadDefaultConfig(ctx)
  if err != nil {
    return err
  }
  dbClient := dynamodb.NewFromConfig(cfg)

  for _, msg := range sqsEvent.Records {
    var body SQSMessageBody
    if err := json.Unmarshal([]byte(msg.Body), &body); err != nil {
      continue
    }

    // 执行业务逻辑
    result, err := processRequest(body.Route, body.Seat, body.Body)
    var processingResult ProcessingResult
    if err != nil {
      processingResult = ProcessingResult{Status: "failed", Error: err.Error()}
    } else {
      processingResult = ProcessingResult{Status: "success", Result: result}
    }

    // 写入DynamoDB
    resultJson, _ := json.Marshal(processingResult)
    _, err = dbClient.PutItem(ctx, &dynamodb.PutItemInput{
      TableName: aws.String("RequestResultTable"),
      Item: map[string]types.AttributeValue{
        "requestId": &types.AttributeValueMemberS{Value: body.RequestId},
        "result":    &types.AttributeValueMemberS{Value: string(resultJson)},
      },
    })
  }
  return nil
}

func processRequest(route, seat string, body json.RawMessage) (interface{}, error) {
  // 替换为实际业务逻辑
  return map[string]string{"route": route, "seat": seat}, nil
}

func main() {
  lambda.Start(handler)
}

步骤4:添加轮询API端点

创建查询结果的Lambda,并在API Gateway添加GET方法,支持客户端通过requestId查询结果:

// 创建查询Lambda
const queryResultLambda = new lambda.Function(this, 'QueryResultLambda', {
  functionName: 'QueryResultLambda',
  handler: 'queryResult',
  memorySize: 512,
  role: customRole,
  runtime: lambda.Runtime.GO_1_X,
  timeout: cdk.Duration.seconds(10),
  code: lambda.Code.fromAsset('cmd/queryResult')
});

resultTable.grantReadData(queryResultLambda);

// 添加GET方法到API Gateway
apigw.addMethod('GET', new apigw.LambdaIntegration(queryResultLambda), {
  requestParameters: {
    'method.request.querystring.requestId': true
  },
  methodResponses: [
    def.MethodResponse(200, ApiResponseModel),
    def.MethodResponse(404, ApiResponseModel),
  ],
  requestValidatorOptions: {
    validateRequestParameters: true,
    requestValidatorName: 'api-result-validator',
  }
});

查询Lambda的Go代码:

package main

import (
  "context"
  "encoding/json"
  "github.com/aws/aws-lambda-go/events"
  "github.com/aws/aws-lambda-go/lambda"
  "github.com/aws/aws-sdk-go-v2/aws"
  "github.com/aws/aws-sdk-go-v2/config"
  "github.com/aws/aws-sdk-go-v2/service/dynamodb"
  "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  requestId := request.QueryStringParameters["requestId"]
  if requestId == "" {
    return events.APIGatewayProxyResponse{StatusCode: 400, Body: `{"error": "requestId is required"}`}, nil
  }

  cfg, err := config.LoadDefaultConfig(ctx)
  if err != nil {
    return events.APIGatewayProxyResponse{StatusCode: 500, Body: `{"error": "internal server error"}`}, err
  }
  dbClient := dynamodb.NewFromConfig(cfg)

  result, err := dbClient.GetItem(ctx, &dynamodb.GetItemInput{
    TableName: aws.String("RequestResultTable"),
    Key: map[string]types.AttributeValue{
      "requestId": &types.AttributeValueMemberS{Value: requestId},
    },
  })
  if err != nil {
    return events.APIGatewayProxyResponse{StatusCode: 500, Body: `{"error": "internal server error"}`}, err
  }

  if result.Item == nil {
    return events.APIGatewayProxyResponse{StatusCode: 404, Body: `{"status": "pending"}`}, nil
  }

  resultStr := result.Item["result"].(*types.AttributeValueMemberS).Value
  return events.APIGatewayProxyResponse{StatusCode: 200, Body: resultStr, Headers: map[string]string{"Content-Type": "application/json"}}, nil
}

func main() {
  lambda.Start(handler)
}

方案二:WebSocket API主动推送结果

如果需要实时向客户端推送结果,可使用API Gateway WebSocket API:

步骤1:创建WebSocket API

const webSocketApi = new apigw.WebSocketApi(this, 'WebSocketApi', {
  connectRouteOptions: {
    integration: new apigw.LambdaIntegration(connectLambda)
  },
  disconnectRouteOptions: {
    integration: new apigw.LambdaIntegration(disconnectLambda)
  }
});

const webSocketStage = new apigw.WebSocketStage(this, 'WebSocketStage', {
  webSocketApi,
  stageName: 'prod',
  autoDeploy: true
});

步骤2:携带连接ID到SQS消息

客户端建立WebSocket连接后会获得connectionId,将该ID作为参数传入SQS消息(可通过客户端请求携带或API Gateway模板自动注入)。

步骤3:Lambda处理后推送结果

Lambda处理完消息后,调用API Gateway的PostToConnection接口将结果推送给客户端:

// 代码片段
client := apigatewaymanagementapi.NewFromConfig(cfg, func(o *apigatewaymanagementapi.Options) {
  o.BaseEndpoint = aws.String("https://" + apiId + ".execute-api." + region + ".amazonaws.com/prod")
})

_, err = client.PostToConnection(ctx, &apigatewaymanagementapi.PostToConnectionInput{
  ConnectionId: aws.String(connectionId),
  Data:         []byte(resultJson),
})

关键说明
  • 方案一适合非实时场景,实现简单、兼容性强,需处理消息重复、结果过期等问题。
  • 方案二适合实时推送场景,但要求客户端支持WebSocket连接。

内容的提问来源于stack exchange,提问作者undefine97

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 13:24:28