英文:
spring boot cold start taking too long on aws lambda and boot initializes twice
问题
这很奇怪,但是我的 Spring Boot API 在部署到 AWS Lambda 上后,执行时间比预期要长得多。
在 CloudWatch 日志中,我看到 Spring Boot 启动了两次,首次使用默认配置文件,其次使用我设置的配置文件。
为什么会启动两次呢?这显著增加了时间成本。
源代码:
lambdahandler.java
public class LambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
handler.activateSpringProfiles("lambda");
} catch (ContainerInitializationException e) {
// Re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}
}
application.java
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这两个文件位于同一个包中。
config.java
@Configuration
@EnableWebMvc
@Profile("lambda")
public class Config {
/**
* 创建所需的 HandlerMapping,以避免创建多个默认的 HandlerMapping 实例
*/
@Bean
public HandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
/**
* 创建所需的 HandlerAdapter,以避免创建多个默认的 HandlerAdapter 实例
*/
@Bean
public HandlerAdapter handlerAdapter() {
return new RequestMappingHandlerAdapter();
}
// ...
// ...
}
pom.xml
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-spring</artifactId>
<version>[0.1,)</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>3.1.0</version>
</dependency>
CloudWatch 日志:
07:16:51.546 [main] INFO com.amazonaws.serverless.proxy.internal.LambdaContainerHandler - Starting Lambda Container Handler
:: Spring Boot ::
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 /)
2020-09-05 07:16:52.726 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : No active profile set, falling back to default profiles: default
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
...
...
2020-09-05 07:16:57.222 INFO 1 --- [ main] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 40 ms
:: Spring Boot ::
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 /)
2020-09-05 07:16:57.442 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : The following profiles are active: lambda
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
public class LambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
handler.activateSpringProfiles("lambda");
} catch (ContainerInitializationException e) {
// Re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}
application.java
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
both these files are in the same package
config.java
@Configuration
@EnableWebMvc
@Profile("lambda")
public class Config {
/**
* Create required HandlerMapping, to avoid several default HandlerMapping instances being created
*/
@Bean
public HandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
/**
* Create required HandlerAdapter, to avoid several default HandlerAdapter instances being created
*/
@Bean
public HandlerAdapter handlerAdapter() {
return new RequestMappingHandlerAdapter();
}
..
..
}
pom.xml
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-spring</artifactId>
<version>[0.1,)</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>3.1.0</version>
</dependency>
cloudwatch log
07:16:51.546 [main] INFO com.amazonaws.serverless.proxy.internal.LambdaContainerHandler - Starting Lambda Container Handler
:: Spring Boot ::
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 /)
2020-09-05 07:16:52.726 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : No active profile set, falling back to default profiles: default
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
..
..
2020-09-05 07:16:57.222 INFO 1 --- [ main] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 40 ms
:: Spring Boot ::
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 /)
2020-09-05 07:16:57.442 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : The following profiles are active: lambda
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 强制重新初始化有关。
handler.activateSpringProfiles("lambda");
尝试通过将活动配置文件与环境变量 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.
handler.activateSpringProfiles("lambda");
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论