Spring Boot 冷启动在 AWS Lambda 上花费的时间太长,并且启动过程会执行两次。

huangapple go评论117阅读模式
英文:

spring boot cold start taking too long on aws lambda and boot initializes twice

问题

这很奇怪,但是我的 Spring Boot API 在部署到 AWS Lambda 上后,执行时间比预期要长得多。

在 CloudWatch 日志中,我看到 Spring Boot 启动了两次,首次使用默认配置文件,其次使用我设置的配置文件。

为什么会启动两次呢?这显著增加了时间成本。

源代码:
lambdahandler.java

  1. public class LambdaHandler implements RequestStreamHandler {
  2. private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
  3. static {
  4. try {
  5. handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
  6. handler.activateSpringProfiles("lambda");
  7. } catch (ContainerInitializationException e) {
  8. // Re-throw the exception to force another cold start
  9. e.printStackTrace();
  10. throw new RuntimeException("Could not initialize Spring Boot application", e);
  11. }
  12. }
  13. }

application.java

  1. @SpringBootApplication
  2. public class Application extends SpringBootServletInitializer {
  3. public static void main(String[] args) {
  4. SpringApplication.run(Application.class, args);
  5. }
  6. }

这两个文件位于同一个包中。

config.java

  1. @Configuration
  2. @EnableWebMvc
  3. @Profile("lambda")
  4. public class Config {
  5. /**
  6. * 创建所需的 HandlerMapping,以避免创建多个默认的 HandlerMapping 实例
  7. */
  8. @Bean
  9. public HandlerMapping handlerMapping() {
  10. return new RequestMappingHandlerMapping();
  11. }
  12. /**
  13. * 创建所需的 HandlerAdapter,以避免创建多个默认的 HandlerAdapter 实例
  14. */
  15. @Bean
  16. public HandlerAdapter handlerAdapter() {
  17. return new RequestMappingHandlerAdapter();
  18. }
  19. // ...
  20. // ...
  21. }

pom.xml

  1. <dependency>
  2. <groupId>com.amazonaws.serverless</groupId>
  3. <artifactId>aws-serverless-java-container-spring</artifactId>
  4. <version>[0.1,)</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.amazonaws</groupId>
  8. <artifactId>aws-lambda-java-core</artifactId>
  9. <version>1.2.1</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>com.amazonaws</groupId>
  13. <artifactId>aws-lambda-java-events</artifactId>
  14. <version>3.1.0</version>
  15. </dependency>

CloudWatch 日志:

  1. 07:16:51.546 [main] INFO com.amazonaws.serverless.proxy.internal.LambdaContainerHandler - Starting Lambda Container Handler
  2. :: Spring Boot ::
  3. 2020-09-05 07:16:52.724 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Starting LambdaRTEntry on 169.254.184.173 with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1051 in /)
  4. 2020-09-05 07:16:52.726 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : No active profile set, falling back to default profiles: default
  5. 2020-09-05 07:16:52.906 INFO 1 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1e81f4dc: startup date [Sat Sep 05 07:16:52 UTC 2020]; root of context hierarchy
  6. ...
  7. ...
  8. 2020-09-05 07:16:57.222 INFO 1 --- [ main] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 40 ms
  9. :: Spring Boot ::
  10. 2020-09-05 07:16:57.442 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Starting LambdaRTEntry on 169.254.184.173 with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1051 in /)
  11. 2020-09-05 07:16:57.442 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : The following profiles are active: lambda
  12. 2020-09-05 07:16:57.445 INFO 1 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5ef60048: startup date [Sat Sep 05 07:16:57 UTC 2020]; root of context hierarchy
英文:

this is strange but my spring boot api taking much longer that expected when deployed on aws lambda.
in the cloudwatch log, i see spring boot is starting up twice first with default profile and second with a profile i set.
Why should it boot twice.. that is significantly costing time..
Source Code:
lambdahandler.java

  1. public class LambdaHandler implements RequestStreamHandler {
  2. private static SpringBootLambdaContainerHandler&lt;AwsProxyRequest, AwsProxyResponse&gt; handler;
  3. static {
  4. try {
  5. handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
  6. handler.activateSpringProfiles(&quot;lambda&quot;);
  7. } catch (ContainerInitializationException e) {
  8. // Re-throw the exception to force another cold start
  9. e.printStackTrace();
  10. throw new RuntimeException(&quot;Could not initialize Spring Boot application&quot;, e);
  11. }
  12. }

application.java

  1. @SpringBootApplication
  2. public class Application extends SpringBootServletInitializer {
  3. public static void main(String[] args) {
  4. SpringApplication.run(Application.class, args);
  5. }

both these files are in the same package

config.java

  1. @Configuration
  2. @EnableWebMvc
  3. @Profile(&quot;lambda&quot;)
  4. public class Config {
  5. /**
  6. * Create required HandlerMapping, to avoid several default HandlerMapping instances being created
  7. */
  8. @Bean
  9. public HandlerMapping handlerMapping() {
  10. return new RequestMappingHandlerMapping();
  11. }
  12. /**
  13. * Create required HandlerAdapter, to avoid several default HandlerAdapter instances being created
  14. */
  15. @Bean
  16. public HandlerAdapter handlerAdapter() {
  17. return new RequestMappingHandlerAdapter();
  18. }
  19. ..
  20. ..
  21. }

pom.xml

  1. &lt;dependency&gt;
  2. &lt;groupId&gt;com.amazonaws.serverless&lt;/groupId&gt;
  3. &lt;artifactId&gt;aws-serverless-java-container-spring&lt;/artifactId&gt;
  4. &lt;version&gt;[0.1,)&lt;/version&gt;
  5. &lt;/dependency&gt;
  6. &lt;dependency&gt;
  7. &lt;groupId&gt;com.amazonaws&lt;/groupId&gt;
  8. &lt;artifactId&gt;aws-lambda-java-core&lt;/artifactId&gt;
  9. &lt;version&gt;1.2.1&lt;/version&gt;
  10. &lt;/dependency&gt;
  11. &lt;dependency&gt;
  12. &lt;groupId&gt;com.amazonaws&lt;/groupId&gt;
  13. &lt;artifactId&gt;aws-lambda-java-events&lt;/artifactId&gt;
  14. &lt;version&gt;3.1.0&lt;/version&gt;
  15. &lt;/dependency&gt;

cloudwatch log

  1. 07:16:51.546 [main] INFO com.amazonaws.serverless.proxy.internal.LambdaContainerHandler - Starting Lambda Container Handler
  2. :: Spring Boot ::
  3. 2020-09-05 07:16:52.724 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Starting LambdaRTEntry on 169.254.184.173 with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1051 in /)
  4. 2020-09-05 07:16:52.726 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : No active profile set, falling back to default profiles: default
  5. 2020-09-05 07:16:52.906 INFO 1 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1e81f4dc: startup date [Sat Sep 05 07:16:52 UTC 2020]; root of context hierarchy
  6. ..
  7. ..
  8. 2020-09-05 07:16:57.222 INFO 1 --- [ main] o.s.web.servlet.DispatcherServlet : FrameworkServlet &#39;dispatcherServlet&#39;: initialization completed in 40 ms
  9. :: Spring Boot ::
  10. 2020-09-05 07:16:57.442 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Starting LambdaRTEntry on 169.254.184.173 with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1051 in /)
  11. 2020-09-05 07:16:57.442 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : The following profiles are active: lambda
  12. 2020-09-05 07:16:57.445 INFO 1 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5ef60048: startup date [Sat Sep 05 07:16:57 UTC 2020]; root of context hierarchy

答案1

得分: 2

为什么需要启动两次?

我怀疑您的代码更改与 activateSpringProfiles 强制重新初始化有关。

  1. handler.activateSpringProfiles(&quot;lambda&quot;);

尝试通过将活动配置文件与环境变量 SPRING_PROFILES_ACTIVE 一起设置为 Lambda 配置文件的一部分。

Java 与无服务器

如果您将 Java 用于像 AWS Lambda 这样的无服务器应用程序,我建议寻找一个支持提前编译的框架,这将极大地提升应用程序的启动速度。

例如,可以查看 Micronaut、Quarkus 并与 Graalvm 一起使用。
Spring Boot 不是与 AWS Lambda 直接配合使用的最佳选项。

英文:

Why should it boot twice ?

I suspect your code change with activateSpringProfiles force reinitialisation.

  1. handler.activateSpringProfiles(&quot;lambda&quot;);

https://github.com/awslabs/aws-serverless-java-container/blob/master/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java#L149

Try setting active profile with env variable SPRING_PROFILES_ACTIVE as part of lambda configuration file.

Java and serverless

If you use java for serverless application like AWS lambdas I would recommend for looking a framework which supports Ahead-of-Time compilation which will boost a lot your application start.

For instance have a look at Micronaut, Quarkus using with Graalvm.
Spring Boot is not the best option using with directly with AWS lambdas.

huangapple
  • 本文由 发表于 2020年9月5日 15:44:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/63751588.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定