Polishing documentation

This commit is contained in:
Oleg Zhurakousky
2019-11-22 11:50:15 +01:00
parent 744e32aed4
commit 6bb6656ae2
3 changed files with 66 additions and 66 deletions

View File

@@ -31,28 +31,3 @@ 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.
Functions can also 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. There is also support for `Message<Pojo>` where the
message headers are copied from the incoming event, depending on the
adapter. The web adapter also supports conversion from form-encoded
data to a `Map`, and if you are using the function with Spring Cloud
Stream then all the conversion and coercion features for message
payloads will be applicable as well.
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.

View File

@@ -22,46 +22,36 @@ include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/maste
include::getting-started.adoc[]
== Function Catalog and Flexible Function Signatures
== Programming model
=== Function Catalog and Flexible Function Signatures
One of the main features of Spring Cloud Function is to adapt and support a range of type signatures for user-defined functions,
while providing a consistent execution model.
That's why all user defined functions are transformed into a canonical representation by `FunctionCatalog`, using primitives
defined by the https://projectreactor.io/[Project Reactor] (i.e., `Flux<T>` and `Mono<T>`).
Users can supply a bean of type `Function<String,String>`, for instance, and the `FunctionCatalog` will wrap it into a
`Function<Flux<String>,Flux<String>>`.
Using Reactor based primitives not only helps with the canonical representation of user defined functions, but it also
facilitates a more robust and flexible(reactive) execution model.
That's why all user defined functions are transformed into a canonical representation by `FunctionCatalog`.
While users don't normally have to care about the `FunctionCatalog` at all, it is useful to know what
kind of functions are supported in user code.
It is also important to understand that Spring Cloud Function provides first class support for reactive API
provided by https://projectreactor.io/[Project Reactor] allowing reactive primitives such as `Mono` and `Flux`
to be used as types in user defined functions providing greater flexibility when choosing programming model for
your function implementation.
Reactive programming model also enables functional support for features that would be otherwise difficult to impossible to implement
using imperative programming style. For more on this please read <<Function Arity>> section.
=== Java 8 function support
Generally speaking users can expect that if they write a function for
a plain old Java type (or primitive wrapper), then the function
catalog will wrap it to a `Flux` of the same type. If the user writes
a function using `Message` (from spring-messaging) it will receive and
transmit headers from any adapter that supports key-value metadata
(e.g. HTTP headers). Here are the details.
Spring Cloud Function embraces and builds on top of the 3 core functional interfaces defined by Java
and available to us since Java 8.
|===
| User Function | Catalog Registration |
| `Function<S,T>` | `Function<Flux<S>, Flux<T>>` |
| `Function<Message<S>,Message<T>>` | `Function<Flux<Message<S>>, Flux<Message<T>>>` |
| `Function<Flux<S>, Flux<T>>` | `Function<Flux<S>, Flux<T>>` (pass through) |
| `Supplier<T>` | `Supplier<Flux<T>>` |
| `Supplier<Flux<T>>` | `Supplier<Flux<T>>` |
| `Consumer<T>` | `Function<Flux<T>, Mono<Void>>` |
| `Consumer<Message<T>>` | `Function<Flux<Message<T>>, Mono<Void>>` |
| `Consumer<Flux<T>>` | `Consumer<Flux<T>>` |
|===
- Supplier<O>
- Function<I, O>
- Consumer<I>
==== Supplier
As you can see from the table above Supplier can be _reactive_ - `Supplier<Flux<T>>`
Supplier can be _reactive_ - `Supplier<Flux<T>>`
or _imperative_ - `Supplier<T>`. From the invocation standpoint this should make no difference
to the implementor of such Supplier. However, when used within frameworks
(e.g., https://spring.io/projects/spring-cloud-stream[Spring Cloud Stream]), Suppliers, especially reactive,
@@ -108,17 +98,6 @@ of a non publisher type (which is normal), it will be converted to a
function that returns a publisher, so that it can be subscribed to in
a controlled way.
=== Function Component Scan
Spring Cloud Function will scan for implementations of `Function`,
`Consumer` and `Supplier` in a package called `functions` if it
exists. Using this feature you can write functions that have no
dependencies on Spring - not even the `@Component` annotation is
needed. If you want to use a different package, you can set
`spring.cloud.function.scan.packages`. You can also use
`spring.cloud.function.scan.enabled=false` to switch off the scan
completely.
=== Function Composition
Function Composition is a feature that allows one to compose several functions into one.
@@ -199,6 +178,41 @@ due to the nature of the reactive functions which are invoked only once to pass
is handled by the reactor, hence we can not access and/or rely on the routing instructions communicated via individual
values (e.g., Message).
=== Function Arity
There are times when a stream of data needs to be categorized and organized. For example,
consider a classic big-data use case of dealing with unorganized data containing, lets say,
orders and invoices, and you want each to go into a separate data store.
This is where function arity (functions with multiple inputs and outputs) support
comes to play.
Lets look at an example of such a function (full implementation details are available
https://github.com/spring-cloud/spring-cloud-stream/blob/master/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/function/MultipleInputOutputFunctionTests.java#L342[here]),
[source, java]
----
@Bean
public Function<Flux<Integer>, Tuple2<Flux<String>, Flux<String>>> organise() {
return flux -> ...;
}
----
Given that Project Reactor is a core dependency of SCF, we are using its Tuple library.
Tuples give us a unique advantage by communicating to us both _cardinality_ and _type_ information.
Both are extremely important in the context of SCSt. Cardinality lets us know
how many input and output bindings need to be created and bound to the corresponding
inputs and outputs of a function. Awareness of the type information ensures proper type
conversion.
Also, this is where the index part of the naming convention for binding
names comes into play, since, in this function, the two output binding
names are `organise-out-0` and `organise-out-1`.
IMPORTANT: IMPORTANT: At the moment, function arity is *only* supported for reactive functions
(`Function<TupleN<Flux<?>...>, TupleN<Flux<?>...>>`) centered on Complex event processing
where evaluation and computation on confluence of events typically requires view into a
stream of events rather than single event.
=== Kotlin Lambda support
We also provide support for Kotlin lambdas (since v2.0).
@@ -230,6 +244,17 @@ same rules for signature transformation outlined in "Java 8 function support" se
To enable Kotlin support all you need is to add `spring-cloud-function-kotlin` module to your classpath which contains the appropriate
autoconfiguration and supporting classes.
=== Function Component Scan
Spring Cloud Function will scan for implementations of `Function`,
`Consumer` and `Supplier` in a package called `functions` if it
exists. Using this feature you can write functions that have no
dependencies on Spring - not even the `@Component` annotation is
needed. If you want to use a different package, you can set
`spring.cloud.function.scan.packages`. You can also use
`spring.cloud.function.scan.enabled=false` to switch off the scan
completely.
== Standalone Web Applications
The `spring-cloud-function-web` module has autoconfiguration that
@@ -309,7 +334,7 @@ public class DeployFunctionDemo {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(DeployFunctionDemo.class,
"--spring.cloud.function.location=..../target/uppercase-0.0.1-SNAPSHOT.jar",
"--spring.cloud.function.function-name=uppercase");
"--spring.cloud.function.definition=uppercase");
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
Function<String, String> function = catalog.lookup("uppercase");

View File

@@ -164,7 +164,7 @@ public class FunctionDeployerTests {
public void testWithMainAndStartClassAndSpringConfiguration() throws Exception {
String[] args = new String[] {
"--spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar",
"--spring.cloud.function.function-name=uppercase" };
"--spring.cloud.function.definition=uppercase" };
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
Function<Message<byte[]>, Message<byte[]>> function = catalog.lookup("uppercase", "application/json");