1. 程式人生 > >Running APIs Written in Java on AWS Lambda

Running APIs Written in Java on AWS Lambda

Java developers have a vast selection of open source frameworks to build server-side APIs at their disposal: from Spring and Spring Boot to Jersey to Spark. Normally, these frameworks embed a servlet container engine, such as Tomcat, in the built package to run on a server.

In the serverless world,

AWS Lambda and Amazon API Gateway are the HTTP frontend and compute platform. Today, we announce the 1.0 release of our aws-serverless-java-container framework. Serverless Java Container makes it easy to take an application written in Java with frameworks such as Spring, Spring Boot, Jersey, or Spark and run it inside AWS Lambda with minimal code changes.

The Serverless Java Container library acts as a proxy between the Lambda runtime and the framework of your choice, pretends to be a servlet engine, translates incoming events to request objects that frameworks can understand, and transforms responses from your application into a format that API Gateway understands.

The Serverless Java Container library is available on Maven. We have different flavors of the library depending on which framework you are using: Spring, Spring Boot, Jersey, or Spark.

For this blog post, we will build a Jersey application. Other implementations of the library have a very similar structure, take a look at the quick start guides on GitHub.

Using the Maven archetypes

We have published basic Maven archetypes for all of the supported frameworks. To run through this tutorial, you will need Apache Maven installed on your local machine.

Using a terminal, open your workspace directory and run the maven command to generate a new project from an archetype. Make sure to replace the groupId and artifactId with your preferred settings:

$ cd myworkspace
$ mvn archetype:generate -DgroupId=my.service -DartifactId=jersey-sample -Dversion=1.0-SNAPSHOT \
      -DarchetypeGroupId=com.amazonaws.serverless.archetypes \
	  -DarchetypeArtifactId=aws-serverless-jersey-archetype \
	  -DarchetypeVersion=1.0.1 -Dinteractive=false

The mvn client asks you to confirm the parameters and then generates the project structure. In this sample, we used the aws-serverless-jersey-archetype – we have similar artifacts for spring, springboot, and spark. Let’s step through the generated code structure. If you just want to get going and test your basic application, skip straight to the local testing section.

The Jersey application

Using your favorite IDE, open the archetype project. The simple application included in the Jersey archetype, defines a /ping path that returns a JSON hello world message.

In your code package, in my case under my.service, you will find a resource package with a PingResource class. The ping class is annotated with JAX-RS’ @Path annotation and defines a single @GET method.

@Path("/ping")
public class PingResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.WILDCARD)
    public Response createPet() {
		// return a basic map. This will be marshalled automatically into a 
		// json object with a single property
        Map<String, String> pong = new HashMap<>();
        pong.put("pong", "Hello, World!");
        return Response.status(200).entity(pong).build();
    }
}

The Lambda handler

In the main package of our application, the archetype also generated a StreamLambdaHandler class. Let’s go through the code in this class:

public class StreamLambdaHandler implements RequestStreamHandler

Our class implements Lambda’s RequestStreamHandler interface. This class is the main entry point for AWS Lambda in our application: the “handler” in Lambda’s jargon. We use a stream handler instead of the POJO-based handler because our event models rely on annotations for marshalling and unmarhsalling, but Lambda’s built-in serializer does not support annotations.


private static final ResourceConfig jerseyApplication = new ResourceConfig()
                                                            .packages("com.sapessi.jersey.resource")
                                                            .register(JacksonFeature.class);

First, we declare a static ResourceConfig, Jersey’s Application implementation, object. We configure this object to scan our resource package for annotated classes and load the JacksonFeature class to handle JSON content types.

private static final JerseyLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler
            = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);

Next, we declare a second static instance of the JerseyLambdaContainerHandler object. We initialize this object using the getAwsProxyHandler static method with our ResourceConfig object. The getAwsProxyHandler method automatically creates an instance of our library configured to handle API Gateway’s proxy integration events. You can create custom implementations of the RequestReader and ResponseWriter objects to support custom event types.

You will notice that both these variables are declared as static class members. They are class members because we only need a single instance of these objects. AWS Lambda tries to re-use containers across invocations. Our handler class is held by the runtime as a singleton and the handleRequest method is invoked each time. We can re-use both the ResourceConfig and JerseyLambdaContainerHandler. Static variables are instantiated by the Java runtime as Lambda starts it; this gives us better performance for the heavy introspection operations.

public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
            throws IOException {
    handler.proxyStream(inputStream, outputStream, context);

Our implementation of the handler method is the main entry point for Lambda. Inside the handler method we make a single call, to the proxyStream method of our container handler. The proxyStream method takes care of reading our input stream into and creating an HttpServletRequest from its data. The HttpServletResponse generated by your application is automatically written to the output stream in the format Amazon API Gateway expects.

The project root

In the project root, the archetype generates three files: pom.xml, sam.yaml, and README.md.
The pom file declares our project and defines the Maven dependencies. You will see that it includes the serverless-java-container library.

<dependency>
    <groupId>com.amazonaws.serverless</groupId>
   <artifactId>aws-serverless-java-container-jersey</artifactId>
   <version>1.0</version>
</dependency>

The pom file also uses the Maven Shade plugin to generate an “uber-jar” that we can upload to AWS Lambda. Take a look at the <build> section.

The sam.yaml file is a Serverless Application Model (SAM) template we can use to deploy our application to AWS or test it locally with SAM Local. SAM is an abstraction on top of CloudFormation that makes it easier to define your serverless stacks in code. The SAM file defines a single resource, our AWS::Serverless::Function. The function is configured to use the “uber-jar” produced by the build process and points to our handler class. The API frontend is defined in the Events section of the function resource. An API Gateway RestApi, Stage, and Deployment are implicitly created when you deploy the template.

The README.md file contains a generated set of instructions to build, deploy and test the application.

Testing the application in local

You can use AWS SAM Local to start your service on your local machine. For SAM Local to work, you need Docker (community or enterprise) installed and running.

First, install SAM Local (if you haven’t already):

$ npm install -g aws-sam-local

Next, using a terminal, open the project root folder and build the jar file.

$ cd myworkspace/jersey-sample
$ mvn clean package

Still in the project root folder – where the sam.yaml file is located – start the API with the SAM Local CLI.

$ sam local start-api --template sam.yaml

...
Mounting com.sapessi.jersey.StreamLambdaHandler::handleRequest (java8) at http://127.0.0.1:3000/{proxy+} [OPTIONS GET HEAD POST PUT DELETE PATCH]
...

We now have a local emulator of API Gateway and Lambda up and running. Using a new shell, you can send a test ping request to your API:

$ curl -s http://127.0.0.1:3000/ping | python -m json.tool

{
    "pong": "Hello, World!"
}

Deploying to AWS

You can use the AWS CLI to quickly deploy your application to AWS Lambda and Amazon API Gateway.
You will need an S3 bucket to store the artifacts for deployment. Once you have created the S3 bucket, run the following command from the project’s root folder – where the sam.yaml file is located:

$ aws cloudformation package --template-file sam.yaml --output-template-file output-sam.yaml --s3-bucket <YOUR S3 BUCKET NAME>
Uploading to xxxxxxxxxxxxxxxxxxxxxxxxxx  6464692 / 6464692.0  (100.00%)
Successfully packaged artifacts and wrote output template to file output-sam.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /your/path/output-sam.yaml --stack-name <YOUR STACK NAME>

As the command output suggests, you can now use the cli to deploy the application. Choose a stack name and run the aws cloudformation deploy command from the output of the package command.

$ aws cloudformation deploy --template-file output-sam.yaml --stack-name ServerlessJerseyApi --capabilities CAPABILITY_IAM

Once the application is deployed, you can describe the stack to show the API endpoint that was created. The endpoint should be the ServerlessJerseyApikey of the Outputs property:

$ aws cloudformation describe-stacks --stack-name ServerlessJerseyApi --query 'Stacks[0].Outputs[*].{Service:OutputKey,Endpoint:OutputValue}'
[
    {
		"Service": "JerseySampleApi",
		"Endpoint": "https://xxxxxxx.execute-api.us-west-2.amazonaws.com/Prod/ping"
    }
]

Copy the OutputValue into a browser or use curl to test your first request:

$ curl -s https://xxxxxxx.execute-api.us-west-2.amazonaws.com/Prod/ping | python -m json.tool

{
    "pong": "Hello, World!"
}

Cold start notes

Java is a large runtime; we would be remiss if we didn’t include a section about cold starts. A cold start is the first invocation of your Lambda function, when AWS Lambda needs to spin up the infrastructure, launch the runtime, and start your code. Multiple factors can affect the speed at which your function starts for the first time:

  • Memory and CPU allocation: AWS Lambda allocates a proportional number of CPU cycles to the memory you allocate to your functions. You will see that the generated SAM templates use 512MB of memory by default. If your code is CPU-bound, increase this parameter to improve performance.
  • Code package size: Every time a Lambda function starts for the first time, it needs to download and unzip your code package. The size of your “uber-jar” matters. Be careful when importing dependencies, and use the Maven Shade plugin to strip out unnecessary transitive dependencies. For example, in the Spark implementation of this library, we exclude the embedded Jetty container.
  • Code lifecycle: Starting your function for the first time, AWS Lambda creates an instance of your handler object and will re-use it for future invocations as a singleton, addressing your handleRequest method directly. This means you can use class members to cache metadata, objects, and connections that you want to re-use across invocation. Avoid caching confidential data: there is no guarantee which Lambda instance will be used, and someone, someday, will inevitably forget to clean their cached data between invocations.
  • Frameworks have different features and performance characteristics. Spring and Spring Boot are extremely powerful when it comes to dependency injection and automatically wiring your application. However, you pay for all this flexibility with the cold start time – reflection is slow. Jersey only performs limited reflection to find its providers and resources. Spark, where everything is “statically linked,” is by far the fastest framework to start.

Given all these parameters, how do we pick the correct framework? If you don’t have strict latency requirements, pick the framework you are most comfortable with. In the real world, with production traffic, you will find that cold starts only impact your 99th if not 99.9th percentile metrics.

Conclusion

Serverless Java Container makes it easy to create scalable APIs with the Java framework of your choice. In this blog post we used Jersey, the library also offers archetypes for Spring, Spring Boot, and Spark. Projects created using the archetypes come pre-packaged with a working Lambda handler, a sample /ping resource, and a SAM template that allows you to quickly test your application in local and deploy it to AWS.

If you run into issues with the Serverless Java Container library, report them on our GitHub repository. For additional AWS Lambda or Amazon API Gateway questions, use the AWS forums.

相關推薦

Running APIs Written in Java on AWS Lambda

Java developers have a vast selection of open source frameworks to build server-side APIs at their disposal: from Spring and Spring Boot

In the news: Go on AWS Lambda · Applied Go

On Jan 15th, Amazon announced Go support for AWS Lambda. This was exciting news for many, according to the number of blog posts that followed this annou

Running an Inteligent Analytical System on AWS

Amazon Web Services is Hiring. Amazon Web Services (AWS) is a dynamic, growing business unit within Amazon.com. We are currently hiring So

Ask HN: Are you using AWS Lambda in production?

I use API Gateway to provide an API endpoint powered by Lambda (and deployed via Serverless). It took a few hours to understand the relationships between G

Upcoming Conference in Sydney on Predictive APIs and Apps ← Inductio Ex Machina ← Mark Reid

Upcoming Conference in Sydney on Predictive APIs and Apps After a big kick-off in Barcelona last year with over 200 attendees, the second International

Using Presto in our Big Data Platform on AWS

Using Presto in our Big Data Platform on AWSby Eva Tse, Zhenxiao Luo, Nezih Yigitbasi @ Big Data Platform teamAt Netflix, the Big Data Platform team is res

Invoke AWS Lambda when a State (Execution Event) Changes in AWS Step Functions

Before you begin this procedure, you must: Confirm that the event change that you want to trigger the Lambda function i

Running FaaS on a Kubernetes Cluster on AWS using Kubeless

Serverless computing allows you to build and run applications and services without provisioning, scaling, or managing any servers. FaaS (

Running Container-Enabled Microservices on AWS

このコースでは、Amazon Elastic Container Service (Amazon ECS) を使用して、コンテナ対応アプリケーションを管理およびスケールする方法を學習します。コンテナ化されたアプリケーションを大規模に実行するという課題に注目し、Amazo

Running Bleeding-Edge Kubernetes on AWS with kops

In an earlier blog post, I explained how to set up a Kubernetes cluster on AWS using kops. By default, the kops create cluster command chooses the

AWS Case Study: Time Inc. Goes All In on AWS

Amazon Web Services is Hiring. Amazon Web Services (AWS) is a dynamic, growing business unit within Amazon.com. We are currently hiring So

In the Works – VMware Cloud on AWS

The long-standing trend toward on-premises virtualization has helped many enterprises to increase operational efficiency and to wring out as much

AWS lambda上執行用Java編寫的APLs

從 Spring 和 Spring Boot 到 Jersey 到 Spark,Java 開發人員可以隨心選擇各種開放源來構建伺服器端 API。這些框架通常都在編譯包內嵌入了用來執行伺服器的 Servlet 容器引擎,例如 Tomcat。AWS Lambda 和 Amazon API Gat

Python 3.7 runtime now available in AWS Lambda

This post is courtesy of Shivansh Singh, Partner Solutions Architect – AWS We are excited to announce that you can now develop your AWS La

Using the AWS Lambda Project in Visual Studio

Last week we launched C# and .NET Core support for AWS Lambda. That release provided updated tooling for Visual Studio to help you get started wri

Just-in-Time Registration of Device Certificates on AWS IoT

In an earlier blog post about certificates, we discussed how use-your-own-certificate support in AWS IoT lets customers use device certificates si

基於Nodejs的高併發實時訊息轉發系統 message pusher and written in nodejs based on socket.io and express

基於Nodejs的訊息轉發系統 message pusher and written in nodejs based on socket.io and express 訊息實時推送,支援線上使用者數實時統計。基於Socket.IO開發,使用websock

《Thinking in Java》 And 《Effective Java》啃起來

大學 前言 技術 數據結構和算法 解決 一句話 定義 應該 太多的 前言   今天從京東入手了兩本書,《Thinking in Java》(第四版) 和 《Effective Java》(第二版)。都可以稱得上是硬書,需要慢慢啃的,預定計劃是在今年前把這兩本書啃完。哈哈,可

Thinking in Java(第四版)—— 第二章 一切皆對象

ati 靜態 數據類型 thinking short str 變量 屬於 字符 一.對象保存的位置 寄存器(cpu) 棧(變量) 堆(對象) 靜態域(static) 常量池(string) 非內存區池 二.基本數據類型 整數型 byte short int