I am receiving a MonoError caused by an IllegalStateException with the cause 'The underlying HTTP client completed without emitting a response.'.
NetworkList is a class contains lists of a network topology and has a setList which is called from within the method convertJsonToPojo.
client is a WebClient (spring-webflux) in Spring 5 (5.0.4.RELEASE) using reactor-netty (0.7.5.RELEASE).
The following snippet shows how I am using WebClient to make asynchronous calls to type = BOARDS, CARDS, PORTS, ROUTERS, etc where the network name is passed in as projectName and the topology type is passed in as type.
NetworkList netList = new NetworkList();
client.get().uri("/{project}/{type}",projectName,type).retrieve().bodyToMono(String.class).subscribe(json -> this.convertJsonToPojo(Type.BOARD, json, netList));
The rest service I am hitting against is a singular service which returns json representing the components asked for.
This seems similar to Reactive WebClient Not Emitting A Response but a response within points to the following reactor-netty issue Issue 138. That issue indicates fixed as of 0.6.6.RELEASE.
So my question is am I using WebClient wrong unknowingly or should I submit this as an unfixed bug? I am fairly new to Spring 5 and the Reactive library so do not want to naively submit a bug.
Thank you for your time in advance!
PS - I have this working with RestTemplate and CompletableFuture so no need to offer that suggestion as an alternative. My tasking is to begin the migration to a reactive architecture. Thank you!
UPDATE - I believe I understand now what is going on but would like a confirmation from someone. The rest service I am hitting against is a traditional one and not a Reactive Stream. Thus, there are no 'stage' updates being shared during the call. .subscribe() is expecting a reactive stream and thus never received the complete stage. replacing .subscribe() with .block() forced a completion and the code works now. So my learning from this is that when dealing with traditional rest services a block is required. Can someone confirm that please? Thank you!
Related
I am working on a project which until now has been using the org.springframework.web.client.RestTemplate
I have recently understood that this class is to be deprecated in favour of the Asynch org.springframework.web.reactive.function.client.WebClient framework.
Now I am massively in favour of this, as my application is suffering from long delays on waiting from
responses from RestTemplate (GET) calls (in which time I could be doing database stuff etc.).
The problem that I have now is that if I make a call like:
final Mono<String> call = webClient
.get()
.uri("/base/recordPath/1?format=json")
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class)
when I make a subsequent call like:
System.out.println(call.block());
I get the expected output (a String version of a populated Json Object).
however if I change the earlier call to (which I want to do!):
final Mono<JsonObject> call = webClient
.get()
.uri("/base/recordPath/1?format=json")
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JsonObject.class)
when I do:
System.out.println(call.block());
I just get {} instead of a populated JsonObject
So it looks like the bodyToMono() hasn't done what 'I' expected
When I used RestTemplate, there was a way to register serialisers with the Template (though this wasn't necessary for JsonObject),
is this also necessary with WebClient? If so how do you do it?
I would be grateful of help.
Thanks
Bill
N.B. I'm not sure if this has any relevance, but the rest endpoint I am accessing does have an IP restriction, so if in some way the WebClient were to alter the originating IP, this may have some effect. Though I would have thought it would be more like a 4** of some sort, I'm not seeing any of those!
Or possibly a Media type issue, as it is going through a DMZ, where the guardian may be changing an 'unauthorised' request from application/json to text/* for example.
Another point which may have relevance is that to get a successful start of the application it is necessary to run it with the following property:
spring.main.web-application-type=none
Update
I now have my application running, though not as I want!
The issue appeared to be transitive dependencies imported by the team pom I am required to use (as a parent pom).
I now get a successful start of the project. But still find that the json Object (which is now Jackson) is still Empty (as reported by object.isEmpty()).
my dependencies/versions now are:
org.springframework:5.2.8.RELEASE
org.springframework.boot:2.3.3.RELEASE
com.fasterxml.jackson:2.11.2
I know I am fighting a parent pom which is against what am trying to do but would like to know what the dependencies I really need are
I am using spring webflux and I would like to queue multiple requests before sending an http call to external system using WebClient and would like to know what is the best practice to do such requirement.
#GetMapping
Mono<Result> get(#RequestParam Optional<String> input) {
//Here I have to somehow keep the input and make a call when I have X items, then I can write to response, how this need to be done? Also what sort of Mono has to be return here?
}
I have found this which can be a solution but still a bit unclear https://ducmanhphan.github.io/2019-08-25-How-to-use-Processor-in-Reactor-Java/
Would be appreciated if anybody explains.
I have started a few days ago to learn about fault tolerance solutions in microservices. I have some microservices in my ecosystem and they are now interconnected with Eureka service lookup. I used FeignClient to call from one to another. As I heard and read, that Hystrix is getting into maintenance, I wondered if I could use Resilience4J in Feign instead of Hystrix. Well, at least not from annotation level right now as it seems. I found a great Feign.Builder adapter to add resilience4j fault tolerance features above a FeignClient as a decorator (https://github.com/resilience4j/resilience4j/tree/master/resilience4j-feign) so I wanted to use it.
So I used this, added together the features and added the default encoder, decoder, etc. items into the feign builder. Turns out I have to finish of course my code with a .target call which creates my client proxy and I could not really do this with Eureka in a good way:
The first constructor, which takes the class type and the URL is hardcoded, so if I add an eureka next server query into this parameter, it is just a hardcoded url for one of the instances, this is not load balanced. Some kinda workaround could be that I create prototype-scope or similar short lived scoped beans of this client and always get the "next url" for the call. This adds lots of burden to use the clients in every class I make. At least as I saw it. Maybe I could add some kind of singleton helper bean around the prototyping, but again this is not a good design as I see
I thought maybe I could create an EurekaTarget from the Target interface, but of course none of the methods indicate any "end of lifecycle" things, not even the apply method. I thought maybe that is one point which is called before doing a service call, but I saw multiple calls towards it so I had to change the url for all calls.
Do you know any better solution to do this migration?
I guess you are using Spring Boot?
The next version v1.0.0 of Resilience4j will support the #FeignClient annotation.
There was a PR which added the functionality -> https://github.com/resilience4j/resilience4j/pull/579
You can then use it as follows:
#FeignClient(name = DUMMY_FEIGN_CLIENT_NAME)
#CircuitBreaker(name = DUMMY_FEIGN_CLIENT_NAME)
public interface DummyFeignClient {
String DUMMY_FEIGN_CLIENT_NAME = "dummyFeignClient";
#GetMapping(path = "/api/{param}")
void doSomething(#PathVariable(name = "param") String param);
}
We struggle to find a solution for the following scenario:
Situation
Receive a message via Spring Cloud Streamlistener
Invoke a REST-Service via Feign-Client
We have configured several Feign-RequestInterceptor to enrich
request header data.
We want to avoid passing every request header on the method call and like the central configuration approach of the request interceptors.
Problem:
How to access data from a specific message, which contains informations, that need to be added to every request call via the Feign-RequestInterceptor.
We don't have a Request-Context, as we come from a message.
Can we be sure , that the message consumption and the REST call is happening on the same thread? If yes, we could use the NamedThreadLocal to store the information.
Yes, unless you hand off to another thread in your StreamListener, the rest call will be made on the same thread (assuming you are using RestTemplate and not the reactive web client).
I'm having an issue with consuming a REST service in Spring 5. The scenario: user updates a field or two on a screen and clicks a button. That calls a Spring controller which in turn makes two REST calls in series to gather information that is then returned to the browser. Our current production version uses Spring 4 and works great. But after upgrading to 5, the service invocations fail unless I put the server into debug mode and debug through the part of the code that does the two calls. If I debug, it works as expected. If I don't debug, I get a NullPointerException because the code uses information retrieved by the REST call that isn't there because it closed the connection before the REST service could return the information. Maybe there's a new dependency I missed or something I've overlooked when I upgraded to Spring 5 from 4, or something, but I've never seen anything like this.
Edit: Here's the code responsible for the REST service call:
ResponseEntity<String> entity = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<String>(){});
restTemplate is an autowired instance of org.springframework.web.client.RestTemplate. Whether the call completes or not, I always get a ResponseEntity. But when the call terminates early, the body data is null, which is incorrect. There will always be body data returned by call, and Spring 4 always captures it correctly. Spring 5 only captures it correctly when I debug through the code, but fails during normal execution - I see this when I examine captured data.
Discovered a solution - the problem likely resides in the JDK version I have. I switched the request factory to HttpComponentsClientHttpRequestFactory, and voila, no more problem.