From 9f1c99bae85ece5c602ef6f3d0c6257dbd536e51 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 24 Feb 2021 15:48:34 +0100 Subject: [PATCH] GH-654 Update routing and RSocket routing documentation with details on 'MessageRoutingCallback' Resolves #654 --- .../main/asciidoc/spring-cloud-function.adoc | 62 ++++++++++++++++++- spring-cloud-function-rsocket/README.md | 15 +++-- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-function.adoc b/docs/src/main/asciidoc/spring-cloud-function.adoc index 4b32b9ab0..62a91ec1c 100644 --- a/docs/src/main/asciidoc/spring-cloud-function.adoc +++ b/docs/src/main/asciidoc/spring-cloud-function.adoc @@ -142,7 +142,47 @@ public class RoutingFunction implements Function { . . . } ---- -The routing instructions could be communicated in several ways; +The routing instructions could be communicated in several ways. We support providing instructions via Message headers, System +properties as well as pluggable strategy. So let's look at some of the details + +*MessageRoutingCallback* + +The `MessageRoutingCallback` is a strategy to assist with determining the name of the route-to function definition. + +[source, java] +---- +public interface MessageRoutingCallback { + + /** + * Determines the name of the function definition to route incoming {@link Message}. + * + * @param message instance of incoming {@link Message} + * @return the name of the route-to function definition + */ + String functionDefinition(Message message); +} +---- + +All you need to do is implement it and and register it as a bean. The framework will automatically +pick it up and use it for routing decisions. +For example + +[source, java] +---- +@Bean +public MessageRoutingCallback customRouter() { + return new MessageRoutingCallback() { + @Override + public String functionDefinition(Message message) { + return (String) message.getHeaders().get("func_name"); + } + }; +} +---- + +In the preceding example you can see a very simple implementation of `MessageRoutingCallback` which determines the function definition from +`func_name` header of the incoming Message. + *Message Headers* @@ -170,8 +210,26 @@ or `spring.cloud.function.routing-expression` as application properties. The rul previous section apply here as well. The only difference is you provide these instructions as application properties (e.g., `--spring.cloud.function.definition=foo`). +NOTE: It is important to understand that providing `spring.cloud.function.definition` +or `spring.cloud.function.routing-expression` as Message headers will only work for imperative functions (e.g., `Function`). +That is to say that we can _only_ route ***per-message*** with imperative functions. With reactive functions we can not route +***per-message***. Therefore you can only provide your routing instructions as Application Properties. +It's all about unit-of-work. In imperative function unit of work is Message so we can route based on such unit-of-work. +With reactive function unit-of-work is the entire stream, so we'll act only on the instruction provided via application +properties and route the entire stream. + +*Order of priority for routing instructions* + +Given that we have several mechanisms of providing routing instructions it is important to understand the priorities for +conflict resolutions in the event multiple mechanisms are used at the same time, so here is the order: + +1. `MessageRoutingCallback` (If function is imperative will take over regardless if anything else is defined) +2. Message Headers (If function is imperative and no `MessageRoutingCallback` provided) +3. Application Properties (Any function) + + *Function Filtering* -Filtering is the type of routing where there are only tow paths - 'go' or 'discard'. In terms of functions it mean +Filtering is the type of routing where there are only two paths - 'go' or 'discard'. In terms of functions it mean you only want to invoke a certain function if some condition returns 'true', otherwise you want to discard input. However, when it comes to discarding input there are many interpretation of what it could mean in the context of your application. For example, you may want to log it, or you may want to maintain the counter of discarded messages. you may also want to do nothing at all. diff --git a/spring-cloud-function-rsocket/README.md b/spring-cloud-function-rsocket/README.md index d32b98df1..04dbabdcd 100644 --- a/spring-cloud-function-rsocket/README.md +++ b/spring-cloud-function-rsocket/README.md @@ -55,19 +55,22 @@ rsocketRequesterBuilder.tcp("localhost", port) .subscribe(System.out::println); ``` -### Function Definitions +### Order of priority for routing instructions As you can see from the preceding example, we provide function definition as a value to `route(..)` operator of `RSocketRequester.Builder`. -However that is not the only way. You can also use standard `spring.cloud.function.definition` property as well as `spring.cloud.function.routing-expression` property. +However that is not the only way. You can also use standard `spring.cloud.function.definition` property as well as `spring.cloud.function.routing-expression` or property or `MessageRoutingCallback` on the server side of the RSocket interaction (see "Function Routing and Filtering" section of reference manual). This raises a question of _order_ and _priorities_ when it comes to reconsiling a conflict in the event several ways of providing definition are used. So it is a mater of clearly stating the rule whcih is: -***1 - spring.cloud.function.routing-expression*** -The `spring.cloud.function.routing-expression` property takes precedence over all other ways of providing definition. So, in the event you may have also use `route(..)` operator or `spring.cloud.function.definition` property, they will be ignored if `spring.cloud.function.routing-expression` property is provided. +***1 - MessageRoutingCallback*** +The `MessageRoutingCallback` takes precedence over all other ways of providing function definition resolution. -***2 - route(..)*** +***2 - spring.cloud.function.routing-expression*** +The `spring.cloud.function.routing-expression` property takes next precedence. So, in the event you may have also use `route(..)` operator or `spring.cloud.function.definition` property, they will be ignored if `spring.cloud.function.routing-expression` property is provided. + +***3 - route(..)*** The next in line is `route(..)` operator. So in the event there are no `spring.cloud.function.routing-expression` property but you defined `spring.cloud.function.definition` property, it will be ignored in favor of definition provided by the `route(..)` operator. -***3 - spring.cloud.function.definition*** +***4 - spring.cloud.function.definition*** The `spring.cloud.function.definition` property is the last in the list allowing you to simply `route("")` to empty string.