GH-408 Enhance RoutingFunction with SpEL and application properties

- Added initial support for communicating routing instructions via SpEL thru both message headers and application properties
- Added support for communication function definition via application properties
- Added additional tests and updated documentation

Resolves #408
This commit is contained in:
Oleg Zhurakousky
2019-09-04 18:30:12 +02:00
parent 11ac6cd679
commit 2aed5abff8
11 changed files with 487 additions and 133 deletions

View File

@@ -123,30 +123,49 @@ you to invoke a single function which acts as a router to an actual function you
This feature is very useful in certain FAAS environments where maintaining configurations
for several functions could be cumbersome or exposing more then one function is not possible.
You enable this feature via `spring.cloud.function.routing.enabled` property setting it
to `true` (default is `false`).
This enables `RoutingFunction` under the name `router` which is loaded in FunctionCatalog.
The `RoutingFunction` is registered in _FunctionCatalog_ under the name `functionRouter`. For simplicity
and consistency you can also refer to `RoutingFunction.FUNCTION_NAME` constant.
This function has the following signature:
[source, java]
----
public class RoutingFunction implements Function<Publisher<Message<?>>, Publisher<?>>, Consumer<Publisher<Message<?>>> {
public class RoutingFunction implements Function<Object, Object> {
. . .
}
----
The routing instructions could be communicated in several ways;
This allows the above function to act as both `Function` and `Consumer`.
As you can see it takes `Message<?>` as an input argument. This allows you to communicate
the name of the actual function you want to invoke by providing `function.name` Message header.
*Message Headers*
In specific execution environments/models the adapters are responsible to translate and communicate `function.name`
via Message header. For example, when using _spring-cloud-function-web_ you can provide `function.name` as an HTTP
If the input argument is of type `Message<?>`, you can communicate routing instruction by setting one of
`spring.cloud.function.definition` or `spring.cloud.function.routing-expression` Message headers.
For more static cases you can use `spring.cloud.function.definition` header which allows you to provide
the name of a single function (e.g., `...definition=foo`) or a composition instruction (e.g., `...definition=foo|bar|baz`).
For more dynamic cases you can use `spring.cloud.function.routing-expression` header which allows
you to use Spring Expression Language (SpEL) and provide SpEL expression that should resolve
into definition of a function (as described above).
NOTE: SpEL evaluation context's root object is the
actual input argument, so in he case of `Message<?>` you can construct expression that has access
to both `payload` and `headers` (e.g., `spring.cloud.function.routing-expression=headers.function_name`).
In specific execution environments/models the adapters are responsible to translate and communicate
`spring.cloud.function.definition` and/or `spring.cloud.function.routing-expression` via Message header.
For example, when using _spring-cloud-function-web_ you can provide `spring.cloud.function.definition` as an HTTP
header and the framework will propagate it as well as other HTTP headers as Message headers.
Using Message also allows us to benefit from `MessageConverter`s to convert incoming request to the actual input type
of the target function
*Application Properties*
Routing instruction can also be communicated via `spring.cloud.function.definition`
or `spring.cloud.function.routing-expression` as application properties. The rules described in the
previous section apply here as well. The only difference is you provide these instructions as
application properties (e.g., `--spring.cloud.function.definition=foo`).
IMPORTANT: When dealing with reactive inputs (e.g., Publisher), routing instructions must only be provided via Function properties. This is
due to the nature of the reactive functions which are invoked only once to pass a Publisher and the rest
is handled by the reactor, hence we can not access and/or rely on the routing instructions communicated via individual
values (e.g., Message).
=== Kotlin Lambda support