Being a new bee in Spring cloud, I am developing multi-function spring cloud function application which is working fine with spring-cloud-function-starter-web (3.0.9.RELEASE) dependency. Note: I have different function in different package and with below configuration it is working fine.
cloud:
function:
scan:
packages: zoo.app1.vanilla
For example, with [POST] localhost:8080/func1
it is invoking Func1 implements Function<I, O>
. Now I want to introduce routing. For that I have only changed the below in application.yml
cloud:
function:
definition: functionRouter
routing-expression: headers['function.name']
scan:
packages: zoo.app1.vanilla
Now when I invoke using
curl --location --request POST 'http://localhost:8080/functionRouter' \
--header 'function.name: func1' \
--header 'Content-Type: text/plain' \
--data-raw '1'
The exception is
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'headers' cannot be found on object of type 'reactor.core.publisher.FluxMapFuseable' - maybe not public or not valid?
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:217) ~[spring-expression-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104) ~[spring-expression-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:91) ~[spring-expression-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:55) ~[spring-expression-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:91) ~[spring-expression-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:117) ~[spring-expression-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:375) ~[spring-expression-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.cloud.function.context.config.RoutingFunction.functionFromExpression(RoutingFunction.java:173)
Now when I look into the code I found, RequestProcessor
with below function
private Object getTargetIfRouting(FunctionWrapper wrapper, Object function) {
if (function instanceof RoutingFunction) {
String name = wrapper.headers.get("function.name").iterator().next();
function = this.functionCatalog.lookup(name);
}
return function;
}
It seems like, by default it expects "function.name" in message header in order to route, hence I thought to comment out routing-expression
line in application.yml, it is getting into infinite loop resulting in stackoverflow error
2020-08-13 11:47:16.454 DEBUG 85560 --- [nio-8080-exec-1] o.s.c.f.c.c.SimpleFunctionRegistry : Applying function: functionRouter
2020-08-13 11:47:16.454 INFO 85560 --- [nio-8080-exec-1] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function 'functionRouter' with acceptedOutputTypes: []
2020-08-13 11:47:16.454 INFO 85560 --- [nio-8080-exec-1] o.s.c.f.context.config.RoutingFunction : Resolved function from provided [definition] property functionRouter
2020-08-13 11:47:16.454 DEBUG 85560 --- [nio-8080-exec-1] o.s.c.f.c.c.SimpleFunctionRegistry : Applying function: functionRouter
2020-08-13 11:47:16.454 INFO 85560 --- [nio-8080-exec-1] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function 'functionRouter' with acceptedOutputTypes: []
2020-08-13 11:47:16.454 INFO 85560 --- [nio-8080-exec-1] o.s.c.f.context.config.RoutingFunction : Resolved function from provided [definition] property functionRouter
2020-08-13 11:47:16.454 DEBUG 85560 --- [nio-8080-exec-1] o.s.c.f.c.c.SimpleFunctionRegistry : Applying function: functionRouter
2020-08-13 11:47:16.454 INFO 85560 --- [nio-8080-exec-1] o.s.c.f.c.c.SimpleFunctionRegistry : Looking up function 'functionRouter' with acceptedOutputTypes: []
2020-08-13 11:47:16.454 INFO 85560 --- [nio-8080-exec-1] o.s.c.f.context.config.RoutingFunction : Resolved function from provided [definition] property functionRouter
2020-08-13 11:47:16.454 DEBUG 85560 --- [nio-8080-exec-1] o.s.c.f.c.c.SimpleFunctionRegistry : Applying function: functionRouter
2020-08-13 11:47:16.468 ERROR 85560 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause
Am I doing anything wrong? Even if this works, will it recognize the different Functions that I have as per spring.cloud.function.scan.packages
property? Please help. Also I have two more questions,
in some blog/posts/documents it seems I can pass the spring.cloud.function.definition
as http header. If this holds true for this 3.0.9.RELEASE, then do I need to mention same property in application.yml?
can I use spring.cloud.function.definition=func1;func2
without using routingFunction
and expect the routing behaviour working properly? Or this is intended for something other feature?
I have never tested/played around different configuration option because of the above issue. Please forgive my little knowledge on spring cloud or if I have asked any childish question.
Edit
After debugging and with help of Spring documentation I found the right configuration
cloud:
function:
scan:
packages: zoo.app1.vanilla
stream:
function:
routing:
enabled: true
With this configuration it is able to route the message to my function, but only for the first time. Now that is something that made me completely confused. Once I start the application and hit from postman, it is able to identify the actual function and converts the input into GenericMessage
as expected (though failing to parse request body later). But when I hit second time (and onwards) it is not able to even parse my input to GenericMessage and gives me different error. And this is repeatable behavior.
For reference Please find the log of two consecutive requests (along with postman curl)
First request : proper routing Second request : routing failure
The issue got resolved in 3.1.0.RELEASE, Thanks Oleg Zhurakousky & Spring team. Ref: Issue Tracker