Spring Boot has become the de facto standard for creating Microservices in Java. Since it’s widely used, Spring Boot has excellent community support. Traditionally, the Spring Boot application is used in a container environment (e.g., EC2 or VM). However, if you’re planning to use an existing Spring Boot application in a serverless environment like AWS Lambda, you will be shocked. Size limitations for AWS Lambda will be the first hurdle because the size of the Spring Boot including all the dependencies as a single uber jar should be less than 50MB.
Apart from this, running a Spring Boot application in a serverless environment like AWS Lambda would be a nightmare due to the cold start issues. Micronaut framework is the perfect alternative in a situation like this. This blog explains what a Cold Start problem is and describes some techniques to resolve it.
In days gone by, cars would not start in cold weather because an engine that sat idle for a long time must be brought up to a normal temperature first. Modern-day cars fix the Cold Start problem by having an additional Engine Starter. In the serverless world, an application should start up quickly and service the request. For example, AWS Lambda has a max time limit of ten seconds for an application to start up. If the Spring Boot application has lots of dependencies, it won’t start in ten seconds, triggering AWS to re-try (after ten seconds) and ultimately fail after multiple attempts.
Spring Boot Startup
The main class in a Spring Boot is the entry point to start the application. When the Spring Boot application comes up, it does a component scan across all the packages and starts to load all the classes in its class path. Scanning several classes in multiple package paths is time-consuming and can literally take ten seconds are more. If you’re using Maven, check all the dependencies and remove unwanted dependencies. You can find similar tasks in Gradle, too.
AWS Serverless Java Container Spring Boot 2
AWS Labs solved the cold start problems in Spring Boot 2. As a first step, include
'com.amazonaws.serverless:aws-serverless-java-container-springboot2:1.4' in your project. This is a lightweight dependency with very few classes. Next, create a class of type
com.amazonaws.services.lambda.runtime.RequestStreamHandler. Make sure you start the handler in an asynchronous way in a static block or in a Constructor. This is to trick the AWS Lambda that the initialization phase is done, even though the Spring Boot app is still coming up in the background. The screenshot below illustrates one such implementation.
If you’re using Serverless framework for your function configuration, your handler would be the single handler that you created in the previous step. For all the functions, the entry point will be the same as shown in the below screenshot.
Spring Beans Lazy Initialization
When the Spring Boot application comes up, it eagerly initializes all the beans for auto-wiring. This is a nice feature that can show any problems with the beans during startup. However, this process takes considerable time, depending upon how many beans you have. To improve this process, Spring Framework provides
org.springframework:spring-context-indexer:5.0.3.RELEASE to speed up the process through a specialized indexer.
JPA repository initialization can be time consuming if you have lots of DB objects. Spring provides the below properties to delay this.
DB Connection Pool
Spring Framework packages with HikariCP for database connection pooling. The default settings for the number of connections would be very high—which you may not need at all in the AWS Lambda.
Depending on the configuration, connection pooling also takes considerable time to initialize the pool.
Spring Cloud Function to the Rescue
One other option is to use Spring Cloud Function. Spring Cloud Function is the Spring Framework’s solution for the serverless world. Spring Cloud Function provides adapters for various cloud providers, including AWS, Microsoft, Google, etc. Look out for Lambda, Azure adapters in the documentation. Spring Cloud Functions are a clean way of creating functions for the serverless environment. However, note that you would not have access to the Dependency Injection of the existing Spring projects.
As discussed above, Spring Boot is not designed for a serverless environment. However, this may change as there is active discussion in the Spring Community around creating a lightweight solution for the serverless world. Finally, if you still have any questions about creating Microservices in Java, please don’t hesitate to reach out to us at any time. We’d love to help you get started.