In the part 8 we introduce concept behind Spring Cloud function and in the part 9 we demonstrated how to develop AWS Lambda with Spring Cloud Function using Java 21 and Spring Boot 3.2. In this article of the series, we'll measure the cold and warm start time including enabling SnapStart on the Lambda function but also applying various priming techniques like priming the DynamoDB invocation and priming (proxying) the whole API Gateway request without going through the network. We'll use Spring Boot 3.2 sample application for our measurements, and for all Lambda functions use JAVA_TOOL_OPTIONS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" and give them Lambda 1024 MB memory.
Let's start with explaining how to enable AWS SnapStart on the Lambda function as it (with priming on top) provides the biggest Lambda performance (especially cold start times) optimization potential. It's is only a matter of configuration:
SnapStart: ApplyOn: PublishedVersions
applied in the Lambda function properties or the global function section of the SAM template. I'd like to dive deeper of how to use various priming techniques on top of SnpaStart for our use case. I explained the ideas behind priming in my article AWS Lambda SnapStart - Measuring priming, end to end latency and deployment time
1) The code for priming of DynamoDB request can be found here.
This class additionally implements import org.crac.Resourceinterface of the CraC project.
With this invocation
Core.getGlobalContext().register(this);
GetProductByIdWithDynamoDBRequestPrimingHandler class registers itself as CRaC resource.
We additionally prime the DynamoDB invocation by implementingbeforeCheckpointmethod from the CRaC API.
@Override public void beforeCheckpoint(org.crac.Context extends Resource> context) throws Exception { productDao.getProduct("0"); }
which we'll invoked during the deployment phase of the Lambda funtiom and before Firecracker microVM snapshot will be taken.
2) The code for priming of the whole API Gateway request can be found here.
This class also additionally implements import org.crac.Resourceinterface as in the example above.
We'll re-use the ugly technique, which I described in my article AWS Lambda SnapStart - Part 6 Priming the request invocation for Java 11 and Micronaut, Quarkus and Spring Boot frameworks . I don't recommend using this technique in production, but it demonstrates the further potentials to reduce the cold start using priming of the whole API Gateway request by pre-loading the mapping between Spring Boot and Spring Cloud Function models and Lambda model performing also DynamoDB invocation priming.
API Gateway request construction of the /products/{id} with id equals to 0 API Gateway JSON request looks like this:
private static String getAPIGatewayRequestMultiLine () { return """ { "resource": "/products/{id}", "path": "/products/0", "httpMethod": "GET", "pathParameters": { "id": "0" }, "requestContext": { "identity": { "apiKey": "blabla" } } } """; }
ThebeforeCheckpointwhen primes (proxies) the whole API Gateway request without going through the network using Spring Cloud FunctionFunctionInvokerclass which invokes its handleRequest method by passing input stream of the API Gateway JSON request constructed above like this:
@Override public void beforeCheckpoint(org.crac.Context extends Resource> context) throws Exception { new FunctionInvoker().handleRequest( new ByteArrayInputStream(getAPIGatewayRequestMultiLine(). getBytes(StandardCharsets.UTF_8)), new ByteArrayOutputStream(), new MockLambdaContext()); }
The results of the experiment below were based on reproducing more than 100 cold and approximately 100.000 warm starts with Lambda function with 1024 MB memory setting for the duration of 1 hour. For it I used the load test tool hey, but you can use whatever tool you want, like Serverless-artillery or Postman.
I ran all these experiments with 4 different scenarios :
1) No SnapStart enabled
In template.yaml use the following configuration:
Handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest #SnapStart: #ApplyOn: PublishedVersions
and we need to invoke Lambda function with name GetProductByIdWithSpringBoot32SCF which is mapped to the GetProductByIdHandler Lambda Handler Java class.
2) SnapStart enabled but no priming applied
In template.yaml use the following configuration:
Handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest SnapStart: ApplyOn: PublishedVersions
and we need to invoke the same Lambda function with name GetProductByIdWithSpringBoot32SCF which is mapped to the GetProductByIdHandler Lambda Handler Java class.
3) SnapStart enabled with DynamoDB invocation priming
In template.yaml use the following configuration:
Handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest SnapStart: ApplyOn: PublishedVersions
and we need to invoke Lambda function with name GetProductByIdWithSpringBoot32SCFAndDynamoDBRequestPriming which is mapped to the GetProductByIdWithDynamoDBRequestPrimingHandler Lambda Handler Java class.
4) SnapStart enabled with API Gateway request invocation priming/proxying
In template.yaml use the following configuration:
Handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest SnapStart: ApplyOn: PublishedVersions
and we need to invoke Lambda function with name
GetProductByIdWithSpringBoot32SCFAndWebRequestPriming which is mapped to the GetProductByIdWithWebRequestPrimingHandler Lambda Handler Java class.
Abbreviationcis for the cold start andwis for the warm start.
Cold (c) and warm (w) start time in ms:
Scenario Number | c p50 | c p75 | c p90 | c p99 | c p99.9 | c max | w p50 | w p75 | w p90 | w p99 | w p99.9 | w max |
---|---|---|---|---|---|---|---|---|---|---|---|---|
No SnapStart enabled | 4768.34 | 4850.11 | 4967.86 | 5248.61 | 5811.92 | 5813.31 | 7.16 | 8.13 | 9.53 | 21.75 | 62.00 | 1367.52 |
SnapStart enabled but no priming applied | 2006.60 | 2065.61 | 2180.17 | 2604.69 | 2662.60 | 2663.54 | 7.45 | 8.40 | 9.92 | 23.09 | 1354.50 | 1496.46 |
SnapStart enabled with DynamoDB invocation priming | 1181.40 | 1263.23 | 1384.90 | 1533.54 | 1661.20 | 1662.17 | 7.57 | 8.73 | 10.83 | 23.83 | 492.37 | 646.18 |
SnapStart enabled with API Gateway request invocation priming | 855.45 | 953.91 | 1107.10 | 1339.97 | 1354.78 | 1355.21 | 8.00 | 9.53 | 12.09 | 26.31 | 163.26 | 547.28 |
By enabling SnapStart on the Lambda function alone, it reduces the cold start time of the Lambda function significantly. By additionally using DynamoDB invocation priming we are be able to achieve cold starts only slightly higher than cold starts described in my article AWS SnapStart -Measuring cold and warm starts with Java 21 using different memory settings where we measured cold and warm starts for the pure Lambda function without the usage of any frameworks including 1024MB memory setting like in our scenario.
Comparing the cold and warm start times we measured with AWS Serverless Java Container in the article Measuring cold and warm starts with AWS Serverless Java Container and Measuring cold and warm starts with AWS Lambda Web Adapter we observe that Spring Cloud Function offers higher cold start times than AWS Lambda Web Adapter but quite comparable cold start times to AWS Serverless Java Container (results vary slightly depending on the percentiles). In terms of warm start/execution times all 3 approaches have quite comparable results when especially looking into the percentiles below 99.9.
以上是AWS Lambda 上的 Spring Boot 应用程序 - 使用 Spring Cloud Function 测量冷启动和热启动部分的详细内容。更多信息请关注PHP中文网其他相关文章!