Method rxExecuteBlocking consuming all results - ssh client - java

I'm trying to do really simple SSH client with Vert.x. As I don't have non-blocking SSH library under the hood, I have to handle everything in rxExecuteBlocking. It's working great when I'm running all logic in one big block of code as follows:
public Single<String> exec() {
return vertx.rxExecuteBlocking(f -> {
String result = "";
// connect()
// exec()
// close()
f.complete(result);
}, false);
}
// hostnames :: Observalbe<String>
hostnames()
.filter()
.flatMapSingle(this::exec)
.moreCalls()
.subscribe(); // OK
I'd rather to have connect(), exec(), close() separeted and call like:
hostnames()
.filter()
.flatMapSingle(this::connect)
.moreCalls()
.flatMapSingle(this::exec)
.moreCalls()
.flatMapSingle(this::close)
.subscribe();
But when running more than one piece of blocking code
public Single<Connection> connect() {
return vertx.rxExecuteBlocking(f -> {
// connect
}, false);
}
public Single<Connection> exec() {
return vertx.rxExecuteBlocking(f -> {
// exec
}, false);
}
the chain stops at flatMapSingle(this::connect), consume all results from filter() first (make all connections) and then continue in chain. This behavior consumes pretty much resources as all connections are in memory (this behavior reminds me reduce() or collect())
The desired result will be not stopping in chain and continue, release resources and do this for every event.
Is there any way to do this?
Thanks in advance.

I would suggest to try to use overloaded flatMap, which takes as an argument the maximum number of concurrently subscribed observables at the particular pipeline stage. Provided there are 20 threads in worker thread pool by default, you could give a fraction of the pool to each of flatMap calls, e.g. 5 to each.
hostnames()
// ...some filtering
.flatMap(hostname -> this.connect(hostname).toObservable(), 5)
// ...more operators
.flatMap(connection -> this.exec(connection).toObservable(), 5)
// ...more operators
.flatMap(connection -> this.close(connection).toObservable(), 5)
.subscribe();
This will ensure that not the whole thread pool is used at the same moment.
Some tweaks to concurrent load may be needed. For example, less concurrently subscribed observables for connect and more for exec if connect is faster than exec. Thus, results of connect are not stacked in a buffer before exec.

Related

Wrapping blocking client with Mono and execute sequence

I am trying to make a reactive application which needs to execute ssh commands.
Currently, there is an SSH client (based on sshd mina) which is blocking (maybe there is a way to use it in a non-blocking way, but I don't know it). My idea is to create a wrapper around this blocking client, so that I can transform the blocking calls into Mono as in the code below.
public class SshReactiveClient extends AutoCloseable {
private final SshClient sshClient;
public SshReactiveClient(final SshClient sshClient) {
this.sshClient = sshClient;
}
public Mono<SshResponse> open(final Duration timeout) {
return Mono.fromCallable(() -> sshClient.open(timeout))
.subscribeOn(Schedulers.boundedElastic());
}
public Mono<SshResponse> execCommand(final SshCommand command, final Duration timeout) {
return Mono.fromCallable(() -> sshClient.execCommand(command, timeout))
.subscribeOn(Schedulers.boundedElastic());
}
#Override
public void close() throws Exception {
sshClient.close();
}
}
First, is it a good idea to proceed like that or not? What would be better?
The second point is how to write the code so that I can execute a sequence of ssh command using the responses from the previous commands to execute the next one?
Your understanding is correct. You need to wrap blocking or sync code and run it on a separate Scheduler. The better way would be if client supports async interface.
To execute commands in a sequence you need to build a flow using reactive API.
execCommand(command1)
.flatMap(res ->
execCommand(getCommand2(res))
)
.flatMap(res ->
execCommand(getCommand3(res))
)
There are many other options depending on your requirements. For example, if you need results from command1 & command2 to execute command3, you could just "shift" flow one level down
execCommand(command1)
.flatMap(res1 ->
execCommand(getCommand2(res1))
.flatMap(res2 ->
execCommand(getCommand3(res1, res2))
)
)
As an alternative, you could apply builder pattern to the reactor flow to collect responses in a sequential flow Builder pattern with Reactor mono
You could also execute command1 and command2 in parallel and use responses from both
Mono.zip(command1, command2)
.flatMap(tuple ->
execCommand(getCommand3(tuple.getT1(), tuple.getT2()))
)

Detect timeouts in a CompletableFuture chain

Is there any possible safe way to detect timeouts in a CompletableFuture chain?
O someValue = CompletableFuture.supplyAsync(() -> {
...
// API Call
...
}).thenApply(o -> {
...
}).thenApply(o -> {
// If the chain has timed out, I still have 'o' ready here
// So at least cache it here, so it's available for the next request
// Even though the current request will return with a 'null'
...
}).get(10, TimeUnit.SECONDS);
// cache 'someValue'
return someValue;
It completes successfully without a timeout, I can use 'someValue' and do whatever with it
If it times out, it throws a TimeoutException and I have lost the value, even though it's still being processed in the background
The idea is that even if it times out and since the API call in the thread still completes in the background and returns the response, I can use that value, let's say, for caching
Not at least in the way you show. When the exception is thrown, you lose any chance of getting your hands on the results of the API call even if it finishes. Your only chances of caching in a chain like that would be something like the following, which would not help with the time-outing API call itself
.thenApplyAsync(o -> {
cache = o;
// do something
}).thenApplyAsync(o -> {
cache = o;
// do something more
}).get(10, TimeUnit.SECONDS);
However reading through this gave me an idea, that what if you did something like the following
SynchronousQueue<Result> q = new SynchronousQueue<>();
CompletableFuture.supplyAsync(() -> {
// API call
}.thenAccept(result -> {
cache.put(result); // cache the value
q.offer(result); // offer value to main thread, if still there
}
);
// Main thread waits 10 seconds for a value to be asynchronously offered into the queue
// In case of timeout, null is returned, but any operations done
// before q.offer(result) are still performed
return queue.poll(10, TimeUnit.SECONDS);
An API call that doesn't finish in 10 seconds is still processed into cache as it is asynchronously accepted and the timeout happens in the main thread and not the CompletableFuture chain, even though the original request won't get the results (and I guess has to deal with it gracefully).

Async method followed by a parallelly executed method in Java 8

After spending the day of learning about the java Concurrency API, I still dont quite get how could I create the following functionality with the help of CompletableFuture and ExecutorService classes:
When I get a request on my REST endpoint I need to:
Start an asynchronous task (includes DB query, filtering, etc.), which will give me a list of String URLs at the end
In the meanwhile, responde back to the REST caller with HTTP OK, that the request was received, I'm working on it
When the asynchronous task is finished, I need to send HTTP requests (with the payload, the REST caller gave me) to the URLs I got from the job. At most the number of URLs would be around a 100, so I need these to happen in parallel.
Ideally I have some syncronized counter which counts how many of the http requests were a success/fail, and I can send this information back to the REST caller (the URL I need to send it back to is provided inside the request payload).
I have the building blocks (methods like: getMatchingObjectsFromDB(callerPayload), getURLs(resultOfgetMachingObjects), sendHttpRequest(Url, methodType), etc...) written for these already, I just cant quite figure out how to tie step 1 and step 3 together. I would use CompletableFuture.supplyAsync() for step 1, then I would need the CompletableFuture.thenComponse method to start step 3, but it's not clear to me how parallelism can be done with this API. It is rather intuitive with ExecutorService executor = Executors.newWorkStealingPool(); though, which creates a thread pool based on how much processing power is available and the tasks can be submitted via the invokeAll() method.
How can I use CompletableFutureand ExecutorService together? Or how can I guarantee parallel execution of a list of tasks with CompletableFuture? Demonstrating code snippet would be much appreciated. Thanks.
You should use join() to wait for all thread finish.
Create Map<String, Boolean> result to store your request result.
In your controller:
public void yourControllerMethod() {
CompletableFuture.runAsync(() -> yourServiceMethod());
}
In your service:
// Execute your logic to get List<String> urls
List<CompletableFuture> futures = urls.stream().map(v ->
CompletableFuture.supplyAsync(url -> requestUrl(url))
.thenAcceptAsync(requestResult -> result.put(url, true or false))
).collect(toList()); // You have list of completeable future here
Then use .join() to wait for all thread (Remember that your service are executed in its own thread already)
CompletableFuture.allOf(futures).join();
Then you can determine which one success/fail by accessing result map
Edit
Please post your proceduce code so that other may understand you also.
I've read your code and here are the needed modification:
When this for loop was not commented out, the receiver webserver got
the same request twice,
I dont understand the purpose of this for loop.
Sorry in my previous answer, I did not clean it up. That's just a temporary idea on my head that I forgot to remove at the end :D
Just remove it from your code
// allOf() only accepts arrays, so the List needed to be converted
/* The code never gets over this part (I know allOf() is a blocking call), even long after when the receiver got the HTTP request
with the correct payload. I'm not sure yet where exactly the code gets stuck */
Your map should be a ConcurrentHashMap because you're modifying it concurrently later.
Map<String, Boolean> result = new ConcurrentHashMap<>();
If your code still does not work as expected, I suggest to remove the parallelStream() part.
CompletableFuture and parallelStream use common forkjoin pool. I think the pool is exhausted.
And you should create your own pool for your CompletableFuture:
Executor pool = Executors.newFixedThreadPool(10);
And execute your request using that pool:
CompletableFuture.supplyAsync(YOURTASK, pool).thenAcceptAsync(Yourtask, pool)
For the sake of completion here is the relevant parts of the code, after clean-up and testing (thanks to Mạnh Quyết Nguyễn):
Rest controller class:
#POST
#Path("publish")
public Response publishEvent(PublishEvent eventPublished) {
/*
Payload verification, etc.
*/
//First send the event to the right subscribers, then send the resulting hashmap<String url, Boolean subscriberGotTheRequest> back to the publisher
CompletableFuture.supplyAsync(() -> EventHandlerService.propagateEvent(eventPublished)).thenAccept(map -> {
if (eventPublished.getDeliveryCompleteUri() != null) {
String callbackUrl = Utility
.getUri(eventPublished.getSource().getAddress(), eventPublished.getSource().getPort(), eventPublished.getDeliveryCompleteUri(), isSecure,
false);
try {
Utility.sendRequest(callbackUrl, "POST", map);
} catch (RuntimeException e) {
log.error("Callback after event publishing failed at: " + callbackUrl);
e.printStackTrace();
}
}
});
//return OK while the event publishing happens in async
return Response.status(Status.OK).build();
}
Service class:
private static List<EventFilter> getMatchingEventFilters(PublishEvent pe) {
//query the database, filter the results based on the method argument
}
private static boolean sendRequest(String url, Event event) {
//send the HTTP request to the given URL, with the given Event payload, return true if the response is positive (status code starts with 2), false otherwise
}
static Map<String, Boolean> propagateEvent(PublishEvent eventPublished) {
// Get the event relevant filters from the DB
List<EventFilter> filters = getMatchingEventFilters(eventPublished);
// Create the URLs from the filters
List<String> urls = new ArrayList<>();
for (EventFilter filter : filters) {
String url;
try {
boolean isSecure = filter.getConsumer().getAuthenticationInfo() != null;
url = Utility.getUri(filter.getConsumer().getAddress(), filter.getPort(), filter.getNotifyUri(), isSecure, false);
} catch (ArrowheadException | NullPointerException e) {
e.printStackTrace();
continue;
}
urls.add(url);
}
Map<String, Boolean> result = new ConcurrentHashMap<>();
Stream<CompletableFuture> stream = urls.stream().map(url -> CompletableFuture.supplyAsync(() -> sendRequest(url, eventPublished.getEvent()))
.thenAcceptAsync(published -> result.put(url, published)));
CompletableFuture.allOf(stream.toArray(CompletableFuture[]::new)).join();
log.info("Event published to " + urls.size() + " subscribers.");
return result;
}
Debugging this was a bit harder than usual, sometimes the code just magically stopped. To fix this, I only put code parts into the async task which was absolutely necessary, and I made sure the code in the task was using thread-safe stuff. Also I was a dumb-dumb at first, and my methods inside the EventHandlerService.class used the synchronized keyword, which resulted in the CompletableFuture inside the Service class method not executing, since it uses a thread pool by default.
A piece of logic marked with synchronized becomes a synchronized block, allowing only one thread to execute at any given time.

Async API giving worse performance

Interesting, I would think have 255 concurrent users, an async API would have better performance. Here are 2 of my endpoints in my Spring server:
#RequestMapping("/async")
public CompletableFuture<String> g(){
CompletableFuture<String> f = new CompletableFuture<>();
f.runAsync(() -> {
try {
Thread.sleep(500);
f.complete("Finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
return f;
}
#RequestMapping("/sync")
public String h() throws InterruptedException {
Thread.sleep(500);
return "Finished";
}
In the /async it runs it on a different thread. I am using Siege for load testing as follows:
siege http://localhost:8080/sync --concurrent=255 --time=10S > /dev/null
For the async endpoint, I got a transaction number of 27 hits
For the sync endpoint, I got a transaction number of 1531 hits
So why is this? Why isnt the async endpoint able to handle more transactions?
Because the async endpoint is using a shared (the small ForkJoinPool.commonPool()) threadpool to execute the sleeps, whereas the sync endpoint uses the larger threadpool of the application server. Since the common pool is so small, you're running maybe 4-8 operations (well, if you call sleeping an operation) at a time, while others are waiting for their turn to even get in the pool. You can use a bigger pool with CompletableFuture.runAsync(Runnable, Executor) (you're also calling the method wrong, it's a static method that returns a CompletableFuture).
Async isn't a magical "make things faster" technique. Your example is flawed as all the requests take 500ms and you're only adding overhead in the async one.

RxJava Observable minimum execution time

I have an Observable (which obtains data from network).
The problem is that observable can be fast or slow depending on network conditions.
I show progress widget, when observable is executing, and hide it when observable completes. When the network is fast - progress flikers (appears and disappears). I want to set minimum execution time of observable to 1 second. How can I do that?
"Delay" operator is not an option because it will delay even for slow network.
You can use Observable.zip() for that. Given
Observable<Response> network = ...
One can do
Observable<Integer> readyNotification = Observable.just(42).delay(1, TimeUnit.SECONDS);
Observable delayedNetwork = network.zipWith(readyNotification,
(response, notUsed) -> response);
Use Observable.concatEager()
It allows you to force one stream to complete after another (concat operator), but also kick off the network request immediately without having to wait for the first argument observable to complete (concatEager):
Observable<Response> responseObservable = ...;
Observable<Response> responseWithMinDelay = Observable.concatEager(
Observable.timer(1, TimeUnit.SECONDS).ignoreElements(),
responseObservable
).cast(Response.class);
It looked like Observable.zip would be a reasonable approach, and it seemed to work well until there was an error emitted; then it didn't wait for the expected time.
This seemed to work well for me:
Observable.mergeDelayError(
useCase.execute(), // can return Unit or throw error
Observable.timer(1, TimeUnit.SECONDS)
)
.reduce { _, _ -> Unit }
.doOnError { /* will wait at least 1 second */ }
.subscribe { /* will wait at least 1 second */ }

Categories

Resources