201 lines
5.4 KiB
Plaintext
201 lines
5.4 KiB
Plaintext
Spring Cloud Function provides a new programming model for Spring Boot
|
|
applications, abstracting away all of the transport details and
|
|
infrastructure, allowing the developer to keep all the familiar tools
|
|
and processes, and focus firmly on business logic.
|
|
|
|
Here's a complete, executable, testable Spring Boot application
|
|
(implementing a simple string manipulation):
|
|
|
|
```
|
|
@SpringBootApplication
|
|
public class Application {
|
|
|
|
@Bean
|
|
public Function<Flux<String>, Flux<String>> uppercase() {
|
|
return flux -> flux.map(value -> value.toUpperCase());
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
SpringApplication.run(Application.class, args);
|
|
}
|
|
}
|
|
```
|
|
|
|
It's just a Spring Boot application, so it can be built, run and
|
|
tested, locally and in a CI build, the same way as any other Spring
|
|
Boot application. The `Function` is from `java.util` and `Flux` is a
|
|
http://www.reactive-streams.org/[Reactive Streams] `Publisher` from
|
|
https://projectreactor.io/[Project Reactor]. The function can be
|
|
accessed over HTTP or messaging.
|
|
|
|
Spring Cloud Function has 4 main features:
|
|
|
|
1. Wrappers for `@Beans` of type `Function`, `Consumer` and
|
|
`Supplier`, exposing them to the outside world as either HTTP
|
|
endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc.
|
|
|
|
2. Compiling strings which are Java function bodies into bytecode, and
|
|
then turning them into `@Beans` that can be wrapped as above.
|
|
|
|
3. Deploying a JAR file containing such an application context with an
|
|
isolated classloader, so that you can pack them together in a single
|
|
JVM.
|
|
|
|
4. TBD: adapters for AWS Lambda, and possibly other "serverless"
|
|
service providers.
|
|
|
|
== Getting Started
|
|
|
|
Build from the command line (and "install" the samples):
|
|
|
|
```
|
|
$ ./mvnw clean install
|
|
```
|
|
|
|
(If you like to YOLO add `-DskipTests`.)
|
|
|
|
Run one of the samples, e.g.
|
|
|
|
```
|
|
$ java -jar spring-cloud-function-samples/spring-cloud-function-sample/target/*.jar
|
|
```
|
|
|
|
This runs the app and exposes its functions over HTTP, so you can
|
|
convert a string to uppercase, like this:
|
|
|
|
```
|
|
$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello
|
|
HELLO
|
|
```
|
|
|
|
You can convert multiple strings (a `Flux<String>`) by separating them
|
|
with new lines
|
|
|
|
```
|
|
$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello
|
|
> World'
|
|
HELLOWORLD
|
|
```
|
|
|
|
(You can use `^Q^J` in a terminal to insert a new line in a literal
|
|
string like that.)
|
|
|
|
== Building and Running a Function
|
|
|
|
The sample `@SpringBootApplication` above has a function that can be
|
|
decorated at runtime by Spring Cloud Function to be an HTTP endpoint,
|
|
or a Stream processor, for instance with RabbitMQ, Apache Kafka or
|
|
JMS.
|
|
|
|
The `@Beans` can be `Function`, `Consumer` or `Supplier` (all from
|
|
`java.util`), and their parametric types can be String or POJO. A
|
|
`Function` is exposed as an HTTP POST if `spring-cloud-function-web`
|
|
is on the classpath, and as a Spring Cloud Stream `Processor` if
|
|
`spring-cloud-function-stream` is on the classpath and a
|
|
`function.name` is configured in the Spring environment. A `Consumer`
|
|
is also exposed as an HTTP POST, or as a Stream `Sink`. A `Supplier`
|
|
translates to an HTTP GET, or a Stream `Source`.
|
|
|
|
Functions can be of `Flux<String>` or `Flux<Pojo>` and Spring Cloud
|
|
Function takes care of converting the data to and from the desired
|
|
types, as long as it comes in as plain text or (in the case of the
|
|
POJO) JSON. TBD: support for `Flux<Message<Pojo>>` and maybe plain
|
|
`Pojo` types (Fluxes implied and implemented by the framework).
|
|
|
|
Functions can be grouped together in a single application, or deployed
|
|
one-per-jar. It's up to the developer to choose. An app with multiple
|
|
functions can be deployed multiple times in different "personalities",
|
|
exposing different functions over different physical transports.
|
|
|
|
== Deploying a Packaged Function
|
|
|
|
TBD: describe the deployer app.
|
|
|
|
== Dynamic Compilation
|
|
|
|
To run these examples, change into the `scripts` directory:
|
|
|
|
----
|
|
cd scripts
|
|
----
|
|
|
|
Also, start a RabbitMQ server locally (e.g. execute `rabbitmq-server`).
|
|
|
|
=== Start the Function Registry Service:
|
|
|
|
----
|
|
./function-registry.sh
|
|
----
|
|
|
|
=== Register a Function:
|
|
|
|
----
|
|
./registerFunction.sh -n uppercase -f "f->f.map(s->s.toString().toUpperCase())"
|
|
----
|
|
|
|
=== Run a REST Microservice using that Function:
|
|
|
|
----
|
|
./web.sh -f uppercase -p 9000
|
|
curl -H "Content-Type: text/plain" -H "Accept: text/plain" :9000/uppercase -d foo
|
|
----
|
|
|
|
=== Register a Supplier:
|
|
|
|
----
|
|
./registerSupplier.sh -n words -f "()->Flux.just(\"foo\",\"bar\")"
|
|
----
|
|
|
|
=== Run a REST Microservice using that Supplier:
|
|
|
|
----
|
|
./web.sh -s words -p 9001
|
|
curl -H "Accept: application/json" :9001/words
|
|
----
|
|
|
|
=== Register a Consumer:
|
|
|
|
----
|
|
./registerConsumer.sh -n print -f "System.out::println"
|
|
----
|
|
|
|
=== Run a REST Microservice using that Consumer:
|
|
|
|
----
|
|
./web.sh -c print -p 9002
|
|
curl -X POST -H "Content-Type: text/plain" -d foo :9002/print
|
|
----
|
|
|
|
=== Run Stream Processing Microservices:
|
|
|
|
First register a streaming words supplier:
|
|
|
|
----
|
|
./registerSupplier.sh -n wordstream -f "()->Flux.intervalMillis(1000).map(i->\"message-\"+i)"
|
|
----
|
|
|
|
Then start the source (supplier), processor (function), and sink (consumer) apps
|
|
(in reverse order):
|
|
|
|
----
|
|
./stream.sh -p 9103 -i uppercaseWords -c print
|
|
./stream.sh -p 9102 -i words -f uppercase -o uppercaseWords
|
|
./stream.sh -p 9101 -s wordstream -o words
|
|
----
|
|
|
|
The output will appear in the console of the sink app (one message per second, converted to uppercase):
|
|
|
|
----
|
|
MESSAGE-0
|
|
MESSAGE-1
|
|
MESSAGE-2
|
|
MESSAGE-3
|
|
MESSAGE-4
|
|
MESSAGE-5
|
|
MESSAGE-6
|
|
MESSAGE-7
|
|
MESSAGE-8
|
|
MESSAGE-9
|
|
...
|
|
----
|