I wanted to prototype an example where I call a ServiceC using a value returned by ServiceA using Spring Reactor Stream API. So I wrote code like this
final ExecutorService executor = new ThreadPoolExecutor(4, 4, 10, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>());
Streams.defer(executor.submit(new CallToRemoteServiceA()))
.flatMap(s -> Streams.defer(executor.submit(new CallToRemoteServiceC(s))))
.consume(s -> System.out.println("End Result : " + s));
To simulate the latency involved in ServiceA and ServiceC the call() methods of CallToRemoteServiceA and CallToRemoteServiceC has Thread.sleep() methods. The problem is that when I comment out the Thread.sleep() method i.e. the service method calls have no latency which is not true in the real world the consume method gets called. If the Thread.sleep() methods are kept in place then the consume method doesn't get called. I understand that the Streams.defer() returns a cold stream and hence it probably only executes the consume method for items accepted after it's registration but then I was wondering how I could create a HotStream from a Future returned by the ExecutorService?
I believe this is because of a bug in the reactor.rx.stream.FutureStream.subscribe() method. In this line:
try {
// Bug in the line below since unit is never null
T result = unit == null ? future.get() : future.get(time, unit);
buffer.complete();
onNext(result);
onComplete();
} catch (Throwable e) {
onError(e); <-- With default constructor this gets called if time == 0 and
future has as yet not returned
}
In this case when the default FutureStream(Future) constructor is called the unit is never null and hence the above code always calls future.get(0, TimeUnit.SECONDS) leading to an immediate timeout exception in the catch(Throwable) block. If you guys agree that this is a bug I can make a pull request with a fix for this issue??
I think what you want is to use Streams.just. You can optionally .dispatchOn(Dispatcher) if you want, but since you're already in the thread of the thread pool, you'll probably want to use the sync Dispatcher. Here's a quick test to illustrate:
#Test
public void streamsDotJust() throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Streams
.just(executor.submit(() -> "Hello World!"))
.map(f -> {
try {
return f.get();
} catch (Exception e) {
throw new IllegalStateException(e);
}
})
.consume(System.out::println);
Thread.sleep(100);
}
Related
I can't find out how to wrap a synchronous method with Resilience4j so that it returns a CompletableFuture, although this seems to be part of Resilience4j's target area.
Especially since the synchronous method I want to wrap can throw an Exception.
What I want in pseudo code:
boolean void syncMethod(Parameter param) throws Exception {
// May throw Exception due to connection/authorization problems.
}
CompletableFuture<Boolean> asyncResilience4jWrapper() {
CompletableFuture<Boolean> result =
...
Resilience4j magic around "syncMethod(param)".
Trying 4 calls, interval between calls of 100 ms.
...;
return result;
}
Resilience4j should just try to call the method 4 times until it gives up, with intervals between the calls of 100 ms and then complete the asynchronous call.
The asyncResilience4jWrapper caller should just get back a CompletableFuture which doesn't block and don't care about any of that.
The really hard part seems to be to get it running for a method with a parameter, throwing an exception!
just do
CompletableFuture<Boolean> asyncResilience4jWrapper(Parameter param) {
return CompletableFuture<Boolean> future = Decorators.ofCallable(() -> syncMethod(param))
.withThreadPoolBulkhead(threadPoolBulkhead)
.withTimeLimiter(timeLimiter, scheduledExecutorService)
.withCircuitBreaker(circuitBreaker)
.withRetry(retry)
.withFallback(asList(TimeoutException.class, CallNotPermittedException.class, BulkheadFullException.class),
throwable -> "Hello from Recovery")
.get().toCompletableFuture();
}
I can't find out how to wrap a synchronous method with Resilience4j so that it returns a CompletableFuture, although this seems to be part of Resilience4j's target area.
Especially since the synchronous method I want to wrap can throw an Exception.
What I want in pseudo code:
boolean void syncMethod() throws Exception {
// May throw Exception due to connection/authorization problems.
}
CompletableFuture<Boolean> asyncResilience4jWrapper() {
CompletableFuture<Boolean> result =
...
Resilience4j magic around "syncMethod()".
Trying 4 calls, interval between calls of 100 ms.
...;
return result;
}
Resilience4j should just try to call the method 4 times until it gives up, with intervals between the calls of 100 ms and then complete the asynchronous call.
The asyncResilience4jWrapper caller should just get back a CompletableFuture which doesn't block and don't care about any of that.
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofSeconds(1));
CompletableFuture<Boolean> future = Decorators.ofCallable(() -> syncMethod)
.withThreadPoolBulkhead(threadPoolBulkhead)
.withTimeLimiter(timeLimiter, scheduledExecutorService)
.withCircuitBreaker(circuitBreaker)
.withFallback(asList(TimeoutException.class, CallNotPermittedException.class, BulkheadFullException.class),
throwable -> "Hello from Recovery")
.get().toCompletableFuture();
Just add withRetry below the CircuitBreaker.
Say I have a method
#RestController
#RequestMapping(value = "/")
public class AppController {
#Async
#PostMapping(value="/")
public CompletableFuture<> doSomething(#RequestBody ..., HTTPServletResponse response){
//something;
return completableResult;
}
}
I understand that the method will return immediately and release the container thread.
But how is the value returned (after some time) handled?
Is there a listener?
And doesn't that block one of the container threads?
Isn't there something like future.get() executed internally ?
There is a method doSomething.isDone() to check if future is ready. You can implement if statement to chech if isDone return true(and call .get() method to retrieve what future have). If it returns false it will mean you need to wait for future.
EDIT:
after post was edit my answer is not so connected anymore but I would do that in similar way as I describe.
I know I'm late but for others looking this could be helpful.
First of all:
I see you are using both Async and CompletableFuture in the same function.They are used to do the same thing i.e. handling Asynchronous requests.
Both of them are capable of doing the same thing independently.
isDone is not something I would recommend because you have to wait your unknown amount of time to return your values.I feel it's better for the consumer ( frontend ,browser or whoever made the request ) to wait for the response.
Using #async
In this we create a CompletableFuture object and returns it from the async method . Consumer (Browser or your GET request from frontend ) will wait on this object to complete. After we have done our asynchronous task, which is running on a different thread we complete the cp object with the desired value of the CompletableFuture, in our case string. SpringBoot converts your CompletableFuture object to relevant Type of the value.
#Async
public CompletableFuture<String> doSomeThing() throws InterruptedException {
CompletableFuture<String> cp = new CompletableFuture<String>();
int i = 5;
while(i-- >= 0){
System.out.println("Doing something ");
Thread.sleep(2000);
}
if(i < 0){
cp.complete("Done doing something ");
}
return cp;
}
Using CompletableFuture
CompletableFuture API has methods ,runAsync and supplyAsync
runAsync : methods takes a Runnable lambda function.This method is used to compute async task on a different thread without returning anything.
supplyAsync: method takes a Supplier type lambda function and returns a CompletableFuture.
We can use this supplyAsync method to return a completablefuture and also run the task in different thread without using the #Async annotation.
public CompletableFuture<String> doSomeThing() {
return CompletableFuture.supplyAsync(()-> {
int i =3;
while(i-- >= 0){
System.out.println("Doing something ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Done doing something";
});
}
Hope this makes things clearer about CompletableFuture. Happy Coding 😋
In the following code
public CompletableFuture<String> getMyFuture(String input)
{
CompletableFuture<String> future = new CompletableFuture<String>().thenApply((result) -> result+ "::");
ExecutorService service = Executors.newFixedThreadPool(6);
service.submit(() -> {
try {
future.complete(getResult(input));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
return future;
}
public String getResult(String input) throws InterruptedException
{
Thread.sleep(3000);
return "hello "+ input +" :" + LocalTime.now();
}
I am expecting the output to contain trailing "::" but program doesn't is "hello first :16:49:30.231
" Is my implementation of apply correct ?
You're invoking complete() method of the CompletionStage that you got at the first line (where you call "thenApply" method).
If your intention is to complete the CompletableFuture with some string value (future.complete(getResult(input))) and then apply some function, you'd better place thenApply() at the end (where you return the future).
public CompletableFuture<String> getMyFuture(String input)
{
CompletableFuture<String> future = new CompletableFuture<String>();
ExecutorService service = Executors.newFixedThreadPool(6);
service.submit(() -> {
try {
future.complete(getResult(input));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
return future.thenApply(result -> result+ "::");
}
I don't know how to explain it in a more understandable way. But in short: you're calling complete() method on the wrong object reference inside your Runnable.
You are creating two CompletableFuture instances. The first, created via new CompletableFuture<String>() will never get completed, you don’t even keep a reference to it that would make completing it possible.
The second, created by calling .thenApply((result) -> result+ "::") on the first one, could get completed by evaluating the specified function once the first one completed, using the first’s result as an argument to the function. However, since the first never completes, the function becomes irrelevant.
But CompletableFuture instances can get completed by anyone, not just a function passed to a chaining method. The possibility to get completed is even prominently displayed in its class name. In case of multiple completion attempts, one would turn out to be the first one, winning the race and all subsequent completion attempts will be ignored. In your code, you have only one completion attempt, which will successfully complete it with the value returned by getResult, without any adaptations.
You could change your code to keep a reference to the first CompletableFuture instance to complete it manually, so that the second gets completed using the function passed to thenApply, but on the other hand, there is no need for manual completion here:
public CompletableFuture<String> getMyFuture(String input) {
ExecutorService service = Executors.newFixedThreadPool(6);
return CompletableFuture.supplyAsync(() -> getResult(input), service)
.thenApply(result -> result + "::");
}
public String getResult(String input) {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3));
return "hello "+ input +" :" + LocalTime.now();
}
When specifying the executor to supplyAsync, the function will be evaluated using that executor. More is not needed.
Needless to say, that’s just for example. You should never create a temporary thread pool executor, as the whole point of a thread pool executor is to allow reusing the threads (and you’re using only one of these six threads at all) and it should get shut down after use.
e.g. I looking to find a way to execute #Async method not absolutely asynchronously.
For example I want to invoke #Asynctask that will block my process for a up to a maximum defined time if task still haven't completed.
#Async
public Future<ModelObject> doSomething() {
//here we will block for a max allowed time if task still haven't been completed
}
So such code will be semi asynchronous but the blocking time can be controlled by developer.
P.S : of course I can achieve this by simply blocking calling thread for a limited time. but I look to achieve that within spring layer
In short, no, there is no way to configure Spring to do this.
The #Async annotation is handled by the AsyncExecutionInterceptor which delegates the work to a AsyncTaskExecutor. You could, in theory, write your own implementation of the AsyncTaskExecutor but even then there would be no way to use the #Async annotation to pass the desired wait time to your executor. Even then, it's not clear to me what the caller's interface would look like since they'd still be getting a Future object back. You would probably also need to subclass the Future object as well. Basically, by the time you are finished, you will have written the entire feature again more or less from scratch.
You could always wrap the returned Future object in your own WaitingFuture proxy which provides an alternate get implementation although even then you'd have no way of specifying the wait value on the callee side:
WaitingFuture<ModelObject> future = new WaitingFuture<ModelObject>(service.doSomething());
ModelObject result = future.get(3000); //Instead of throwing a timeout, this impl could just return null if 3 seconds pass with no answer
if(result == null) {
//Path A
} else {
//Path B
}
Or if you don't want to write your own class then just catch the TimeoutException.
Future<ModelObject> future = doSomething();
try {
ModelObject result = future.get(3000,TimeUnit.MILLISECONDS);
//Path B
} catch (TimeoutException ex) {
//Path A
}
You can do it with an #Async method that returns a Future:
Future<String> futureString = asyncTimeout(10000);
futureString.get(5000, TimeUnit.MILLISECONDS);
#Async
public Future<String> asyncTimeout(long mills) throws InterruptedException {
return new AsyncResult<String>(
sleepAndWake(mills)
);
}
public String sleepAndWake(long mills) throws InterruptedException{
Thread.sleep(mills);
return "wake";
}