Files
spring-cloud-function/spring-cloud-function-rsocket
Oleg Zhurakousky 0abce5a2b8 GH-652 Improve error messaging and function exist check
FunctionRSocketUtils looks up function by a bean name hen it attempts  to determine if there is a remote routing. That is not correct since bean name may not exist if function was manually registered. Also, the error message 'Must only contain one output redirect' was not clear as it was not showing the actual function name for propper debugging

Resolves #651
2021-02-22 12:19:57 +01:00
..
2020-07-16 13:35:21 +02:00
2021-02-01 18:56:47 +00:00

Introduction

Spring Cloud Function allows you to invoke function via RSocket. While you can read more about RSocket and its java implementation here, this section will describe the parts relevant to Spring Cloud Function integration.

Programming model

From the user perspective bringing RSocket does not change the implementation of functions or any of its features, such as type conversion, composition, POJO functions etc. And while RSocket allows first class reactive interaction over the network supporting important reactive features such as back pressure, users of Spring Cloud Function still have freedom to implement their business logic using reactive or imperative functions delegating any adjustment needed to apply proper invocation model to the framework.

To use RSocket integration all you need is to add spring-cloud-function-rsocket dependency to your classpath

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-function-rsocket</artifactId>
 </dependency>

To interact with functions via RSocket we rely on Spring Boot support for RSocket and RSocketRequester.Builder API. The code below shows the key parts and you can get more details on various interaction models from this test case.

@Bean
public Function<String, String> uppercase() {
	return v -> v.toUpperCase();
}

. . .

RSocketRequester.Builder rsocketRequesterBuilder =
				applicationContext.getBean(RSocketRequester.Builder.class);

rsocketRequesterBuilder.tcp("localhost", port)
	.route(“uppercase")
	.data("\"hello\"")
	.retrieveMono(String.class)
	.subscribe(System.out::println);

Once connected to RSocket we use route operation to specify which function we want to invoke providing the actual payload via data operation. Then we use one of the retrieve operations that best suits our desired interaction (RSocket supports multiple interaction models such as fire-and-forget, request-reply etc.)

If you want to provide additional information that you would normally communicate via Message headers, you can use metadata operation for that.

rsocketRequesterBuilder.tcp("localhost", port)
	.route(“uppercase”)
	.metadata("{\”header_key\":\”header-value\"}", MimeTypeUtils.APPLICATION_JSON)		
	.data("\"hello\"")
	.retrieveMono(String.class)
	.subscribe(System.out::println);

Function Definitions

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. 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.

2 - 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 The spring.cloud.function.definition property is the last in the list allowing you to simply route("") to empty string.

You can also look at one of the RSocket samples that is also introduces you to Cloud Events