使用 API Gateway Integrator 在 Quarkus 中实施适用于 AWS Lambda 的 OpenAPI

AWS API Gateway 集成使得使用符合 OpenAPI 标准的 Lambda Function 轻松实现 REST API。

关于开放API

它是一个允许以标准方式描述 REST API 的规范。OpenAPI规范 (OAS) 为 REST API 定义了编程语言无关的标准接口描述。这使得人类和计算机都可以发现和理解服务的功能,而无需访问源代码、附加文档或检查网络流量。

关于Swagger

Swagger是一组围绕 OpenAPI 规范构建的开源工具,可以帮助软件专业人员设计、构建、记录和使用 REST API。OpenAPI 处理规范,Swagger 实现规范来描述 REST API 。

OpenAPI规范简介

目的

  • API是使用OpenAPI标准定义的,可以被人类和计算机发现,无需访问源代码、文档或通过网络流量检查即可了解服务的功能。
  • 如果定义正确,消费者可以使用最少量的实现逻辑来理解远程服务并与之交互。
  • 然后,文档生成工具可以使用 OpenAPI 定义来显示 API,代码生成工具可以使用各种编程语言、测试工具和许多其他用例来生成服务器和客户端。

开放API的 主要 概念

OpenAPI文档

定义/描述 API 的文档(或一组文档)。OpenAPI 定义使用并符合 OpenAPI 规范。

路径模板

路径模板是指使用由大括号 ({}) 分隔的模板表达式,将 URL 路径的一部分标记为可使用路径参数进行替换。

路径中的每个模板表达式必须对应于路径项本身和/或路径项的每个操作中包含的路径参数。

媒体类型

媒体类型定义分布在多个资源中。媒体类型定义应符合RFC6838

可能的媒体类型定义的一些示例:

  • 应用程序/json
  • 测试/普通;chartset-uff-8

HTTP 状态代码

HTTP 状态代码用于指示已执行操作的状态。可用的状态代码由RFC7231定义,注册的状态代码列在IANA 状态代码注册表中。

AWS Lambda 函数的开放 API 参考实现

参考架构

参考实现由以下组件组成:

  • AWS Lambda函数实现业务需求/逻辑
  • API 网关将功能公开为 API
  • Amazon API 网关集成器,用于集成开放 API 规范并将 Lambda 函数操作公开为 API

有关每个架构组件的更多详细信息,请参阅后续部分。

关于 AWS Lambda 函数

Lambda 是一种计算服务,允许在不配置或管理服务器的情况下运行代码。Lambda 在高可用性计算基础设施上运行代码,并执行计算资源的所有管理,包括服务器和操作系统维护、容量配置和自动扩展以及日志记录。使用 Lambda,几乎可以为任何类型的应用程序或后端服务运行代码。它所需要的只是以Lambda 支持的语言之一提供代码。

Lambda 仅在需要时运行函数并自动扩展,从每天几个请求到每秒数千个请求。它节省了计算成本,因为只需支付函数在运行时消耗的计算时间------代码不运行时不收费。可以使用 Lambda API 或来自其他 AWS 服务的事件调用 lambda 函数,例如在 S3 存储桶中创建对象时。

关于 AWS API 网关

Amazon API Gateway 是一项完全托管的服务,使开发人员可以轻松创建、发布、维护、监控和保护任何规模的 API。API 充当应用程序从后端服务访问数据、业务逻辑或功能的"前门"。

关于AWS API集成功能

这种类型的集成允许 API 公开 AWS 服务操作。在AWS集成中,需要配置集成请求和集成响应,并设置从方法请求到集成请求、从集成响应到方法响应的必要数据映射。

它允许指定用于此方法的后端集成的详细信息。此扩展是OpenAPI 操作对象的扩展属性。结果是一个API 网关集成对象。

重要财产

与指定后端的集成类型。有效值为:

  • http 或 http_proxy,用于与 HTTP 后端集成。
  • aws_proxy,用于与 AWS Lambda 函数的代理集成
  • aws,用于与 AWS Lambda 函数自定义集成,或与其他 AWS 服务集成,例如 Amazon DynamoDB、Amazon Simple notification Service 或 Amazon Simple Queue Service。
  • mock,用于与 API Gateway 集成,无需调用任何后端。这种类型的集成允许 API Gateway 返回响应,而无需将请求进一步发送到后端。

Lambda 代理集成支持单个 Lambda 函数的简化集成设置。设置很简单,可以随后端一起发展,而无需拆除已经创建的设置。由于这些原因,强烈建议与 Lambda 函数集成。

相比之下,Lambda 自定义集成允许为具有类似输入和输出数据格式要求的各种集成端点重用已配置的映射模板。该设置涉及较多,推荐用于更高级的应用场景。

x-amazon-apigateway-integrations例子

以下示例使用 OpenAPI 标准定义 HTTP API(一个 GET 和一个 POST),并为每个 API 使用一个 API 网关集成来与 Lambda Function 集成(通过引用函数的 ARN)。它使用 AWS Identity and Access Management (IAM) 角色作为集成凭证。以下格式采用 YAML。

复制代码
openapi: 3.0.1
info:
  description: "This is a definition of Proxy Pattern Service. The service has 2 APIs."
  version: v1
  title: "Proxy Pattern Service"
paths:
  /v1/proxypattern/employee:
    options:
      summary: CORS support
      description: |
        Enable CORS by returning correct headers
      consumes:
        - application/json
      produces:
        - application/json
      tags:
        - CORS
      security:
        - NONE: []
      x-amazon-apigateway-integration:
        type: mock
        requestTemplates:
          application/json: |
            {
              "statusCode" : 200
            }
        responses:
          "default":
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Headers : "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-apigw-api-id,X-Amz-Security-Token,Cache-Control'"
              method.response.header.Access-Control-Allow-Methods : "'*'"
              method.response.header.Access-Control-Allow-Origin : "'*'"
            responseTemplates:
              application/json: |
                {}
      responses:
        200:
          description: Default response for CORS method
          headers:
            Access-Control-Allow-Headers:
              type: "string"
            Access-Control-Allow-Methods:
              type: "string"
            Access-Control-Allow-Origin:
              type: "string"
    post:
      summary: "Save Employee"
      operationId: "saveemployees"
      tags:
        - saveemployees
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/employee'
      responses:
        '200':
          description: "Saved Employee Successfully"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/employee"
        '400':
          description: "Application Errors"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

        '500':
          description: "Other unspecified Errors"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

      x-amazon-apigateway-integration:
        credentials:
          Fn::Sub: arn:aws:iam::${AWS::AccountId}:role/delegate-admin-lambda-proxy-pattern-role
        httpMethod: "POST"
        uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProxyPatternService.Arn}:live/invocations
        responses:
          "default":
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin : "'*'"
          "BAD.*":
            statusCode: "400"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin : "'*'"
          "INT.*":
            statusCode: "500"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin : "'*'"
        type: "aws_proxy"
    get:
      summary: "Get All the Employees"
      operationId: "getemployees"
      tags:
        - getemployees
      responses:
        '200':
          description: "All The Employees retrieved successfully"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                type: "array"
                items:
                  $ref: "#/components/schemas/employee"
        '204':
          description: "Employees not found"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
        '500':
          description: "Other unspecified Errors"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

      x-amazon-apigateway-integration:
        credentials:
          Fn::Sub: arn:aws:iam::${AWS::AccountId}:role/delegate-admin-lambda-proxy-pattern-role
        httpMethod: "POST"
        uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProxyPatternService.Arn}:live/invocations
        responses:
          "default":
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin : "'*'"
          "BAD.*":
            statusCode: "204"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin : "'*'"
          "INT.*":
            statusCode: "500"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin : "'*'"
        type: "aws_proxy"

components:
  schemas:
    Employee:
      type: object
      description: EMPLOYEE
      properties:
        employeeId:
          type: string
          description: Id of the Employee
        employeeName:
          type: string
          description: Name of the Employee
      required:
        - employeeId
    ErrorResponse:
      type: "object"
      properties:
        errorCode:
          type: string
          description: |
            indicates an error in processing.
            XXXX - Error in saving message
        errorMessage:
          type: string
          description: "message description of error."

以下是 AWS Lambda 函数的云形成模板,该模板将通过 AWS API Gateway 公开。格式为 YAML。此处,已引用OpenAPI定义/openapi-apigateway-PxyPtrnSvc.yaml文件 ( ),以便API Gateway了解通过 OpenAPI 定义文件定义的集成点。

复制代码
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Serverless Quarkus HTTP - Proxy Pattern Poc Service
Globals:
  Api:
    EndpointConfiguration:
      Type: PRIVATE
    Auth:
      ResourcePolicy:
        CustomStatements: {
          Effect: 'Allow',
          Action: 'execute-api:Invoke',
          Resource: ['execute-api:/*/*/*'],
          Principal: '*'
        }
Resources:
  ProxyPatternServiceAWSApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      TracingEnabled: true
      Name: ProxyPatternServiceSvcApiGateway
      StageName: dev
      DefinitionBody:
        'Fn::Transform':
          Name: 'AWS::Include'
          Parameters:
            Location: './openapi-apigateway-PxyPtrnSvc.yaml'
  ProxyPatternServiceLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: '/aws/lambda/ProxyPatternService'
      RetentionInDays: 30

  ProxyPatternService:
    Type: AWS::Serverless::Function
    Properties:
      Handler: not.used.in.provided.runtime
      Runtime: provided
      CodeUri: component1/function.zip
      MemorySize: 512
      Timeout: 900
      FunctionName: ProxyPatternService
      Environment:
        Variables:
          LOG_LEVEL: INFO
          DISABLE_SIGNAL_HANDLERS: true
      Role: !Sub "arn:aws:iam::${AWS::AccountId}:role/delegate-admin-lambda-proxy-pattern-role"
      Tracing: Active
Outputs:
  ProxyPatternServiceAWSApiGateway:
    Description: 'API Gateway endpoint URL for dev stage for Proxy Pattern Service Template'
    Value: !Sub 'https://${ProxyPatternServiceAWSApiGateway}.execute-api.${AWS::Region}.amazonaws.com/dev/'
  ProxyPatternServiceAWSApiGatewayRestApiId:
    Description: 'API Gateway ARN for Basic AWS API Gateway'
    Value: !Ref ProxyPatternServiceAWSApiGateway
    Export:
      Name: ProxyPatternServiceAWSApiGateway-RestApiId
  ProxyPatternServiceAWSApiGatewayRootResourceId:
    Value: !GetAtt ProxyPatternServiceAWSApiGateway.RootResourceId
    Export:
      Name: ProxyPatternServiceAWSApiGateway-RootResourceId

示例 API 定义可以与以下使用 Java 的 Controller 接口相关,并且可以在 Java Quarkus 中实现:

复制代码
package com.example.proxypattern.controller;

import com.example.proxypattern.exception.model.ErrorResponse;
import com.example.proxypattern.model.Employee;

import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("v1/proxypattern/")
public interface IProxyPatternController {

    @GET
    @Path("/employee")
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(summary = "Get All the Employees", description = "getEmployees")
    @APIResponses(value = {
            @APIResponse(responseCode = "204", description = "Employees not found"),
            @APIResponse(responseCode = "200", description = "All The Employees retrieved successfully", content = @Content(schema = @Schema(implementation = Employee.class))),
            @APIResponse(responseCode = "500", description = "Other unspecified Errors", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) })
    public Response getAllEmployee();

    @POST
    @Path("/employee")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(summary = "Save Employee", description = "saveEmployees")
    @APIResponses(value = {
            @APIResponse(responseCode = "400", description = "Application Errors", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
            @APIResponse(responseCode = "200", description = "Saved Employee Successfully", content = @Content(schema = @Schema(implementation = Employee.class))),
            @APIResponse(responseCode = "500", description = "Other unspecified Errors", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) })
    public Response saveEmployee(Employee employee);
}

实现语言可能有所不同。如果技术决策是使用Java作为编码语言,则可以选择Java Quarkus作为Lambda函数的实现语言。Lambda 支持自定义运行时,因此可以本地构建 Quarkus 代码来解决与 Lambda 冷启动相关的响应时间问题。

控制器实现代码如下所示。这里,业务逻辑已通过业务服务类进行抽象:

复制代码
package com.example.proxypattern.controller.impl;

import com.example.proxypattern.controller.IProxyPatternController;
import com.example.proxypattern.model.Employee;
import com.example.proxypattern.service.impl.EmployeeServiceImpl;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.opentracing.Traced;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.core.Response;import java.util.List;

@Slf4j
@Traced
@Singleton
public class ProxyPatternControllerImpl implements IProxyPatternController {

    @Inject
    EmployeeServiceImpl employeeService;

    @Override
    public Response getAllEmployee() {
        Response.Status status = Response.Status.OK;
        //The business logic is abstracted here in Service code
        List<Employee> listOfEmployee = employeeService.getAllEmployee();
        if(listOfEmployee.isEmpty()){
            status = Response.Status.NO_CONTENT;
        }
        log.info("Sending employee {}", listOfEmployee);
        return Response.status(status).entity(listOfEmployee).build();
    }

    @Override
    public Response saveEmployee(Employee employee) {
    	//The business logic is abstracted here in Service code
        Employee employeeResponse = employeeService.saveEmployee(employee);
        return Response.status(Response.Status.CREATED).entity(employeeResponse).build();
    }
}

可以使用Maven来构建项目,也可以使用以下依赖来构建项目:

复制代码
<dependencies>
      
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy</artifactId>
        </dependency>

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-amazon-lambda-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-openapi</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy-jackson</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-logging-json</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.slf4j</groupId>
            <artifactId>slf4j-jboss-logmanager</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-hibernate-validator</artifactId>
        </dependency>

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-opentracing</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-health</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-junit5</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-junit5-mockito</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jacoco</groupId>
            <artifactId>org.jacoco.agent</artifactId>
            <classifier>runtime</classifier>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

结论

AWS API Gateway 集成可以轻松使用 Lambda Function 实现 REST API,该函数符合 OpenAPI 标准,并且可以轻松与 AWS API Gateway 集成。

相关推荐
A尘埃8 小时前
用户注册(阿里云手机验证码)
阿里云·智能手机·云计算·短信验证码
Y淑滢潇潇18 小时前
RHCSA Linux系统 vim 编辑器
linux·云计算·vim
天青色等艳遇18 小时前
Linux基础6
linux·云计算
星释1 天前
阿里云服务迁移实战: 02-服务器迁移
服务器·阿里云·云计算
云攀登者-望正茂1 天前
使用 Datadog 和 Slack Alerts 监控 AWS EC2
云计算·aws·datadog
孤枫211 天前
aws平台练习B
云计算·aws
阿明观察1 天前
聚焦AI与大模型创新,紫光云如何引领云计算行业快速演进?
人工智能·云计算
Linux运维老纪2 天前
GitLab之搭建(Building GitLab)
linux·mysql·云计算·gitlab·运维开发
云攀登者-望正茂2 天前
通过AWS EKS 生成并部署容器化应用
云原生·kubernetes·aws
zkmall2 天前
阿里云 OSS 在 ZKmall开源商城的应用实践
阿里云·开源·云计算