使用 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 集成。

相关推荐
知孤云出岫6 小时前
云上拼团GO指南——腾讯云博客部署案例,双11欢乐GO
云计算·腾讯云
lihuhelihu7 小时前
第3章 CentOS系统管理
linux·运维·服务器·计算机网络·ubuntu·centos·云计算
dessler8 小时前
Linux系统-ubuntu系统安装
linux·运维·云计算
kongxx9 小时前
AWS S3在客户端应用不能使用aws-sdk场景下的文件上传与下载
aws
kongxx9 小时前
AWS S3 JavaScript SDK(v3)常用操作
aws
Elastic 中国社区官方博客9 小时前
如何将数据从 AWS S3 导入到 Elastic Cloud - 第 3 部分:Elastic S3 连接器
大数据·elasticsearch·搜索引擎·云计算·全文检索·可用性测试·aws
九河云9 小时前
如何选择适合的AWS EC2实例类型
服务器·云计算·aws
Huaqiwill12 小时前
Ubuntun搭建并行计算环境
linux·云计算
为什么这亚子13 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
划水小将军13 小时前
阿里云函数计算GBK编码
阿里云·云计算