351 lines
13 KiB
Plaintext
351 lines
13 KiB
Plaintext
// Do not edit this file (e.g. go instead to src/main/asciidoc)
|
|
|
|
image::https://travis-ci.org/spring-cloud/spring-cloud-function.svg?branch=master[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-function]
|
|
|
|
== Introduction
|
|
|
|
Spring Cloud Function is a project with the following high-level goals:
|
|
|
|
* _Promote the implementation of business logic via functions._
|
|
* _Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task._
|
|
* _Support a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS)._
|
|
* _Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers._
|
|
|
|
It abstracts 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):
|
|
|
|
[source,java]
|
|
----
|
|
@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. Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk[Apache OpenWhisk] 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/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 a `Processor` if
|
|
`spring-cloud-function-stream` is on the classpath and a
|
|
`spring.cloud.function.stream.endpoint` property 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" localhost: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" localhost:9001/words
|
|
----
|
|
|
|
=== Register a Consumer:
|
|
|
|
----
|
|
./registerConsumer.sh -n print -t String -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 localhost: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
|
|
...
|
|
----
|
|
|
|
== Building
|
|
|
|
:jdkversion: 1.7
|
|
|
|
=== Basic Compile and Test
|
|
|
|
To build the source you will need to install JDK {jdkversion}.
|
|
|
|
Spring Cloud uses Maven for most build-related activities, and you
|
|
should be able to get off the ground quite quickly by cloning the
|
|
project you are interested in and typing
|
|
|
|
----
|
|
$ ./mvnw install
|
|
----
|
|
|
|
NOTE: You can also install Maven (>=3.3.3) yourself and run the `mvn` command
|
|
in place of `./mvnw` in the examples below. If you do that you also
|
|
might need to add `-P spring` if your local Maven settings do not
|
|
contain repository declarations for spring pre-release artifacts.
|
|
|
|
NOTE: Be aware that you might need to increase the amount of memory
|
|
available to Maven by setting a `MAVEN_OPTS` environment variable with
|
|
a value like `-Xmx512m -XX:MaxPermSize=128m`. We try to cover this in
|
|
the `.mvn` configuration, so if you find you have to do it to make a
|
|
build succeed, please raise a ticket to get the settings added to
|
|
source control.
|
|
|
|
For hints on how to build the project look in `.travis.yml` if there
|
|
is one. There should be a "script" and maybe "install" command. Also
|
|
look at the "services" section to see if any services need to be
|
|
running locally (e.g. mongo or rabbit). Ignore the git-related bits
|
|
that you might find in "before_install" since they're related to setting git
|
|
credentials and you already have those.
|
|
|
|
The projects that require middleware generally include a
|
|
`docker-compose.yml`, so consider using
|
|
http://compose.docker.io/[Docker Compose] to run the middeware servers
|
|
in Docker containers. See the README in the
|
|
https://github.com/spring-cloud-samples/scripts[scripts demo
|
|
repository] for specific instructions about the common cases of mongo,
|
|
rabbit and redis.
|
|
|
|
NOTE: If all else fails, build with the command from `.travis.yml` (usually
|
|
`./mvnw install`).
|
|
|
|
=== Documentation
|
|
|
|
The spring-cloud-build module has a "docs" profile, and if you switch
|
|
that on it will try to build asciidoc sources from
|
|
`src/main/asciidoc`. As part of that process it will look for a
|
|
`README.adoc` and process it by loading all the includes, but not
|
|
parsing or rendering it, just copying it to `${main.basedir}`
|
|
(defaults to `${basedir}`, i.e. the root of the project). If there are
|
|
any changes in the README it will then show up after a Maven build as
|
|
a modified file in the correct place. Just commit it and push the change.
|
|
|
|
=== Working with the code
|
|
If you don't have an IDE preference we would recommend that you use
|
|
http://www.springsource.com/developer/sts[Spring Tools Suite] or
|
|
http://eclipse.org[Eclipse] when working with the code. We use the
|
|
http://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools
|
|
should also work without issue as long as they use Maven 3.3.3 or better.
|
|
|
|
==== Importing into eclipse with m2eclipse
|
|
We recommend the http://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with
|
|
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
|
|
marketplace".
|
|
|
|
NOTE: Older versions of m2e do not support Maven 3.3, so once the
|
|
projects are imported into Eclipse you will also need to tell
|
|
m2eclipse to use the right profile for the projects. If you
|
|
see many different errors related to the POMs in the projects, check
|
|
that you have an up to date installation. If you can't upgrade m2e,
|
|
add the "spring" profile to your `settings.xml`. Alternatively you can
|
|
copy the repository settings from the "spring" profile of the parent
|
|
pom into your `settings.xml`.
|
|
|
|
==== Importing into eclipse without m2eclipse
|
|
If you prefer not to use m2eclipse you can generate eclipse project metadata using the
|
|
following command:
|
|
|
|
[indent=0]
|
|
----
|
|
$ ./mvnw eclipse:eclipse
|
|
----
|
|
|
|
The generated eclipse projects can be imported by selecting `import existing projects`
|
|
from the `file` menu.
|
|
|
|
|
|
== Contributing
|
|
|
|
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
|
and follows a very standard Github development process, using Github
|
|
tracker for issues and merging pull requests into master. If you want
|
|
to contribute even something trivial please do not hesitate, but
|
|
follow the guidelines below.
|
|
|
|
=== Sign the Contributor License Agreement
|
|
Before we accept a non-trivial patch or pull request we will need you to sign the
|
|
https://cla.pivotal.io/sign/spring[Contributor License Agreement].
|
|
Signing the contributor's agreement does not grant anyone commit rights to the main
|
|
repository, but it does mean that we can accept your contributions, and you will get an
|
|
author credit if we do. Active contributors might be asked to join the core team, and
|
|
given the ability to merge pull requests.
|
|
|
|
=== Code of Conduct
|
|
This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc[code of
|
|
conduct]. By participating, you are expected to uphold this code. Please report
|
|
unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
|
|
|
=== Code Conventions and Housekeeping
|
|
None of these is essential for a pull request, but they will all help. They can also be
|
|
added after the original pull request but before a merge.
|
|
|
|
* Use the Spring Framework code format conventions. If you use Eclipse
|
|
you can import formatter settings using the
|
|
`eclipse-code-formatter.xml` file from the
|
|
https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring
|
|
Cloud Build] project. If using IntelliJ, you can use the
|
|
http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
|
Plugin] to import the same file.
|
|
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
|
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
|
for.
|
|
* Add the ASF license header comment to all new `.java` files (copy from existing files
|
|
in the project)
|
|
* Add yourself as an `@author` to the .java files that you modify substantially (more
|
|
than cosmetic changes).
|
|
* Add some Javadocs and, if you change the namespace, some XSD doc elements.
|
|
* A few unit tests would help a lot as well -- someone has to do it.
|
|
* If no-one else is using your branch, please rebase it against the current master (or
|
|
other target branch in the main project).
|
|
* When writing a commit message please follow http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
|
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
|
message (where XXXX is the issue number).
|