Not able to output WebClient call results - java

I'm learning more about using Spring Webflux and experimenting with testing a simple async webservice call. I've looked at several examples and I can't see what I'm doing wrong. I have a service that makes a call to a third party API and all I want to do is output the Json response returned. I'm not converting the response into model objects just yet but this will be the next step if I can get basics working first. The code doesn't log any of the output of the webservice call and I've also tried sending to System.out::println and that also doesn't work. The output in the test only includes the following log output
023-01-04 00:53:46.622 INFO 19938 --- [ main] c.r.io.service.impl.ListlyServiceImpl : Starting call to Listly API
2023-01-04 00:53:52.395 INFO 19938 --- [ main] c.r.io.service.impl.ListlyServiceImpl : Exiting service call to Listly
However , when I put a break point on
listlyResponse.subscribe(listlyResp ->
log.info(listlyResp));
I can actually see the correct contents of the response from the web service call. Any ideas on what I'm doing wrong? This is the code
#Service
public class ListlyServiceImpl implements ListlyService {
private final static Logger log = LoggerFactory.getLogger(ListlyServiceImpl.class);
private final String baseUrl = "https://list.ly/api/v4";
#Override
public void callListlyService(String searchUrl) {
if (searchUrl == null) {
throw new RuntimeException("Search URL cannot be null");
}
log.info("Starting call to Listly API");
Mono<String> listlyResponse = WebClient.create(baseUrl)
.get()
.uri(uriBuilder -> uriBuilder
.path("/meta")
.queryParam("url","{url}")
.build(searchUrl))
.retrieve()
.bodyToMono(String.class);
listlyResponse.subscribe(listlyResp ->
log.info(listlyResp));
// listlyResponseFlux.subscribe(System.out::println);
log.info("Exiting service call to Listly");
}
}
I'm expecting to be able to output the contents of the web service call to the log output which is not working for some reason.

It is not clear without seeing your entire project, because I cannot see where you actually call your callListlyService() method.
But I can see you're calling it from Main thread.
I think you misunderstood the reactive paradigm.
As soon as you call subscribe() on reactive chain you should know that within the chain may happen the thread switching (depending on operators on your chain/some reactive API that may do thread switching)
In case of WebClient the retrive() subscribes from netty threads by default and the next executions will happen on reactor-%s thread
So when you call your service method, it makes subsribe() and returns immediately showing you log "Exiting service call to Listly"
and your method returns immediately, so you cannot see result of your call.
Just to see your result you can call block() on your reactive chain, that will force your calling thread to block and waiting the response from your WebClient. But this approach is not recommended in reactor because you're losing benefits from reactive-way. In real cases you should return Publisher<> from your method and , for example, subscribe by yourself where you need/use Spring WebFlux returning Publishers in your controller methods leaving subsribe process to WebFlux
One another way just for experimanetal purposes: you can place Thread.sleep(n) in the end of your test, where n - time in millis for your Main thread to wait. The time should be greater than your actual web-call

Related

How to turn a Mono into a truly asynchronous (not reactive!) method call?

I have a method
#Service
public class MyService {
public Mono<Integer> processData() {
... // very long reactive operation
}
}
In the normal program flow, I call this method asynchronously via a Kafka event.
For testing purposes I need to expose the method as a web service, but the method should be exposed as asynchronous: returning only HTTP code 200 OK ("request accepted") and continuing the data processing in the background.
Is it OK (= doesn't it have any unwanted side effects) just to call Mono#subscribe() and return from the controller method?
#RestController
#RequiredArgsConstructor
public class MyController {
private final MyService service;
#GetMapping
public void processData() {
service.processData()
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
}
}
Or is it better to do it like this (here I am confused by the warning from IntelliJ, maybe the same as https://youtrack.jetbrains.com/issue/IDEA-276018 ?):
public Mono<Void> processData() {
service.processData()
.subscribeOn(Schedulers.boundedElastic())
.subscribe(); // IntelliJ complains "Inappropriate 'subscribe' call" but I think it's a false alarm in my case(?)
return Mono.empty();
}
Or some other solution?
Is it OK (= doesn't it have any unwanted side effects) just to call Mono#subscribe() and return from the controller method?
There are side effects, but you may be ok living with them:
It truly is fire and forget - which means while you'll never be notified about a success (which most people realise), you'll also never be notified about a failure (which far fewer people realise.)
If the process hangs for some reason, that publisher will never complete, and you'll have no way of knowing. Since you're subscribing on the bounded elastic threadpool, it'll also tie up one of those limited threads indefinitely too.
The first point you might be fine with, or you might want to put some error logging further down that reactive chain as a side-effect somehow so you at least have an internal notification if something goes wrong.
For the second point - I'd recommend putting a (generous) timeout on your method call so it at least gets cancelled if it hasn't completed in a set time, and is no longer hanging around consuming resources. If you're running an asynchronous task, then this isn't a massive issue as it'll just consume a bit of memory. If you're wrapping a blocking call on the elastic scheduler then this is worse however, as you're tying up a thread in that threadpool indefinitely.
I'd also question why you need to use the bounded elastic scheduler at all here - it's used for wrapping blocking calls, which doesn't seem to be the foundation of this use case. (To be clear, if your service is blocking then you should absolutely wrap it on the elastic scheduler - but if not then there's no reason to do so.)
Finally, this example:
public Mono<Void> processData() {
service.processData()
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
return Mono.empty();
}
...is a brilliant example of what not to do, as you're creating a kind of "imposter reactive method" - someone may very reasonably subscribe to that returned publisher thinking it will complete when the underlying publisher completes, which obviously isn't what's happening here. Using a void return type and thus not returning anything is the correct thing to do in this scenario.
Your option with the following code is actually ok:
#GetMapping
public void processData() {
service.processData()
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
}
This is actually what you do in a #Scheduled method which simply returns nothing and you explicitly subscribe to the Mono or Flux so that elements are emitted.

Spring : call REST API after receiving response from another REST API

I have a controller that calls a webservice to start a batch job, when the result is returned, it should call another REST API based on this result. Then it should wait for the new result, and return this second result to user:
#RestController
public class LaunchController {
#PostMapping(path = "/launch", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<LaunchResult> launch(#Valid #RequestBody LaunchParams params) {
// in launch() I call the first REST API
LaunchResult result = myService.launch(params);
// here I need to call another REST API
AnotherResult result2 = callAnotherWebAPIBasedOnThisResult(result);
return ResponseEntity.ok(result2);
}
Now I want to know that is it good practice to do it like this (synchronously) and all in one controller ? Exist other way of doing this ?
Your controller is perfectly fine as it does not have any application logic inside and it actually calls the service methods. But It lacks the exception handling. You have catch with proper exceptions with try catch block or throws keyword.
The decision to convert the endpoint to an asychronous one depends on a few factors :
Is the batch job going to take time to be executed.
Can this process be converted to an asynchronous one.
Does the use case expect the user to wait until the action is completed.
If the your answer is yes, it's better to convert the endpoint to an ayschronous one and update the user with the details later after all processes including the batch processes are completed . It's always better NOT to keep the user waiting for a response. Non-blocking requests makes sense when you are dealing with a lot of data and processing needed for this data. Also, by making this request asynchronous you will have better control over the processing stages and provide the user with better statistics incase any of the processing stage resulted in failure. For instance the batch job could fail or the second rest api call could result in an error.

Understanding Spring's Web Reactive Framework

I am currently developing an application with SpringBoot 2, spring-boot-starter-webflux on netty and jOOQ.
Below is the code that I have come up with after hours of research and stackoverflow searches. I have built in a lot of
logging in order to see what's happening on which thread.
UserController:
#RequestMapping(value = "/user", method = RequestMethod.POST)
public Mono<ResponseEntity<Integer>> createUser(#RequestBody ImUser user) {
return Mono.just(user)
.map(it -> {
logger.debug("Receiving request on thread: " + Thread.currentThread().getName());
return it;
})
.map(userService::create)
.map(it -> {
logger.debug("Sending response on thread: " + Thread.currentThread().getName());
return ResponseEntity.status(HttpStatus.CREATED).body(it);
})
.mapError(DuplicateKeyException.class, e -> new SomeSpecialException(e.getMessage(), e));
}
UserService:
public int create(ImUser user) {
return Mono.just(user)
.subscribeOn(Schedulers.elastic())
.map(u -> {
logger.debug("UserService thread: " + Thread.currentThread().getName());
return imUserDao.insertUser(u);
})
.block();
}
UserDao:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public int insertUser(ImUser user) {
logger.debug("Insert DB on thread: " + Thread.currentThread().getName());
return dsl.insertInto(IM_USER,IM_USER.VERSION, IM_USER.FIRST_NAME, IM_USER.LAST_NAME, IM_USER.BIRTHDATE, IM_USER.GENDER)
.values(1, user.getFirstName(), user.getLastName(), user.getBirthdate(), user.getGender())
.returning(IM_USER.ID)
.fetchOne()
.getId();
}
The code works as expected, "Receiving request" and "Sending response" both run on the same thread (reactor-http-server-epoll-x)
while the blocking code ( the call to imUserDao.insertUser(u) ) runs on an elastic Scheduler thread (elastic-x).
The transaction is bound to the thread on which the annotated method is called (which is elastic-x) and thus works as expected (I have tested
it with a different method which is not posted here, to keep things simple).
Here is a log sample:
20:57:21,384 DEBUG admin.UserController| Receiving request on thread: reactor-http-server-epoll-7
20:57:21,387 DEBUG admin.UserService| UserService thread: elastic-2
20:57:21,391 DEBUG admin.ExtendedUserDao| Insert DB on thread: elastic-2
20:57:21,393 DEBUG tools.LoggerListener| Executing query
...
20:57:21,401 DEBUG tools.StopWatch| Finishing : Total: 9.355ms, +3.355ms
20:57:21,409 DEBUG admin.UserController| Sending response on thread: reactor-http-server-epoll-7
I have researched reactive programming for a long time now, but never quite got to program anything reactive. Now that I am, I am wondering if I am doing it correctly.
So here are my questions:
1. Is the code above a good way to handle incoming HTTP requests, query the DB and then respond?
Please ignore the logger.debug(...) calls which I have built in for the sake of my sanity :) I kind of expected to have a Flux< ImUser> as the argument to the controller method, in the sense that I have a stream of multiple potential requests
that will come at some point and will all be handled in the same way. Instead, the examples that I have found create a Mono.from(...); every time a request comes in.
2. The second Mono created in the UserService ( Mono.just(user) ) feels somewhat awkward. I understand that I need to start a new stream to be able to
run code on the elastic Scheduler, but isn't there an operator that does this?
3. From the way the code is written, I understand that the Mono inside the UserService will be blocked until the DB operation finishes,
but the original stream, which serves the requests, isn't blocked. Is this correct?
4. I plan to replace Schedulers.elastic() with a parallel Scheduler where I can configure the number of worker threads. The idea is that the number of maximum worker threads should be the same as maximum DB connections.
What will happen when all worker threads inside the Scheduler will be busy? Is that when backpressure jumps in?
5. I initially expected to have this code inside my controller:
return userService.create(user)
.map(it -> ResponseEntity.status(HttpStatus.CREATED).body(it))
.mapError(DuplicateKeyException.class, e -> new SomeSpecialException(e.getMessage(), e));
but I have not been able to achieve that AND keep the things running in the correct threads. Is there any way to achieve this inside my code?
Any help would be greatly appreciated. Thanks!
Service and Controller
The fact that your service is blocking is problematic, because then in the controller you are calling a blocking method from inside a map that isn't moved on a separate thread. This has the potential to block all controllers.
What you could do instead is return a Mono from UserService#create (remove the block() at the end). Since the service ensures that the Dao method call is isolated, it is less problematic. From there, no need to do Mono.just(user) in the Controller: just call create and start chaining operators directly on the resulting Mono:
#RequestMapping(value = "/user", method = RequestMethod.POST)
public Mono<ResponseEntity<Integer>> createUser(#RequestBody ImUser user) {
//this log as you saw was executed in the same thread as the controller method
logger.debug("Receiving request on thread: " + Thread.currentThread().getName());
return userService.create(user)
.map(it -> {
logger.debug("Sending response on thread: " + Thread.currentThread().getName());
return ResponseEntity.status(HttpStatus.CREATED).body(it);
})
.mapError(DuplicateKeyException.class, e -> new SomeSpecialException(e.getMessage(), e));
}
Logging
Note that if you want to log something there are a couple better options than doing a map and returning it:
doOnNext method is tailored for that: react to one of the reactive signals (in this instance, onNext: a value is emitted) and perform some non-mutating action, leaving the output sequence exactly the same as the source sequence. The "side-effect" of the doOn can be writing to the console or incrementing statistic counters for instance... There's also doOnComplete, doOnError, doOnSubscribe, doOnCancel, etc...
log simply logs all events in the sequence above it. It will detect if you use SLF4J and use the configured logger at DEBUG level if so. Otherwise it'll use the JDK Logging features (so you also need to configure that to display DEBUG level logs).
A word about transactions or rather anything relying on ThreadLocal
ThreadLocal and thread-stickiness can be problematic in reactive programming, because there's less guarantee of the underlying execution model staying the same throughout a whole sequence. A Flux can execute in several steps, each in a different Scheduler (and so thread or thread pool). Even at a specific step, one value could be processed by thread A of the underlying thread pool while the next one, arriving later on, would be processed on thread B.
Relying on Thread Local is less straightforward in this context, and we are currently actively working on providing alternatives that fit better in the reactive world.
Your idea of making a pool of the size of the connection pool is good, but not necessarily sufficient, with the potential of several threads being used by a transactional flux, thus maybe polluting some threads with the transaction.
What happens when a pool runs out of threads
If you are using a particular Scheduler to isolate blocking behavior like here, once it runs out of threads it would throw a RejectedExecutionException.

Java: How to handle an API call that can take around 10 seconds

I have a requirement and I am bit confused about its design.
Requirement: iOS makes a call to backend(java), backend makes a call to the cloud API which return a token for future calls. The cloud API might take approximately 6 to 10 seconds to return the actual result, so instead of waiting for 6 to 10 seconds it gives a token back and let the caller(in my case the backend java server) to pull the results.
Current Approach: iOS calls the backend(java server), the backend calls cloud API and get's the token, then it sleeps the thread for 1 second and once the thread is invoked it hit the cloud API to get the status, if the status is not completed thread.sleep is invoked again and this continues till the cloud API call give's the complete result. Once the cloud API returns the result the backend returns the result to iOS.
The approach is not scalable and was done to test the cloud API but now we need a more scalable approach.
This is what I am thinking about iOS calls backend, backend calls the API and send back the result to iOS(it displays some static screen just to keep users engaged) and in the mean time it puts the object in Spring Thread pool Executor. The executor hits the API every one second and update the iOS through push notification and this continues till we get the final result from cloud API.
This is better then existing approach but even this doesn't look scalable and thread pool executor will get exhausted after some time(making it slow) and also thread.sleep is also not a good option.
I thought about using AWS SQS but it doesn't provide real time processing and running background jobs every 1 second doesn't seem to be a good option.
I am also exploring Apache Kafka and trying to understand whether it can fit to my use case.
Let me know if someone has tacked the similar kind of use case.
Here #EventListener in tandem with #Scheduled can be utilized, if Spring 4.2 (or newer) version is used.
First Create an event object say APIResult which will hold the API result
public class APIResult extends ApplicationEvent {
public APIResult(Object result) {
super(source);
}
}
Next register a listener for the event published as APIResult
#Component
public class MyListener {
#EventListener
public void handleResult(APIResult result) {
// do something ...
}
}
Next create a scheduled process which will hold the token(s) for which result is not yet retrieved
#Component
public class MyScheduled {
private final ApplicationEventPublisher publisher;
private List<String> tokens = new ArrayList<>();
#Autowired
public MyScheduled (ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#Scheduled(initialDelay=1000, fixedRate=5000) // modify it as per requirement
public void callAPIForResult() {
// call the API and get result for each token(s) ....
this.publisher.publishEvent(new APIResult(result));
}
// method to add & remove tokens
}
The overall process flow should be like
Application submit a request to API and collect the respective token.
Token is passed to scheduled service to fetch the result.
In its next run the scheduled service iterates over the available token(s) and call API to fetch the results (if result is available publish the event else continue)
The published event is intercepted by registered listener; which itself process the result or delegates as applicable
This approach will transparently fetch results without messing with the business logic and at same time leveraging the standard framework features viz. scheduling and asynchronous event publishing & processing.
Although I have not tested this but it should work, at least giving an idea on how to implement. The setup is tested with Spring boot ver. 1.5.1.RELEASE which is backed by Spring's 4.3.6.RELEASE
Do let know in comments if any further information is required.
Reference - Application Event in Spring (link)
I am thinking about using Spring ConcurrentTaskExecutor(let's call it cloudApiCall) and as soon as I received the token from Cloud API, I will submit a future job to the executor and return the token to the Mobile Client. The thread associated with ConcurrentTaskExecutor will pick the job, call the Cloud API and submit the response to the another ConcurrentTaskExecutor(let's call it pushNotification) which will be responsible for pushing the silent notification to the Mobile client. The thread associated ConcurrentTaskExecutor(cloudApiCall), will also check the status of the call, if the future call is required, it will submit the job back to ConcurrentTaskExecutor(cloudApiCall). This will continue till we get the complete response.

jersey ws 2.0 #suspended AsyncResponse, what does it do?

I am analyzing some jersey 2.0 code and i have a question on how the following method works:
#Stateless
#Path("/mycoolstuff")
public class MyEjbResource {
…
#GET
#Asynchronous //does this mean the method executes on child thread ?
public void longRunningOperation(#Suspended AsyncResponse ar) {
final String result = executeLongRunningOperation();
ar.resume(result);
}
private String executeLongRunningOperation() { … }
}
Lets say im at a web browser and i type in www.mysite/mycoolstuff
this will execute the method but im not understanding what the asyncResponse is used for neither the #Asynchronous annotation. From the browser how would i notice its asychnronous ? what would be the difference in removing the annotation ? Also the suspended annotation after reading the documentation i'm not clear its purpose.
is the #Asynchronous annotation simply telling the program to execute this method on a new thread ? is it a convenience method for doing "new Thread(.....)" ?
Update: this annotation relieves the server of hanging onto the request processing thread. Throughput can be better. Anyway from the official docs:
Request processing on the server works by default in a synchronous processing mode, which means that a client connection of a request is processed in a single I/O container thread. Once the thread processing the request returns to the I/O container, the container can safely assume that the request processing is finished and that the client connection can be safely released including all the resources associated with the connection. This model is typically sufficient for processing of requests for which the processing resource method execution takes a relatively short time. However, in cases where a resource method execution is known to take a long time to compute the result, server-side asynchronous processing model should be used. In this model, the association between a request processing thread and client connection is broken. I/O container that handles incoming request may no longer assume that a client connection can be safely closed when a request processing thread returns. Instead a facility for explicitly suspending, resuming and closing client connections needs to be exposed. Note that the use of server-side asynchronous processing model will not improve the request processing time perceived by the client. It will however increase the throughput of the server, by releasing the initial request processing thread back to the I/O container while the request may still be waiting in a queue for processing or the processing may still be running on another dedicated thread. The released I/O container thread can be used to accept and process new incoming request connections.
#Suspended have more definite if you used it, else it will not make any difference of using it.
Let's talk about benefits of it:
#Suspended will pause/Suspend the current thread until it gets response,by default #NO_TIMEOUT no suspend timeout set. So it doesn't mean your request response (I/O)thread will get free and be available for other request.
Now Assume you want your service to be a response with some specific time, but the method you are calling from resource not guarantee the response time, then how will you manage your service response time? At that time, you can set suspend timeout for your service using #Suspended, and even provide a fall back response when time get exceed.
Below is some sample of code for setting suspend/pause timeout
public void longRunningOperation(#Suspended AsyncResponse ar) {
ar.setTimeoutHandler(customHandler);
ar.setTimeout(10, TimeUnit.SECONDS);
final String result = executeLongRunningOperation();
ar.resume(result);
}
for more details refer this
The #Suspended annotation is added before an AsyncResponse parameter on the resource method to tell the underlying web server not to expect this thread to return a response for the remote caller:
#POST
public void asyncPost(#Suspended final AsyncResponse ar, ... <args>) {
someAsyncMethodInYourServer(<args>, new AsyncMethodCallback() {
#Override
void completed(<results>) {
ar.complete(Response.ok(<results>).build());
}
#Override
void failed(Throwable t) {
ar.failed(t);
}
}
}
Rather, the AsyncResponse object is used by the thread that calls completed or failed on the callback object to return an 'ok' or throw an error to the client.
Consider using such asynchronous resources in conjunction with an async jersey client. If you're trying to implement a ReST service that exposes a fundamentally async api, these patterns allow you to project the async api through the ReST interface.
We don't create async interfaces because we have a process that takes a long time (minutes or hours) to run, but rather because we don't want our threads to ever sleep - we send the request and register a callback handler to be called later when the result is ready - from milliseconds to seconds later - in a synchronous interface, the calling thread would be sleeping during that time, rather than doing something useful. One of the fastest web servers ever written is single threaded and completely asynchronous. That thread never sleeps, and because there is only one thread, there's no context switching going on under the covers (at least within that process).
The #suspend annotation makes the caller actually wait until your done work. Lets say you have a lot of work to do on another thread. when you use jersey #suspend the caller just sits there and waits (so on a web browser they just see a spinner) until your AsyncResponse object returns data to it.
Imagine you had a really long operation you had to do and you want to do it on another thread (or multiple threads). Now we can have the user wait until we are done. Don't forget in jersey you'll need to add the " true" right in the jersey servlet definition in web.xml to get it to work.

Categories

Resources