Introduction
AWS Lambda is another way to use cloud computing on Amazon’s AWS.
It allows you deliver you code and run in production without any server management.
It is auto-scale, high available and you pay only when you are using your function.
Creating the Application
For this basic example, I choose the spring as framework because most of my webservices are created using this framework.
If you are using eclipse, you can install the AWS Toolkit for eclipse. It is very helpful during development and testing stages.
First, you have create the pom.xml file:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>br.com.presba</groupId> <artifactId>presba-lambda</artifactId> <version>0.0.1</version> <name>presba-lambda</name> <properties> <spring.version>4.0.1.RELEASE</spring.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
As you can see, the dependencies for the spring framework and aws lambda are defined and the plugin maven-shade-plugin is set in the build section.
Below are the structure and the files used in this project.
presba-lambda src/main/java br.com.presba dao BasicSample.java Application.java LambaFunctionHandler.java src/main/resources application-context.xml
application-context.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"> <context:component-scan base-package="br.com.presba" /> </beans>
Application.java
package br.com.presba; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { private static ApplicationContext springContext = null; private static ApplicationContext getSpringContext() { if (springContext == null) { synchronized (ApplicationContext.class) { if (springContext == null) { springContext = new ClassPathXmlApplicationContext("/application-context.xml"); } } } return springContext; } public static <T> T getBean(Class<T> clazz) { return getSpringContext().getBean(clazz); } }
In the Application.java file is created the ApplicationContext using singleton pattern and the ApplicationContext.getBean is encapsulated in the Application.getBean protecting the application context for been accessed from other classes.
LambdaFunctionHandler.java
package br.com.presba; import java.util.Calendar; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import br.com.presba.dao.BasicSample; public class LambdaFunctionHandler implements RequestHandler<String, String> { private BasicSample basicSample; public String handleRequest(String input, Context context) { basicSample = Application.getBean(BasicSample.class); context.getLogger().log("AWS Request ID: " + context.getAwsRequestId()); context.getLogger().log("Input: " + input + " at " + Calendar.getInstance().getTimeInMillis()); return basicSample.doSomething(input); } }
In the LambdaFunctonHandler.java the RequestHandler interface is implemented in order to receive the AWS Lambda call.
BasicSample.java
package br.com.presba.dao; import org.springframework.stereotype.Component; @Component public class BasicSample { public String doSomething(String input) { return "Something has done with the input " + input; } }
In this BasicSample.java file the method doSomething uses the input string and returns a new string.
Deploying on AWS
The AWS Toolkit helps us on this step. The only thing we have to do is open the any .java file, open the context menu (right-click), chose “AWS Lambda” and then “Upload function to AWS Lambda…” option.
You will see the window below:
Chose the “Create a new Lambda function” option, type the name BasicSampleFunction and click “Next”.
In the window above, you must create a IAM role and a S3 Bucket for your function.
You also need to change the Memory to 512MB, because with less memory, the application takes longer during cold start.
Click Finish and wait until your function is deployed.
Running the Function
It’s time to test our work. Right click on any .java file, chose “AWS Lambda” and then “Run function on AWS Lambda…” option.
In the window below, type any text you want to pass to your lambda function and click “Invoke”
In the first execution, it will take some time to start your application (~ 3 sec).
If everything works fine, you will see the output in the console.
Troubleshooting
If you want to see the log of your call, you can go to the CloudWatch and click on the Log in the left menu.
Look for your function, the log should be /aws/lambda/FunctionName, click on it and a new window will appear.
Chose the first (or the only) Log Stream to see the log.
Conclusion
This is just a start point for the AWS Lambda, you can try some other frameworks instead of spring.
In the next post I’ll show how to create a API Gateway and call the lamba function.
Is there anything that would prevent the spring context from being loaded on every call to this lambda function? If not or if the context needs to be loaded frequently, the response time will not be good right? Is there some way to reduce the impact of spring context loading in a lambda function call? Does it even make sense to use spring in a lambda function? I suppose your spring context could be smaller and lighter since you would be focused on a single api, but it seems to me that use of the spring context makes more sense for a full application server or batch job.
LikeLike
I totally agree with you. Does not make sense to use spring in lambda function, however you may have some legacy app running spring and you need to deploy it fast and serverless.
LikeLike
Hi,
Somehow I am getting below error while running above example on AWS. Any idea?
errorMessage”: “Line 12 in XML document from class path resource [application-context.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 12; columnNumber: 113; cvc-elt.1: Cannot find the declaration of element ‘beans’.”,
“errorType”: “org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException”,
LikeLike
Are you using the same version of spring as me? Also, did you change de xsd confing in the application-context.xml?
Make sure that the parameter xsi:schemaLocation is the same as below:
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"
LikeLike
What about the latest version of spring where we do not use .xml file rather we use annotation based configuration file. How will be the context in that case?
LikeLike
I haven’t tried it yet, but I recommend not creating a new spring app for aws lambda, it’s only recommended when you already have the code.
LikeLike