Using Amazon Corretto (OpenJDK) for lean, fast, and efficient Amazon Lambda Applications

海外精选
海外精选的内容汇集了全球优质的亚马逊云科技相关技术内容。同时,内容中提到的“AWS” 是 “Amazon Web Services” 的缩写,在此网站不作为商标展示。
0
0
{"value":"#### **Using [Amazon Corretto](https://aws.amazon.com/cn/corretto/?trk=cndc-detail) (OpenJDK) for lean, fast, and efficient [AWS Lambda](https://aws.amazon.com/cn/lambda/?trk=cndc-detail)**\n***By Guest Blogger [Adam Bien](https://adam-bien.com/)***\n\nIn this post, I will discuss how you can launch large, monolithic applications on top of [AWS Lambda](https://aws.amazon.com/lambda/), and I’ll show that they perform well and are cost effective. You’ll learn that the same application you develop for Lambda can be run locally or deployed across your favorite AWS container service, without modifications. Furthermore, I’ll introduce you to running [Amazon Corretto](https://aws.amazon.com/corretto) on ARM64 using Lambda’s Graviton2-based offering. Corretto, Amazon’s version of OpenJDK, is my go-to option, especially for ARM64, because it lets you run a Java Lambda on Graviton processors efficiently and cost-effectively. I’ll be using it to demonstrate how you can use it with [Quarkus](https://quarkus.io/) to build microservices quickly and at low cost.\n\n##### **Built-in functionality over external dependencies**\nJava’s dynamic dependency mechanism was introduced in 2009 via “[JSR 330:Dependency Injection for Java](https://jcp.org/en/jsr/detail?id=330)” and is often used with [MicroProfile](https://microprofile.io/) or [Jakarta EE](https://jakarta.ee/).\n\nFor those unfamiliar with dependency injection, consider it like updating your classes’ instance variables dynamically via a service. This is accomplished through annotations that you place in your code, e.g., ```@Inject.``` Therefore, rather than instantiating an instance variable yourself in your class, you would ask a service to do it for you. This makes your class more maintainable. The instance that you inject into your class from the external service is loosely coupled with your client class, which lets you maintain your code more easily. Moreover, you can more easily develop test cases, because you can inject variables into your classes based on configuration files without recompiling. In addition, since the classes are loosely coupled, they become easier to test in isolation. A MicroProfile runtime combined with Java’s built-in class libraries lets you make external dependencies optional and increases productivity with fast iterations and short deployment times. Using dependency injection also lets you focus on developing your app. And this happens without having to worry about the boilerplate code that you would ordinarily need to implement many times, repetitively, with no differentiated and additive benefit.\n\nHowever, even a small Java microservice can become a large Lambda. The following code illustrates a typical Create, Read, Update, Delete (CRUD) operation in Java that’s comprised of a few injected classes:\t\n\n```\\nimport javax.json.*;\\nimport javax.ws.rs.*;\\nimport javax.ws.rs.core.*;\\n\\npublic class CRUDResource {\\n\\n @Inject\\n CRUDStore store;\\n\\n @GET\\n @Path(\\"{id}\\")\\n public JsonObject fetch(@PathParam(\\"id\\") String id){\\n var result = this.store.fetch(id);\\n //....\\n }\\n\\n @GET\\n public JsonArray findAll(){}\\n\\n @DELETE\\n @Path(\\"{id}\\")\\n public void delete(@PathParam(\\"id\\") String id){}\\n\\n @DELETE\\n public void deleteAll() {}}\\n\\n @PUT\\n @Path(\\"{id}\\")\\n public Response upsert(@PathParam(\\"id\\") String id, JsonObject input){}\\n\\n @PATCH\\n @Path(\\"{id}\\")\\n public Response patch(@PathParam(\\"id\\") String id, JsonObject input) {}\\n\\n @POST\\n public Response insert(JsonObject input) {}\\n}\\n```\nA Java microservice is usually composed of multiple REST endpoints. On the one hand, an application containing multiple HTTP endpoints in a single Lambda would be considered quite large by Lambda standards. On the other hand, a cohesive, monolithic Java microservice reduces development and deployment complexity. In my experience, I’ve seen that monolithic Java microservices were more productive and have been easily maintained. The challenges of distributed computing don’t apply to a monolith because all of the invocations within the monolith are local. The question then becomes: is it viable to take a Java microservice and deploy it on top of [AWS Lambda](https://aws.amazon.com/cn/lambda/?trk=cndc-detail)?\n\n#### **Quarkus – A Next Generation Runtime**\nQuarkus is a framework that simultaneously supports MicroProfile APIs and the [Amazon API Gateway](https://aws.amazon.com/api-gateway/).\n\nThis means that Quarkus can interact with the AWS REST API, WebSocket API, or HTTP API. For example, an API Gateway/Elastic Load Balancer converts HTTP requests to HTTP Events and forwards them to the Lambda runtime. Quarkus consumes the HTTP request with a generic Lambda implementation:```io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest```.The ability to map HTTP requests for large numbers of endpoints – with each handling various HTTP methods – to the reactive event model within Quarkus is a productive way to build micro-service-like Lambda applications. Deploying this on Lambda means that you can have a monolithic web application which is capable of supporting many different kinds of HTTP requests that are spun up to handle traffic, on-demand.\n\nLet’s discuss performance. The duration of cold and warm must start to be minimized to meet cost and customer experience expectations. Quarkus’ build time optimizations can reduce startup time and decrease the reflection needs during dependency injection. Execution times are improved by requesting that Quarkus examine the metadata at build time and replace expensive reflection with straightforward, generated bytecode. This is done instead of relying on the JVM to run sophisticated class loading mechanisms and dynamic invocations at runtime.\n\nQuarkus offers productivity gains through its use of Jakarta Context Dependency Injection ([CDI](https://jakarta.ee/specifications/cdi/3.0/)), Jakarta JSON Binding ([JSON-B](https://jakarta.ee/specifications/jsonb/3.0/jakarta-jsonb-spec-3.0.html)), Jakarta JSON Processing ([JSON-P](https://jakarta.ee/specifications/jsonp/1.1/jsonp-spec-1.1.html)), JAX-RS, Jakarta [Bean Validation](https://jakarta.ee/specifications/bean-validation/3.0/jakarta-bean-validation-spec-3.0.html), Configuration for MicroProfile CDI, JSON-B, JSON-P, JAX-RS, and Bean Validation APIs. These combine to make developing HTTP services more convenient.\n\nJAX-RS maps HTTP requests to Java methods, JSON-B binds JSON to classes, JSON-P parses JSON to Map-like structure, and CDI lets you make your code leaner and more testable. Moreover, Bean Validation lets you verify the correctness of your input parameters, and [MicroProfile Config](https://microprofile.io/microprofile-config/) enables you to make Lambda environment entries directly injectable into Java fields.\n\nQuarkus integrates these APIs as extensions and performs the optimizations mentioned above. You can use them without significant performance impacts.\n\n#### **From a JAR to function.zip**\nLet’s deploy a JAX-RS endpoint:\n```\\n@Path(\\"hello\\")\\n@ApplicationScoped\\npublic class GreetingResource {\\n\\n @Inject\\n Greeter greeter;\\n\\n @GET\\n @Produces(MediaType.TEXT_PLAIN)\\n public String hello() {\\n return this.greeter.greetings();\\n }\\n\\n @POST\\n @Consumes(MediaType.TEXT_PLAIN)\\n public void hello(String message) {\\n this.greeter.greetings(message);\\n }\\n}\\n```\nwith injected Java class containing a MicroProfile Config property:\n```\\n@ApplicationScoped\\npublic class Greeter {\\n\\n static System.Logger LOG = System.getLogger(Greeter.class.getName());\\n\\n @Inject\\n @ConfigProperty(defaultValue = \\"hello, quarkus on AWS\\", name=\\"message\\")\\n String message;\\n\\n public String greetings() {\\n return this.message;\\n }\\n\\n public void greetings(String message) {\\n LOG.log(INFO, \\"received: \\" + message);\\n }\\n}\\n```\nNote: The code is available from GitHub: [here](https://github.com/AdamBien/aws-quarkus-lambda-cdk-plain/tree/main/lambda/src/main/java/airhacks/lambda/greetings/boundary).\n\nIn the above code, you’re requesting that MicroProfile Config inject the String message field’s value from a well-defined sequence of sources:```META-INF/microprofile-config.properties``` environment entries, and system properties. Lambda’s configuration specified via the cdk or AWS Management Console overrides the default values specified in the configuration file.\n\n#### **Infrastructure as Code (IaC) with Java**\nJava is not only a great language with which to build Lambda functions, but also well suited for IaC automation with [AWS Cloud Development Kit (cdk)](https://aws.amazon.com/cdk/) v2. The following LambdaStack class comprising the Lambda and HTTP API Gateway constructs, and the corresponding integration, are packaged and deployed as an [AWS CloudFormation](https://aws.amazon.com/cloudformation/) stack.\n\n```\\npublic class LambdaStack extends Stack {\\n\\n static Map<String, String> configuration = Map.of(\\"message\\", \\"hello, quarkus / large AWS Lambda\\");\\n static String functionName = \\"aws_Large\\";\\n static String lambdaHandler = \\"io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest\\";\\n static int memory = 1024; //~0.5 vCPU\\n static int timeout = 10;\\n\\n public LambdaStack(Construct scope, String id) {\\n super(scope, id);\\n var function = createFunction(functionName, lambdaHandler, configuration, memory, timeout);\\n integrateWithHTTPApiGateway(function);\\n }\\n\\n void integrateWithHTTPApiGateway(Function function){\\n var lambdaIntegration = HttpLambdaIntegration.Builder.create(\\"HttpApiGatewayIntegration\\",function)\\n .build();\\n var httpApiGateway = HttpApi.Builder.create(this, \\"HttpApiGatewayIntegration\\")\\n .defaultIntegration(lambdaIntegration)\\n .build();\\n CfnOutput.Builder.create(this, \\"HttpApiGatewayUrlOutput\\").value(httpApiGateway.getUrl()).build();\\n }\\n\\nFunction createFunction(String functionName,String functionHandler, Map<String,String> configuration, int memory, int timeout) {\\n return Function.Builder.create(this, functionName)\\n .runtime(Runtime.JAVA_11)\\n .architecture(Architecture.ARM_64)\\n .code(Code.fromAsset(\\"../lambda/target/function.zip\\"))\\n .handler(functionHandler)\\n .memorySize(memory)\\n .functionName(functionName)\\n .environment(configuration)\\n .timeout(Duration.seconds(timeout))\\n .build();\\n }\\n```\nCDK example code is available from GitHub: [here](https://github.com/AdamBien/aws-quarkus-lambda-cdk-plain/tree/main/cdk/src/main/java/airhacks)\t\n\nUnder Lambda, the number of vCPU resources provided is tied to the amount of memory that you select. Choosing a configuration that uses 1 GB of RAM will provide us with approximately half of a vCPU, which is sufficient for our needs. We configure the need for Corretto 11 with the method runtime(Runtime.JAVA_11) and the target CPU architecture with ```runtime(Runtime.JAVA_11)```and the target CPU architecture with ```architecture(Architecture.ARM_64)```which will use [AWS Graviton2](https://aws.amazon.com/ec2/graviton/) processors.\n\nThe Map passed as configuration to the Builder in the method environment (configuration) is injectable via the MicroProfile configuration.\n\nThe “executable” main method in the CDKApp class instantiates the LambdaStack:\n\n```\\nimport software.amazon.awscdk.*;\\n\\npublic class CDKApp {\\n\\npublic static void main(final String[] args) {\\n var app = new App();\\n var appName = \\"oversized\\";\\n Tags.of(app).add(\\"project\\", \\"MicroProfile with Quarkus on AWS Lambda\\");\\n Tags.of(app).add(\\"environment\\",\\"development\\");\\n Tags.of(app).add(\\"application\\", appName);\\n\\n new LambdaStack(app, appName);\\n app.synth();\\n }\\n}\\n```\nOur example is structured as a “self-provisioned service”, and it ships with two directories: lambda and cdk.\n\nThe maven command “```mvn clean package```” executed from the Lambda directory starts the Quarkus build and creates “function.zip.” After function.zip is available, you execute from the “cdk” directory ```mvn clean package&& cdk``` deploy to provision the AWS resources and deploy the Lambda to AWS. The overall Lambda package size is 13.7 MB.\n\nOutputs:\n```large.HttpApiGatewayUrlOutput = https://{GENERATED_ID}execute-api.eu-central-1.amazonaws.com/```\n#### **The performance and costs of large Lambdas on Corretto (OpenJDK 11)**\nOur Lambda is accessible via HTTP:```https://{GENERATED_ID}.execute-api.eu-central-1.amazonaws.com/curl```. The very first request is a cold start. The first invocation:```curlhttps://{GENERATED_ID}.execute-api.eu-central-1.amazonaws.com/hello``` takes three seconds for our MicroProfile application. Then, internal OpenJDK optimizations kick in and further optimize the runtime performance.\n\nEven though our Lambda is a full-stack MicroProfile Java application with dependency injection, JSON-B serialization, MicroProfile Config, and running on a 0.5 vCPU, **a “warm” request takes only 5-6 ms**.\n\n![image.png](https://dev-media.amazoncloud.cn/489252c5ade8453f9de61349c2928386_image.png)\n\nOne reason that you might look into using a full-stack framework such as Quarkus on top of Lambda is the potential cost savings. For example, after entering our Lambda settings: Region: EU (Frankfurt), Architecture: Arm, Number of requests: five per second, average duration: 6 ms, amount of memory allocated: 1024 MB, and amount of ephemeral storage allocated: 512 MB (minimum/unchanged) into the AWS pricing calculator, the resulting monthly Lambda costs, Without Free Tier, are: \$3.68.\\n\\n![image.png](https://dev-media.amazoncloud.cn/e1ca3436fbfc485b867a6190a9246f34_image.png)\\n\\nMore realistically, a Lambda will use external AWS Services, e.g., [Amazon Simple Storage Service (Amazon S3)](https://aws.amazon.com/s3/), [Amazon DynamoDB](https://aws.amazon.com/dynamodb/), [Amazon Simple Queue Service (Amazon SQS)](https://aws.amazon.com/sqs/), [Amazon Simple Notification Service (Amazon SNS)](https://aws.amazon.com/sns/), [Amazon Kinesis](https://aws.amazon.com/kinesis/), or [Amazon MSK](https://aws.amazon.com/msk/) . If you were to assume the calls to be made would be synchronous and increased the average Lambda instance duration from 6 ms to ~100 ms, then the resulting monthly costs would be approximately \$20.15. Feel free to do your own calculations and try it for yourself.\n\n#### **Hybrid deployments and local testing**\nAlthough our simplistic application is packaged as Lambda (function.zip), it’s still a regular Quarkus/MicroProfile application, and it can be started locally with:```mvn compilequarkus:dev```. After less than a second (0.861s), or after a local “cold start,” the application becomes available under```http://localhost:8080/hello```.\n\nQuarkus also supports container (Docker) builds out-of-the-box. The same Quarkus/MicroProfile application can be deployed as a Lambda function and to all AWS services supporting containers, such as, [Amazon Elastic Container Service (Amazon ECS)](https://aws.amazon.com/ecs/)/[Amazon Elastic Kubernetes Service (Amazon EKS) Fargate](https://aws.amazon.com/eks/), [AWS Elastic Beanstalk](https://aws.amazon.com/elasticbeanstalk/), [AWS App Runner](https://aws.amazon.com/apprunner/), or [AWS Lightsail](https://aws.amazon.com/lightsail/) . In addition to [container images](https://quarkus.io/guides/container-image), Quarkus builds also produces executable JARs which are runnable on bare [Amazon Elastic Compute Cloud (Amazon EC2)](https://aws.amazon.com/ec2/) instances.\n\n#### **Lambda Functions are not application servers**\nAlthough we can run the same Java bytecode without modification on multiple AWS Cloud services, Lambda behaves differently from an application running on an application server in a container.\n\nQuarkus in a container runs continuously, and multiple threads serve the requests. A Lambda is single-threaded. Several independent Lambda instances and JVMs respond to parallel requests.\n\nContainers are restarted less frequently, and a Lambda is potentially re-initialized at every request.\n\nMicroProfile and CDI support instance lifecycles/contexts such as, RequestScoped, SessionScoped, or ApplicationScoped. In the Lambda case, only the ApplicationScoped context is applicable.\n\nGiven the stateless and short-lived nature of Lambda, some MicroProfile APIs (e.g., startup hooks, MicroProfile metrics, timers, schedules, JDBC connection pools) must be extracted from Java code to AWS services (e.g. [Amazon EventBridge](https://aws.amazon.com/eventbridge/), [Amazon CloudWatch metrics](https://aws.amazon.com/cloudwatch/), or [Amazon RDS](https://aws.amazon.com/rds/) proxy).\n\n#### **Conclusion**\nTogether with Corretto’s JDK, Lambda is an excellent MicroProfile deployment platform. MicroProfile and Lambda let you focus on business value rather than the underlying infrastructure. MicroProfile provides implementation-agnostic APIs, Quarkus implements MicroProfile APIs which improve startup and runtime behavior, and Corretto continuously optimizes the performance of “warm” functions. Furthermore, Corretto is optimized to run on ARM64 architectures and Graviton2 processors more cost-efficiently. To see me present the ideas from this article in a recorded video format, see “[Microprofile on Quarkus](https://www.youtube.com/watch?v=VSadpI-b1hU)” and “[MicroProfile on Quarkus as AWS Lambda deployed with AWS CDK](https://www.youtube.com/watch?v=NA0WjIgp4CQ)“.\n\n**Author:**\n\n![image.png](https://dev-media.amazoncloud.cn/8dc0dd997f8a4aa29edf065a85732b00_image.png)\n\n**Adam Bien**\nAdam Bien is a software architect and developer (with usually 20/80 distribution) in Java (SE / EE / Jakarta EE / MicroProfile) and Web (ES 6+, Web Components, Web Standards “no frameworks”) projects. Often he’s starting as an architect and after a few days finds himself developing PoCs, performing code reviews, or helping the teams developing critical parts of the system.\nIn the recent years he has helped many clients to migrate Java EE / Jakarta EE / MicroProfile applications to serverless architectures on AWS. Such projects often started as code and architecture reviews and ended with a pragmatic cloud migration. He speaks regularly at conferences, but doesn’t consider himself a professional speaker, nor a writer. He’s just really enjoying writing code and killing the bloat. …and Java is perfect for that.","render":"<h4><a id=\\"Using_Amazon_Corretto_OpenJDK_for_lean_fast_and_efficient_AWS_Lambda_0\\"></a><strong>Using Amazon Corretto (OpenJDK) for lean, fast, and efficient AWS Lambda</strong></h4>\\n<p><em><strong>By Guest Blogger <a href=\\"https://adam-bien.com/\\" target=\\"_blank\\">Adam Bien</a></strong></em></p>\\n<p>In this post, I will discuss how you can launch large, monolithic applications on top of <a href=\\"https://aws.amazon.com/lambda/\\" target=\\"_blank\\">AWS Lambda</a>, and I’ll show that they perform well and are cost effective. You’ll learn that the same application you develop for Lambda can be run locally or deployed across your favorite AWS container service, without modifications. Furthermore, I’ll introduce you to running <a href=\\"https://aws.amazon.com/corretto\\" target=\\"_blank\\">Amazon Corretto</a> on ARM64 using Lambda’s Graviton2-based offering. Corretto, Amazon’s version of OpenJDK, is my go-to option, especially for ARM64, because it lets you run a Java Lambda on Graviton processors efficiently and cost-effectively. I’ll be using it to demonstrate how you can use it with <a href=\\"https://quarkus.io/\\" target=\\"_blank\\">Quarkus</a> to build microservices quickly and at low cost.</p>\\n<h5><a id=\\"Builtin_functionality_over_external_dependencies_5\\"></a><strong>Built-in functionality over external dependencies</strong></h5>\\n<p>Java’s dynamic dependency mechanism was introduced in 2009 via “<a href=\\"https://jcp.org/en/jsr/detail?id=330\\" target=\\"_blank\\">JSR 330:Dependency Injection for Java</a>” and is often used with <a href=\\"https://microprofile.io/\\" target=\\"_blank\\">MicroProfile</a> or <a href=\\"https://jakarta.ee/\\" target=\\"_blank\\">Jakarta EE</a>.</p>\\n<p>For those unfamiliar with dependency injection, consider it like updating your classes’ instance variables dynamically via a service. This is accomplished through annotations that you place in your code, e.g., <code>@Inject.</code> Therefore, rather than instantiating an instance variable yourself in your class, you would ask a service to do it for you. This makes your class more maintainable. The instance that you inject into your class from the external service is loosely coupled with your client class, which lets you maintain your code more easily. Moreover, you can more easily develop test cases, because you can inject variables into your classes based on configuration files without recompiling. In addition, since the classes are loosely coupled, they become easier to test in isolation. A MicroProfile runtime combined with Java’s built-in class libraries lets you make external dependencies optional and increases productivity with fast iterations and short deployment times. Using dependency injection also lets you focus on developing your app. And this happens without having to worry about the boilerplate code that you would ordinarily need to implement many times, repetitively, with no differentiated and additive benefit.</p>\\n<p>However, even a small Java microservice can become a large Lambda. The following code illustrates a typical Create, Read, Update, Delete (CRUD) operation in Java that’s comprised of a few injected classes:</p>\n<pre><code class=\\"lang-\\">import javax.json.*;\\nimport javax.ws.rs.*;\\nimport javax.ws.rs.core.*;\\n\\npublic class CRUDResource {\\n\\n @Inject\\n CRUDStore store;\\n\\n @GET\\n @Path(&quot;{id}&quot;)\\n public JsonObject fetch(@PathParam(&quot;id&quot;) String id){\\n var result = this.store.fetch(id);\\n //....\\n }\\n\\n @GET\\n public JsonArray findAll(){}\\n\\n @DELETE\\n @Path(&quot;{id}&quot;)\\n public void delete(@PathParam(&quot;id&quot;) String id){}\\n\\n @DELETE\\n public void deleteAll() {}}\\n\\n @PUT\\n @Path(&quot;{id}&quot;)\\n public Response upsert(@PathParam(&quot;id&quot;) String id, JsonObject input){}\\n\\n @PATCH\\n @Path(&quot;{id}&quot;)\\n public Response patch(@PathParam(&quot;id&quot;) String id, JsonObject input) {}\\n\\n @POST\\n public Response insert(JsonObject input) {}\\n}\\n</code></pre>\\n<p>A Java microservice is usually composed of multiple REST endpoints. On the one hand, an application containing multiple HTTP endpoints in a single Lambda would be considered quite large by Lambda standards. On the other hand, a cohesive, monolithic Java microservice reduces development and deployment complexity. In my experience, I’ve seen that monolithic Java microservices were more productive and have been easily maintained. The challenges of distributed computing don’t apply to a monolith because all of the invocations within the monolith are local. The question then becomes: is it viable to take a Java microservice and deploy it on top of AWS Lambda?</p>\n<h4><a id=\\"Quarkus__A_Next_Generation_Runtime_53\\"></a><strong>Quarkus – A Next Generation Runtime</strong></h4>\\n<p>Quarkus is a framework that simultaneously supports MicroProfile APIs and the <a href=\\"https://aws.amazon.com/api-gateway/\\" target=\\"_blank\\">Amazon API Gateway</a>.</p>\\n<p>This means that Quarkus can interact with the AWS REST API, WebSocket API, or HTTP API. For example, an API Gateway/Elastic Load Balancer converts HTTP requests to HTTP Events and forwards them to the Lambda runtime. Quarkus consumes the HTTP request with a generic Lambda implementation:<code>io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest</code>.The ability to map HTTP requests for large numbers of endpoints – with each handling various HTTP methods – to the reactive event model within Quarkus is a productive way to build micro-service-like Lambda applications. Deploying this on Lambda means that you can have a monolithic web application which is capable of supporting many different kinds of HTTP requests that are spun up to handle traffic, on-demand.</p>\\n<p>Let’s discuss performance. The duration of cold and warm must start to be minimized to meet cost and customer experience expectations. Quarkus’ build time optimizations can reduce startup time and decrease the reflection needs during dependency injection. Execution times are improved by requesting that Quarkus examine the metadata at build time and replace expensive reflection with straightforward, generated bytecode. This is done instead of relying on the JVM to run sophisticated class loading mechanisms and dynamic invocations at runtime.</p>\n<p>Quarkus offers productivity gains through its use of Jakarta Context Dependency Injection (<a href=\\"https://jakarta.ee/specifications/cdi/3.0/\\" target=\\"_blank\\">CDI</a>), Jakarta JSON Binding (<a href=\\"https://jakarta.ee/specifications/jsonb/3.0/jakarta-jsonb-spec-3.0.html\\" target=\\"_blank\\">JSON-B</a>), Jakarta JSON Processing (<a href=\\"https://jakarta.ee/specifications/jsonp/1.1/jsonp-spec-1.1.html\\" target=\\"_blank\\">JSON-P</a>), JAX-RS, Jakarta <a href=\\"https://jakarta.ee/specifications/bean-validation/3.0/jakarta-bean-validation-spec-3.0.html\\" target=\\"_blank\\">Bean Validation</a>, Configuration for MicroProfile CDI, JSON-B, JSON-P, JAX-RS, and Bean Validation APIs. These combine to make developing HTTP services more convenient.</p>\\n<p>JAX-RS maps HTTP requests to Java methods, JSON-B binds JSON to classes, JSON-P parses JSON to Map-like structure, and CDI lets you make your code leaner and more testable. Moreover, Bean Validation lets you verify the correctness of your input parameters, and <a href=\\"https://microprofile.io/microprofile-config/\\" target=\\"_blank\\">MicroProfile Config</a> enables you to make Lambda environment entries directly injectable into Java fields.</p>\\n<p>Quarkus integrates these APIs as extensions and performs the optimizations mentioned above. You can use them without significant performance impacts.</p>\n<h4><a id=\\"From_a_JAR_to_functionzip_66\\"></a><strong>From a JAR to function.zip</strong></h4>\\n<p>Let’s deploy a JAX-RS endpoint:</p>\n<pre><code class=\\"lang-\\">@Path(&quot;hello&quot;)\\n@ApplicationScoped\\npublic class GreetingResource {\\n\\n @Inject\\n Greeter greeter;\\n\\n @GET\\n @Produces(MediaType.TEXT_PLAIN)\\n public String hello() {\\n return this.greeter.greetings();\\n }\\n\\n @POST\\n @Consumes(MediaType.TEXT_PLAIN)\\n public void hello(String message) {\\n this.greeter.greetings(message);\\n }\\n}\\n</code></pre>\\n<p>with injected Java class containing a MicroProfile Config property:</p>\n<pre><code class=\\"lang-\\">@ApplicationScoped\\npublic class Greeter {\\n\\n static System.Logger LOG = System.getLogger(Greeter.class.getName());\\n\\n @Inject\\n @ConfigProperty(defaultValue = &quot;hello, quarkus on AWS&quot;, name=&quot;message&quot;)\\n String message;\\n\\n public String greetings() {\\n return this.message;\\n }\\n\\n public void greetings(String message) {\\n LOG.log(INFO, &quot;received: &quot; + message);\\n }\\n}\\n</code></pre>\\n<p>Note: The code is available from GitHub: <a href=\\"https://github.com/AdamBien/aws-quarkus-lambda-cdk-plain/tree/main/lambda/src/main/java/airhacks/lambda/greetings/boundary\\" target=\\"_blank\\">here</a>.</p>\\n<p>In the above code, you’re requesting that MicroProfile Config inject the String message field’s value from a well-defined sequence of sources:<code>META-INF/microprofile-config.properties</code> environment entries, and system properties. Lambda’s configuration specified via the cdk or AWS Management Console overrides the default values specified in the configuration file.</p>\\n<h4><a id=\\"Infrastructure_as_Code_IaC_with_Java_113\\"></a><strong>Infrastructure as Code (IaC) with Java</strong></h4>\\n<p>Java is not only a great language with which to build Lambda functions, but also well suited for IaC automation with <a href=\\"https://aws.amazon.com/cdk/\\" target=\\"_blank\\">AWS Cloud Development Kit (cdk)</a> v2. The following LambdaStack class comprising the Lambda and HTTP API Gateway constructs, and the corresponding integration, are packaged and deployed as an <a href=\\"https://aws.amazon.com/cloudformation/\\" target=\\"_blank\\">AWS CloudFormation</a> stack.</p>\\n<pre><code class=\\"lang-\\">public class LambdaStack extends Stack {\\n\\n static Map&lt;String, String&gt; configuration = Map.of(&quot;message&quot;, &quot;hello, quarkus / large AWS Lambda&quot;);\\n static String functionName = &quot;aws_Large&quot;;\\n static String lambdaHandler = &quot;io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest&quot;;\\n static int memory = 1024; //~0.5 vCPU\\n static int timeout = 10;\\n\\n public LambdaStack(Construct scope, String id) {\\n super(scope, id);\\n var function = createFunction(functionName, lambdaHandler, configuration, memory, timeout);\\n integrateWithHTTPApiGateway(function);\\n }\\n\\n void integrateWithHTTPApiGateway(Function function){\\n var lambdaIntegration = HttpLambdaIntegration.Builder.create(&quot;HttpApiGatewayIntegration&quot;,function)\\n .build();\\n var httpApiGateway = HttpApi.Builder.create(this, &quot;HttpApiGatewayIntegration&quot;)\\n .defaultIntegration(lambdaIntegration)\\n .build();\\n CfnOutput.Builder.create(this, &quot;HttpApiGatewayUrlOutput&quot;).value(httpApiGateway.getUrl()).build();\\n }\\n\\nFunction createFunction(String functionName,String functionHandler, Map&lt;String,String&gt; configuration, int memory, int timeout) {\\n return Function.Builder.create(this, functionName)\\n .runtime(Runtime.JAVA_11)\\n .architecture(Architecture.ARM_64)\\n .code(Code.fromAsset(&quot;../lambda/target/function.zip&quot;))\\n .handler(functionHandler)\\n .memorySize(memory)\\n .functionName(functionName)\\n .environment(configuration)\\n .timeout(Duration.seconds(timeout))\\n .build();\\n }\\n</code></pre>\\n<p>CDK example code is available from GitHub: <a href=\\"https://github.com/AdamBien/aws-quarkus-lambda-cdk-plain/tree/main/cdk/src/main/java/airhacks\\" target=\\"_blank\\">here</a></p>\\n<p>Under Lambda, the number of vCPU resources provided is tied to the amount of memory that you select. Choosing a configuration that uses 1 GB of RAM will provide us with approximately half of a vCPU, which is sufficient for our needs. We configure the need for Corretto 11 with the method runtime(Runtime.JAVA_11) and the target CPU architecture with <code>runtime(Runtime.JAVA_11)</code>and the target CPU architecture with <code>architecture(Architecture.ARM_64)</code>which will use <a href=\\"https://aws.amazon.com/ec2/graviton/\\" target=\\"_blank\\">AWS Graviton2</a> processors.</p>\\n<p>The Map passed as configuration to the Builder in the method environment (configuration) is injectable via the MicroProfile configuration.</p>\n<p>The “executable” main method in the CDKApp class instantiates the LambdaStack:</p>\n<pre><code class=\\"lang-\\">import software.amazon.awscdk.*;\\n\\npublic class CDKApp {\\n\\npublic static void main(final String[] args) {\\n var app = new App();\\n var appName = &quot;oversized&quot;;\\n Tags.of(app).add(&quot;project&quot;, &quot;MicroProfile with Quarkus on AWS Lambda&quot;);\\n Tags.of(app).add(&quot;environment&quot;,&quot;development&quot;);\\n Tags.of(app).add(&quot;application&quot;, appName);\\n\\n new LambdaStack(app, appName);\\n app.synth();\\n }\\n}\\n</code></pre>\\n<p>Our example is structured as a “self-provisioned service”, and it ships with two directories: lambda and cdk.</p>\n<p>The maven command “<code>mvn clean package</code>” executed from the Lambda directory starts the Quarkus build and creates “function.zip.” After function.zip is available, you execute from the “cdk” directory <code>mvn clean package&amp;&amp; cdk</code> deploy to provision the AWS resources and deploy the Lambda to AWS. The overall Lambda package size is 13.7 MB.</p>\\n<p>Outputs:<br />\\n<code>large.HttpApiGatewayUrlOutput = https://{GENERATED_ID}execute-api.eu-central-1.amazonaws.com/</code></p>\\n<h4><a id=\\"The_performance_and_costs_of_large_Lambdas_on_Corretto_OpenJDK_11_184\\"></a><strong>The performance and costs of large Lambdas on Corretto (OpenJDK 11)</strong></h4>\\n<p>Our Lambda is accessible via HTTP:<code>https://{GENERATED_ID}.execute-api.eu-central-1.amazonaws.com/curl</code>. The very first request is a cold start. The first invocation:<code>curlhttps://{GENERATED_ID}.execute-api.eu-central-1.amazonaws.com/hello</code> takes three seconds for our MicroProfile application. Then, internal OpenJDK optimizations kick in and further optimize the runtime performance.</p>\\n<p>Even though our Lambda is a full-stack MicroProfile Java application with dependency injection, JSON-B serialization, MicroProfile Config, and running on a 0.5 vCPU, <strong>a “warm” request takes only 5-6 ms</strong>.</p>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/489252c5ade8453f9de61349c2928386_image.png\\" alt=\\"image.png\\" /></p>\n<p>One reason that you might look into using a full-stack framework such as Quarkus on top of Lambda is the potential cost savings. For example, after entering our Lambda settings: Region: EU (Frankfurt), Architecture: Arm, Number of requests: five per second, average duration: 6 ms, amount of memory allocated: 1024 MB, and amount of ephemeral storage allocated: 512 MB (minimum/unchanged) into the AWS pricing calculator, the resulting monthly Lambda costs, Without Free Tier, are: \$3.68.</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/e1ca3436fbfc485b867a6190a9246f34_image.png\\" alt=\\"image.png\\" /></p>\n<p>More realistically, a Lambda will use external AWS Services, e.g., <a href=\\"https://aws.amazon.com/s3/\\" target=\\"_blank\\">Amazon Simple Storage Service (Amazon S3)</a>, <a href=\\"https://aws.amazon.com/dynamodb/\\" target=\\"_blank\\">Amazon DynamoDB</a>, <a href=\\"https://aws.amazon.com/sqs/\\" target=\\"_blank\\">Amazon Simple Queue Service (Amazon SQS)</a>, <a href=\\"https://aws.amazon.com/sns/\\" target=\\"_blank\\">Amazon Simple Notification Service (Amazon SNS)</a>, <a href=\\"https://aws.amazon.com/kinesis/\\" target=\\"_blank\\">Amazon Kinesis</a>, or <a href=\\"https://aws.amazon.com/msk/\\" target=\\"_blank\\">Amazon MSK</a> . If you were to assume the calls to be made would be synchronous and increased the average Lambda instance duration from 6 ms to ~100 ms, then the resulting monthly costs would be approximately $20.15. Feel free to do your own calculations and try it for yourself.</p>\\n<h4><a id=\\"Hybrid_deployments_and_local_testing_197\\"></a><strong>Hybrid deployments and local testing</strong></h4>\\n<p>Although our simplistic application is packaged as Lambda (function.zip), it’s still a regular Quarkus/MicroProfile application, and it can be started locally with:<code>mvn compilequarkus:dev</code>. After less than a second (0.861s), or after a local “cold start,” the application becomes available under<code>http://localhost:8080/hello</code>.</p>\\n<p>Quarkus also supports container (Docker) builds out-of-the-box. The same Quarkus/MicroProfile application can be deployed as a Lambda function and to all AWS services supporting containers, such as, <a href=\\"https://aws.amazon.com/ecs/\\" target=\\"_blank\\">Amazon Elastic Container Service (Amazon ECS)</a>/<a href=\\"https://aws.amazon.com/eks/\\" target=\\"_blank\\">Amazon Elastic Kubernetes Service (Amazon EKS) Fargate</a>, <a href=\\"https://aws.amazon.com/elasticbeanstalk/\\" target=\\"_blank\\">AWS Elastic Beanstalk</a>, <a href=\\"https://aws.amazon.com/apprunner/\\" target=\\"_blank\\">AWS App Runner</a>, or <a href=\\"https://aws.amazon.com/lightsail/\\" target=\\"_blank\\">AWS Lightsail</a> . In addition to <a href=\\"https://quarkus.io/guides/container-image\\" target=\\"_blank\\">container images</a>, Quarkus builds also produces executable JARs which are runnable on bare <a href=\\"https://aws.amazon.com/ec2/\\" target=\\"_blank\\">Amazon Elastic Compute Cloud (Amazon EC2)</a> instances.</p>\\n<h4><a id=\\"Lambda_Functions_are_not_application_servers_202\\"></a><strong>Lambda Functions are not application servers</strong></h4>\\n<p>Although we can run the same Java bytecode without modification on multiple AWS Cloud services, Lambda behaves differently from an application running on an application server in a container.</p>\n<p>Quarkus in a container runs continuously, and multiple threads serve the requests. A Lambda is single-threaded. Several independent Lambda instances and JVMs respond to parallel requests.</p>\n<p>Containers are restarted less frequently, and a Lambda is potentially re-initialized at every request.</p>\n<p>MicroProfile and CDI support instance lifecycles/contexts such as, RequestScoped, SessionScoped, or ApplicationScoped. In the Lambda case, only the ApplicationScoped context is applicable.</p>\n<p>Given the stateless and short-lived nature of Lambda, some MicroProfile APIs (e.g., startup hooks, MicroProfile metrics, timers, schedules, JDBC connection pools) must be extracted from Java code to AWS services (e.g. <a href=\\"https://aws.amazon.com/eventbridge/\\" target=\\"_blank\\">Amazon EventBridge</a>, <a href=\\"https://aws.amazon.com/cloudwatch/\\" target=\\"_blank\\">Amazon CloudWatch metrics</a>, or <a href=\\"https://aws.amazon.com/rds/\\" target=\\"_blank\\">Amazon RDS</a> proxy).</p>\\n<h4><a id=\\"Conclusion_213\\"></a><strong>Conclusion</strong></h4>\\n<p>Together with Corretto’s JDK, Lambda is an excellent MicroProfile deployment platform. MicroProfile and Lambda let you focus on business value rather than the underlying infrastructure. MicroProfile provides implementation-agnostic APIs, Quarkus implements MicroProfile APIs which improve startup and runtime behavior, and Corretto continuously optimizes the performance of “warm” functions. Furthermore, Corretto is optimized to run on ARM64 architectures and Graviton2 processors more cost-efficiently. To see me present the ideas from this article in a recorded video format, see “<a href=\\"https://www.youtube.com/watch?v=VSadpI-b1hU\\" target=\\"_blank\\">Microprofile on Quarkus</a>” and “<a href=\\"https://www.youtube.com/watch?v=NA0WjIgp4CQ\\" target=\\"_blank\\">MicroProfile on Quarkus as AWS Lambda deployed with AWS CDK</a>“.</p>\\n<p><strong>Author:</strong></p>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/8dc0dd997f8a4aa29edf065a85732b00_image.png\\" alt=\\"image.png\\" /></p>\n<p><strong>Adam Bien</strong><br />\\nAdam Bien is a software architect and developer (with usually 20/80 distribution) in Java (SE / EE / Jakarta EE / MicroProfile) and Web (ES 6+, Web Components, Web Standards “no frameworks”) projects. Often he’s starting as an architect and after a few days finds himself developing PoCs, performing code reviews, or helping the teams developing critical parts of the system.<br />\\nIn the recent years he has helped many clients to migrate Java EE / Jakarta EE / MicroProfile applications to serverless architectures on AWS. Such projects often started as code and architecture reviews and ended with a pragmatic cloud migration. He speaks regularly at conferences, but doesn’t consider himself a professional speaker, nor a writer. He’s just really enjoying writing code and killing the bloat. …and Java is perfect for that.</p>\n"}
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭