102 lines
5.5 KiB
Plaintext
102 lines
5.5 KiB
Plaintext
*{project-version}*
|
|
|
|
|
|
The https://aws.amazon.com/[AWS] adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda.
|
|
|
|
[[introduction]]
|
|
== Introduction
|
|
|
|
include::adapters/aws-intro.adoc[]
|
|
|
|
[[functional-bean-definitions]]
|
|
== Functional Bean Definitions
|
|
|
|
Your functions will start much quicker if you can use functional bean definitions instead of `@Bean`. To do this make your main class
|
|
an `ApplicationContextInitializer<GenericApplicationContext>` and use the `registerBean()` methods in `GenericApplicationContext` to
|
|
create all the beans you need. You function need to be registered as a bean of type `FunctionRegistration` so that the input and
|
|
output types can be accessed by the framework. There is an example in github (the AWS sample is written in this style). It would
|
|
look something like this:
|
|
|
|
```java
|
|
@SpringBootConfiguration
|
|
public class FuncApplication implements ApplicationContextInitializer<GenericApplicationContext> {
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
FunctionalSpringApplication.run(FuncApplication.class, args);
|
|
}
|
|
|
|
public Function<Foo, Bar> function() {
|
|
return value -> new Bar(value.uppercase()));
|
|
}
|
|
|
|
@Override
|
|
public void initialize(GenericApplicationContext context) {
|
|
context.registerBean("function", FunctionRegistration.class,
|
|
() -> new FunctionRegistration<Function<Foo, Bar>>(function())
|
|
.type(FunctionTypeUtils.functionType(Foo.class, Bar.class)));
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
[[aws-context]]
|
|
== AWS Context
|
|
|
|
In a typical implementation of AWS Handler user has access to AWS _context_ object. With function approach you can have the same experience if you need it.
|
|
Upon each invocation the framework will add `aws-context` message header containing the AWS _context_ instance for that particular invocation. So if you need to access it
|
|
you can simply have `Message<YourPojo>` as an input parameter to your function and then access `aws-context` from message headers.
|
|
For convenience we provide AWSLambdaUtils.AWS_CONTEXT constant.
|
|
|
|
[[platform-specific-features]]
|
|
== Platform Specific Features
|
|
|
|
[[http-and-api-gateway]]
|
|
=== HTTP and API Gateway
|
|
|
|
AWS has some platform-specific data types, including batching of messages, which is much more efficient than processing each one individually. To make use of these types you can write a function that depends on those types. Or you can rely on Spring to extract the data from the AWS types and convert it to a Spring `Message`. To do this you tell AWS that the function is of a specific generic handler type (depending on the AWS service) and provide a bean of type `Function<Message<S>,Message<T>>`, where `S` and `T` are your business data types. If there is more than one bean of type `Function` you may also need to configure the Spring Boot property `function.name` to be the name of the target bean (e.g. use `FUNCTION_NAME` as an environment variable).
|
|
|
|
The supported AWS services and generic handler types are listed below:
|
|
|
|
|===
|
|
| Service | AWS Types | Generic Handler |
|
|
|
|
| API Gateway | `APIGatewayProxyRequestEvent`, `APIGatewayProxyResponseEvent` | `org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler` |
|
|
| Kinesis | KinesisEvent | org.springframework.cloud.function.adapter.aws.SpringBootKinesisEventHandler |
|
|
|===
|
|
|
|
|
|
For example, to deploy behind an API Gateway, use `--handler org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler` in your AWS command line (in via the UI) and define a `@Bean` of type `Function<Message<Foo>,Message<Bar>>` where `Foo` and `Bar` are POJO types (the data will be marshalled and unmarshalled by AWS using Jackson).
|
|
|
|
[[custom-runtime]]
|
|
== Custom Runtime
|
|
|
|
You can also benefit from https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html[AWS Lambda custom runtime] feature of AWS Lambda
|
|
and Spring Cloud Function provides all the necessary components to make it easy.
|
|
|
|
From the code perspective the application should look no different then any other Spring Cloud Function application.
|
|
The only thing you need to do is to provide a `bootstrap` script in the root of your zip/jar that runs the Spring Boot application.
|
|
and select "Custom Runtime" when creating a function in AWS.
|
|
Here is an example 'bootstrap' file:
|
|
```text
|
|
#!/bin/sh
|
|
|
|
cd ${LAMBDA_TASK_ROOT:-.}
|
|
|
|
java -Dspring.main.web-application-type=none -Dspring.jmx.enabled=false \
|
|
-noverify -XX:TieredStopAtLevel=1 -Xss256K -XX:MaxMetaspaceSize=128M \
|
|
-Djava.security.egd=file:/dev/./urandom \
|
|
-cp .:`echo lib/*.jar | tr ' ' :` com.example.LambdaApplication
|
|
```
|
|
The `com.example.LambdaApplication` represents your application which contains function beans.
|
|
|
|
Set the handler name in AWS to the name of your function. You can use function composition here as well (e.g., `uppecrase|reverse`).
|
|
That is pretty much all. Once you upload your zip/jar to AWS your function will run in custom runtime.
|
|
We provide a https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-aws-custom-new[sample project]
|
|
where you can also see how to configure yoru POM to properly generate the zip file.
|
|
|
|
The functional bean definition style works for custom runtimes as well, and is
|
|
faster than the `@Bean` style. A custom runtime can start up much quicker even than a functional bean implementation
|
|
of a Java lambda - it depends mostly on the number of classes you need to load at runtime.
|
|
Spring doesn't do very much here, so you can reduce the cold start time by only using primitive types in your function, for instance,
|
|
and not doing any work in custom `@PostConstruct` initializers.
|