知識庫 / Spring / Spring Boot RSS 訂閱

在AWS Lambda中運行Spring Boot應用程序

Cloud,Spring Boot
HongKong
6
11:38 AM · Dec 06 ,2025

1. 概述

本教程將介紹如何使用 Serverless Application Model (SAM) 框架將 Spring Boot 應用程序部署到 AWS Lambda。

我們可能會發現這種方法對將現有的 API 服務器遷移到無服務器架構非常有用。

通過這樣做,我們可以利用 AWS Lambda 的可擴展性和按執行次數計費的定價模型,以高效且經濟的方式運行我們的應用程序。

2. 理解 Lambda 函數

AWS Lambda 是一種由 Amazon Web Services (AWS) 提供的無服務器計算服務。它允許我們運行代碼,而無需配置或管理服務器。

與傳統服務器相比,Lambda 函數的關鍵區別在於 Lambda 函數是事件驅動的,且生命週期非常短

Lambda 函數不是像服務器一樣持續運行,而是僅在響應特定的事件時才運行,例如 API 請求、隊列中的消息或上傳到 S3 的文件。

需要注意的是,Lambda 函數在首次響應請求時需要一些時間啓動,這被稱為“冷啓動”。

如果緊接着的請求在短時間內到達,則可能使用相同的 Lambda 運行時,這被稱為“熱啓動”。如果同時到達多個請求,則會啓動多個 Lambda 運行時。

由於 Spring Boot 的啓動時間相對較長,與 Lambda 理想的毫秒級相比,我們將討論這如何影響性能。

3. 項目設置

現在,讓我們通過修改 pom.xml 並添加一些配置來遷移一個現有的 Spring Boot 項目。

支持的 Spring Boot 版本包括 2.2.x、2.3.x、2.4.x、2.5.x、2.6.x 和 2.7.x。

3.1. 示例 Spring Boot API

我們的應用程序由一個簡單的 API 組成,它處理所有對 GET 請求到 api/v1/users 端點:

@RestController
@RequestMapping("/api/v1/")
public class ProfileController {

    @GetMapping(value = "users", produces = MediaType.APPLICATION_JSON_VALUE)
    public List<User> getUser() {
        return List.of(new User("John", "Doe", "[email protected]"), 
                       new User("John", "Doe", "[email protected]"));
    }
}

它將返回一個包含 User 對象的列表:

public class User {

    private String name;
    private String surname;
    private String emailAddress;

    //standard constructor, getters and setters
}

讓我們啓動我們的應用程序並調用API:

$ java -jar app.jar
$ curl -X GET http://localhost:8080/api/v1/users -H "Content-Type: application/json"

API響應如下:

[
   {
      "name":"John",
      "surname":"Doe",
      "email":"[email protected]"
   },
   {
      "name":"John",
      "surname":"Doe",
      "email":"[email protected]"
   }
]

3.2. 將 Spring Boot 應用程序轉換為 Lambda 運行包(通過 Maven)

為了在 Lambda 上運行我們的應用程序,請將 aws-serverless-java-container-springboot2 依賴項添加到我們的 pom.xml 文件中:

<dependency>
    <groupId>com.amazonaws.serverless</groupId>
    <artifactId>aws-serverless-java-container-springboot2</artifactId>
    <version>${springboot2.aws.version}</version>
</dependency>

然後,我們將添加 maven-shade-plugin 並移除 spring-boot-maven-plugin

Maven Shade Plugin 用於創建帶陰影的(或 uber) JAR 文件。 帶陰影的 JAR 文件是一個包含其所有依賴項的自包含可執行 JAR 文件,從而使其可以獨立運行:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.3.0</version>
    <configuration>
        <createDependencyReducedPom>false</createDependencyReducedPom>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <artifactSet>
                    <excludes>
                        <exclude>org.apache.tomcat.embed:*</exclude>
                    </excludes>
                 </artifactSet>
            </configuration>
         </execution>
     </executions>
 </plugin>

總而言之,這種配置將在 Maven 構建的打包階段生成一個帶陰影的 JAR 文件。

該 JAR 文件將包含 Spring Boot 通常打包的所有類和資源,但不包括 Tomcat 的那些。我們不需要為使用 AWS Lambda 而運行嵌入式 Web 容器。

4. Lambda 處理程序

創建實現 <em RequestHandler</em> 接口的類。

<em RequestHandler</em> 是一個定義單個方法 <em handleRequest</em> 的接口。根據我們構建的 Lambda 類型,有幾種處理請求的方式。

在本例中,我們正在處理來自 API Gateway 的請求,因此我們可以使用 <em RequestHandler<AwsProxyRequest, AwsProxyResponse></em> 版本,其中輸入是 API Gateway 請求,響應是 API Gateway 響應。

由 AWS 提供的 Spring Boot 服務器端無服務器庫為我們提供了一個特殊的 <em SpringBootLambdaContainerHandler</em> 類,用於通過 Spring 處理 API 調用,從而使 Spring Boot API 服務器代碼庫的行為類似於 Lambda。

4.1. 啓動時機

需要注意的是,在 AWS Lambda 中,初始化階段有時間限制,限制為 10 秒

如果我們的應用程序花費時間超過這個限制,AWS Lambda 將會超時並嘗試啓動一個新的 Lambda 運行時。

根據我們的 Spring Boot 應用程序啓動速度,我們可以選擇以下兩種初始化 Lambda 處理器的方式:

  • 同步 – 在我們的應用程序的啓動時間遠小於時間限制的情況下
  • 異步 – 在我們的應用程序的啓動時間可能較長的情況下

4.2. 同步初始化

讓我們為我們的 Spring Boot 項目定義一個新的處理程序:

public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
    private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;

    static {
        try {
            handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class); }
        catch (ContainerInitializationException ex){
            throw new RuntimeException("Unable to load spring boot application",ex); }
    }

    @Override
    public AwsProxyResponse handleRequest(AwsProxyRequest input, Context context) {
        return handler.proxy(input, context);
    }
}

我們正在使用 SpringBootLambdaContainerHandler 處理 API Gateway 請求並將其傳遞到我們的應用程序上下文中。我們初始化這個處理器的操作在 LambaHandler 類的靜態構造函數中進行,並且從 handleRequest 函數中調用它。

處理對象隨後調用 Spring Boot 應用程序中的適當方法來處理請求並生成響應。最後,它將響應返回給 Lambda 運行時,以便將其傳遞迴 API Gateway。

讓我們通過 Lambda 處理器來調用我們的 API:

@Test
void whenTheUsersPathIsInvokedViaLambda_thenShouldReturnAList() throws IOException {
    LambdaHandler lambdaHandler = new LambdaHandler();
    AwsProxyRequest req = new AwsProxyRequestBuilder("/api/v1/users", "GET").build();
    AwsProxyResponse resp = lambdaHandler.handleRequest(req, lambdaContext);
    Assertions.assertNotNull(resp.getBody());
    Assertions.assertEquals(200, resp.getStatusCode());
}

4.3. 異步初始化

有時 Spring Boot 應用程序啓動可能較慢。這是因為在啓動過程中,Spring 引擎會構建上下文,掃描並初始化代碼庫中的所有 Bean。

此過程可能會影響啓動時間,並在無服務器環境中造成許多問題。

為了解決此問題,我們可以定義一個新的處理程序:

public class AsynchronousLambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
    private SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;

    public AsynchronousLambdaHandler() throws ContainerInitializationException {
        handler = (SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse>) 
          new SpringBootProxyHandlerBuilder()
            .springBootApplication(Application.class)
            .asyncInit()
            .buildAndInitialize();
    }

    @Override
    public AwsProxyResponse handleRequest(AwsProxyRequest input, Context context) {
        return handler.proxy(input, context);
    }
}

此方法與之前的方法相似。在此實例中,SpringBootLambdaContainerHandler 在請求處理器的對象構造函數中構建,而不是使用static構造函數。因此,它在 Lambda 的啓動階段執行的階段不同。

5. 部署應用程序

AWS SAM(Serverless Application Model)是一個開源框架,用於在 AWS 上構建無服務器應用程序。

在為我們的 Spring Boot 應用程序定義 Lambda 處理程序之後,我們需要使用 SAM 準備所有組件以進行部署。

5.1. SAM 模板

SAM 模板(SAM YAML)是一個 YAML 格式的文件,用於定義部署無服務器應用程序所需的 AWS 資源。 它提供了一種聲明式的方式來指定無服務器應用程序的配置。

因此,我們來定義我們的 template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 30

Resources:
  ProfileApiFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: .
      Handler: com.baeldung.aws.handler.LambdaHandler::handleRequest
      Runtime: java11
      AutoPublishAlias: production
      SnapStart:
        ApplyOn: PublishedVersions
      Architectures:
        - x86_64
      MemorySize: 2048
      Environment: 
        Variables:
          JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1 
      Events:
        HelloWorld:
          Type: Api 
          Properties:
            Path: /{proxy+}
            Method: ANY

我們來討論一下配置中的各個字段:

  • type – 指示此資源是使用 AWS::Serverless::Function 資源類型的 AWS Lambda 函數。
  • coreUri – 指定函數代碼的位置。
  • AutoPublishAlias – 指定 AWS Lambda 在自動發佈函數新版本時應使用的別名。
  • Handler – 指定 Lambda 處理程序類。
  • Events – 指定觸發 Lambda 函數的事件。
  • Type – 指示這是一個 Api 事件源。
  • Properties – 對於 API 事件,它定義了 API Gateway 應響應的 HTTP 方法和路徑。

5.2. SAM 部署

現在,我們將應用程序部署為 AWS Lambda。

第一步是下載並安裝 AWS CLI,然後安裝 AWS SAM CLI

讓我們在 template.yaml 所在的路徑上運行 AWS SAM CLI,並執行以下命令:

$ sam build

當我們運行此命令時,AWS SAM CLI 將打包並構建我們的 Lambda 函數的源代碼和依賴項,生成一個 ZIP 文件,作為我們的部署包。

讓我們在本地部署我們的應用程序:

$ sam local start-api

接下來,讓我們通過使用 sam local 觸發正在運行的 Spring Boot 服務:

$ curl localhost:3000/api/v1/users

API響應與之前相同:

[
   {
      "name":"John",
      "surname":"Doe",
      "email":"[email protected]"
   },
   {
      "name":"John",
      "surname":"Doe",
      "email":"[email protected]"
   }
]

我們還可以將其部署到 AWS:

$ sam deploy

6. 使用 Spring 在 Lambda 中的侷限性

雖然 Spring 是一種強大的、靈活的框架,可用於構建複雜且可擴展的應用程序,但在 Lambda 環境中可能並非最佳選擇。

主要原因是 Lambda 函數旨在成為小型、單用途函數,能夠快速高效地執行

6.1. 冷啓動 (Cold Start)

AWS Lambda 函數的冷啓動時間是指在處理事件之前,初始化函數環境所需的時間。

影響 Lambda 函數冷啓動性能的因素包括:

  • 包大小 – 包大小越大,初始化時間越長,冷啓動速度也會越慢。
  • 初始化時間 – Spring Framework 初始化和設置應用程序上下文所需的時間。 這包括初始化任何依賴項,例如數據庫連接、HTTP 客户端或緩存框架。
  • 自定義初始化邏輯 – 儘量減少自定義初始化邏輯,並確保其針對冷啓動進行優化。

我們可以使用 Lambda SnapStart 來改善啓動時間。

6.2. 數據庫連接池

在如 AWS Lambda 這種按需執行的無服務器環境中,維護連接池可能具有挑戰性。

當事件觸發 Lambda 函數時,AWS Lambda 引擎可以創建一個新的應用程序實例。在請求之間,運行時可能會停頓或終止。

許多連接池持有開放的連接。這可能導致混淆或錯誤,因為池在暖啓動後會被重新使用,並且對於某些數據庫引擎來説,它可能導致資源泄漏。簡而言之,標準連接池依賴於服務器持續運行並維護連接。

為了解決這個問題,AWS 提供了一種名為 RDS Proxy 的解決方案,它為 Lambda 函數提供連接池服務。

通過使用 RDS Proxy,Lambda 函數可以連接到數據庫,而無需維護自己的連接池。

7. 結論

在本文中,我們學習瞭如何將現有的 Spring Boot API 應用程序轉換為 AWS Lambda 函數。

我們考察了 AWS 提供的用於此目的的庫。 此外,我們還考慮了 Spring Boot 較慢的啓動時間可能會如何影響我們的配置方式。

然後,我們查看了如何部署 Lambda 函數並使用 SAM CLI 進行測試。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.