如何让AWS Lambda支持Spring Boot应用部署与运行报错排查
问题背景
目标
现有一个Spring Boot应用,需要适配AWS Lambda运行。
需求
构建可直接上传至Lambda函数的jar包,通过API Gateway调用该函数时,返回和本地执行java -jar启动应用完全一致的响应结果。
已完成配置
- 已编写
@RestController接口,同时创建Lambda请求处理类StreamLambdaHandler.java,代码如下:
package mypackage; import com.amazonaws.serverless.exceptions.ContainerInitializationException; import com.amazonaws.serverless.proxy.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.model.AwsProxyResponse; import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class StreamLambdaHandler implements RequestStreamHandler { private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler; static { try { handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(MyClass.class); // MyClass是应用中执行SpringApplication.run的启动类 } catch (ContainerInitializationException e) { e.printStackTrace(); throw new RuntimeException("Could initialize Spring framework", e); } } @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { handler.proxyStream(inputStream, outputStream, context); outputStream.close(); } }
- 已将该类的
handleRequest方法配置为Lambda函数执行入口,配置截图 - 使用AWS SAM部署,编写的
sam.yaml配置如下:
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: AWS Serverless Spring Boot API - Spring Boot API PrycePick Resources: SpringbootSampleFunction: Type: AWS::Serverless::Function Properties: Handler: pryce_pick.search.StreamLambdaHandler::handleRequest Runtime: java11 CodeUri: out/artifacts/myfunction/my.zip MemorySize: 512 Policies: AWSLambdaBasicExecutionRole Timeout: 30 Events: GetResource: Type: Api Properties: Path: /{proxy+} Method: any Outputs: SpringbootSampleApi: Description: URL for application Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' Export: Name: SpringbootApp
- 执行的部署命令如下:
aws cloudformation package --template-file sam.yaml --output-template-file output-sam.yaml --s3-bucket mybucket aws cloudformation deploy --template-file output-sam.yaml --stack-name bookApi --capabilities CAPABILITY_IAM
故障现象
构建包含全量依赖的jar包后,在AWS控制台Lambda测试界面验证:
对应报错日志片段:
ationTypeMapping.java:102) at org.springframework.core.annotation.AnnotationTypeMappings.addIfPossible(AnnotationTypeMappings.java:112) at org.springframework.core.annotation.AnnotationTypeMappings.addIfPossible(AnnotationTypeMappings.java:105) at org.springframework.core.annotation.AnnotationTypeMappings.addMetaAnnotationsToQueue(AnnotationTypeMappings.java:99) at org.springframework.core.annotation.AnnotationTypeMappings.addAllMappings(AnnotationTypeMappings.java:79) at org.springframework.core.annotation.AnnotationTypeMappings.<init>(AnnotationTypeMappings.java:68) at org.springframework.core.annotation.AnnotationTypeMappings.<init>(AnnotationTypeMappings.java:46) at org.springframework.core.annotation.AnnotationTypeMappings$Cache.createMappings(AnnotationTypeMappings.java:245) at java.base/java.util.concurrent.ConcurrentMap.computeIfAbsent(Unknown Source) at org.springframework.core.annotation.AnnotationTypeMappings$Cache.get(AnnotationTypeMappings.java:241) at org.springframework.core.annotation.AnnotationTypeMappings.forAnnotationType(AnnotationTypeMappings.java:199) at org.springframework.core.annotation.AnnotationTypeMappings.forAnnotationType(AnnotationTypeMappings.java:182) at org.springframework.core.annotation.AnnotationTypeMappings.forAnnotationType(AnnotationTypeMappings.java:169) at org.springframework.core.annotation.TypeMappedAnnotations$IsPresent.doWithAnnotations(TypeMappedAnnotations.java:330) at org.springframework.core.annotation.TypeMappedAnnotations$IsPresent.doWithAnnotations(TypeMappedAnnotations.java:279) at org.springframework.core.annotation.AnnotationsScanner.processClassHierarchy(AnnotationsScanner.java:188) at org.springframework.core.annotation.AnnotationsScanner.processClassHierarchy(AnnotationsScanner.java:171) at org.springframework.core.annotation.AnnotationsScanner.processClass(AnnotationsScanner.java:106) at org.springframework.core.annotation.AnnotationsScanner.process(AnnotationsScanner.java:86) at org.springframework.core.annotation.AnnotationsScanner.scan(AnnotationsScanner.java:77) at org.springframework.core.annotation.TypeMappedAnnotations.scan(TypeMappedAnnotations.java:242) at org.springframework.core.annotation.TypeMappedAnnotations.isPresent(TypeMappedAnnotations.java:98) at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:144) at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:97) at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169) at com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler.handleRequest(SpringBootLambdaContainerHandler.java:132) at com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler.handleRequest(SpringBootLambdaContainerHandler.java:52) at com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.proxy(LambdaContainerHandler.java:163) at com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.proxyStream(LambdaContainerHandler.java:198) at mypackage.StreamLambdaHandler.handleRequest(StreamLambdaHandler.java:29) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at lambdainternal.EventHandlerLoader$StreamMethodRequestHandler.handleRequest(EventHandlerLoader.java:375) at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:899) at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:262) at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:199) at lambdainternal.AWSLambda.main(AWSLambda.java:193) END RequestId: 29d504dc-3510-4d78-b010-3fe48a4e6f13 REPORT RequestId: 29d504dc-3510-4d78-b010-3fe48a4e6f13 Duration: 2278.06 ms Billed Duration: 2279 ms Memory Size: 512 MB Max Memory Used: 106 MB Init Duration: 940.24 ms
解决方案
报错是依赖版本不匹配+配置路径错误+打包方式错误共同导致的,按以下步骤修改即可:
- 修正适配包依赖
日志里的注解扫描异常,是因为引错了aws-serverless的适配包。使用Spring Boot 2.x必须引入aws-serverless-java-container-springboot2依赖,Spring Boot 3.x对应引入aws-serverless-java-container-springboot3,不要使用不带版本号的通用spring包,该包仅兼容Spring Boot 1.x,和高版本Spring搭配必然出现注解扫描错误。Java11+Spring Boot 2.7.x环境下,适配包选1.9.3版本即可,Maven依赖示例:<dependency> <groupId>com.amazonaws.serverless</groupId> <artifactId>aws-serverless-java-container-springboot2</artifactId> <version>1.9.3</version> </dependency> - 修正Handler配置路径
代码里StreamLambdaHandler属于mypackage包,但sam.yaml中配置的Handler路径是pryce_pick.search.StreamLambdaHandler::handleRequest,包名完全不匹配,Lambda无法加载到正确的处理类,修改为实际包名对应的路径:Handler: mypackage.StreamLambdaHandler::handleRequest - 修正打包方式
不要把jar包额外压缩成zip上传,Lambda原生支持Spring Boot打出来的fat jar。使用Spring Boot官方maven插件打包,生成的jar包直接作为CodeUri即可,不需要二次压缩。打包插件配置示例:
执行<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.7.12</version> <configuration> <mainClass>mypackage.MyClass</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>mvn clean package后,直接将target目录下生成的jar路径配置为CodeUri即可。 - 冷启动优化(可选)
在application.properties中增加配置,避免Spring启动时初始化内置Tomcat,缩短冷启动时间:spring.main.web-application-type=none
以上配置修改完成后重新打包部署,接口即可正常响应,返回结果和本地java -jar启动完全一致。
内容的提问来源于stack exchange,提问作者אמיר שקדי
相关产品推荐
相关产品推荐

