I need to send 4 http requests at same time and wait until all of them finished (i'm using volley)
I've tried to send them separately in 4 threads and use thread.join but it seems that onResponse and onError methods are running in main thread so the request threads finishes after call queue.add(jsonArrayRequest).
I can't use countdownlatch because as I know first it doesn't run threads at same time (it runs them in a queue) and second it blocks main thread.
what's your suggestion? let me know if there's better way to do this using Retrofit , OkHttp or other libraries.
To achieve it without using any patterns or other libraries, you can mark the request as finished if it responded, and call the method, in each of them, you want to execute if all the requests are finished. On that method, you just need to check if all the requests are done.
Example:
isRequest1Finished = false;
isRequest2Finished = false;
response1 = null;
response2 = null;
volleyRequest1(new Response.Listener<Something>() {
#Override
public void onResponse(Something response) {
isRequest1Finished = true;
response1 = response;
doSomething();
}
})
volleyRequest2(new Response.Listener<Something>() {
#Override
public void onResponse(Something response) {
isRequest2Finished = true;
response2 = response;
doSomething();
}
})
//....do on all of your requests
and in your doSomething() method:
void doSomething() {
if (isRequest1Finished && isRequest2Finished) {
// do something with the response1, response2, etc....
}
}
But my suggestion is using RxJava, where you can apply zip operator, in which it combines all of your asynchronous responses into one result:
Example:
Observable request1 = getRequest1();
Observable request2 = getRequest2();
Observable.zip(request1, request2,
new BiFunction<Something, Something, Pair<Something, Something>() {
#Override
public Pair<Something, Something> apply(Something response1, Something response2) {
return new Pair(response1, response2); // you can create a custom object to handle all of the responses
}
})
.map( // do something with your responses )
Related
Tech stack: rx-java 1.1.x, retrofit 1.9.x, spring 4.3.x.
A bit of context:
I'm pretty new to rx-java. My service A has an API endpoint that makes a search call to service B that is frequently used, and fails a bit too often then it should. Some of the errors are pretty clear timeouts from other different services deep down, that took >30s, but quite a lot of them are pretty quick ones, around <1s.
What exactly I'm trying to do:
Retry only the calls that fail under given threshold (let's say <1s), ideally the ones returning 5xx HTTP code responses.
Ideas that came to my mind, but do not solve the problem:
Regular Observable.timeout() seems of no use, because for now I don't want to touch (interrupt) calls that are taking longer. I only want to retry those that came back as failed (5XX response), not interrupt the longer ones.
retry() seems of no use, because I don't want to simply retry every failed call.
retryWhen() could be of use, but I am not sure how can I extract the HTTP from a Throwable and what exactly should I measure in the Observable call.
Code:
#RestController
#RequestMapping(...)
public class MyController {
#RequestMapping(method = GET)
public DeferredResult<MyJsonWrapper> fetchSomething() {
MySearchRequest searchRequest,
BindingResult bindingResult,
HttpServletRequest request) {
return new MyDeferredResult(
serviceB.searchSomething(...)
.doOnNext( result -> /* log size of search */ ));
}
serviceB.searchSomething(...) also returns Observable<MyJsonWrapper>
What is MyDeferredResult:
class MyDeferredResult<T> extends DeferredResult<T> {
public MyDeferredResult(Observable<T> observable) {
onTimeout(this::handleTimeout);
ConnectableObservable<T> publication = observable.publish();
publication.subscribe(this::onNext, this::onError, this::onCompleted);
publication.connect(subscription -> this.subscription = subscription);
}
(...)
private void handleTimeout() {
setErrorResult(new MyTimeoutException( /* some info about request */ ));
subscription.unsubscribe();
}
How can I retry only the requests that failed under 1s that are 5xx HTTP responses?
I have been able to implement a working solution. To measure the Observable's time I chose Spring's StopWatch, started counting in doOnSubscribe() and stopped in doOnTerminate().
I create the StopWatch and pass it to my custom retry function used in retryWhen(), and only when the code goes to the retryWhen() block I check if the time was under my given threshold.
How my call looks like now:
StopWatch stopWatch = new StopWatch();
int executionTimeThresholdMillis = 1000; // 1 second
return new MyDeferredResult(
serviceB.searchSomething(...)
.doOnSubscribe(stopWatch::start)
.doOnTerminate(stopWatch::stop)
.retryWhen(
return RetryGivenHttpResponsesUnderThreshold.builder()
.maxRetries(MAX_RETRIES)
.httpResponsesToRetry(List.of(HTTP_CODE_TO_FAIL))
.observableExecutionTime(stopWatch)
.executionTimeThresholdMillis(executionTimeThresholdMillis)
.build())
.doOnNext( result -> /* log size of search */ ));
}
Now, the example of how could you implement the retry function. I want both checking the HTTP response and elapsed time, so the code is only somehow configurable. I hope someone else will also use it and then change it accordingly to one's needs:
public class RetryGivenHttpResponsesUnderThreshold implements Func1<Observable<? extends Throwable>, Observable<?>> {
private final int maxRetries;
private final Collection<Integer> httpResponsesToRetry;
private int retryCount;
private final boolean isMeasurable;
private final long maxObservableExecutionTimeMilis;
private final StopWatch stopWatch;
(...)
// constructors, builders, validations...
#Override
public Observable<?> call(final Observable<? extends Throwable> attempts) {
return attempts
.flatMap(throwable -> {
boolean needsRetry = false;
if (throwable instanceof HttpException) {
if (httpResponsesToRetry.contains(((HttpException) throwable).code())) {
// !IMPORTANT! in my case I want to get getLastTaskTimeMillis(), and NOT getTotalTimeMillis()
// because the timer will be stopped on every error that will trigger retry
final long observableExecutionTimeMilis = stopWatch.getLastTaskTimeMillis();
if (isMeasurable) {
needsRetry = observableExecutionTimeMilis <= maxObservableExecutionTimeMilis;
} else {
needsRetry = true;
}
}
}
if (needsRetry && retryCount < maxRetries) {
retryCount++;
// Simply retry.
return Observable.just(null);
}
// Just pass the error along.
return Observable.error(throwable);
});
}
}
A very simple retry example when the response status is 500. You can include the multiple conditions along with the status code(500) and where it throw IOException so that the retry feature is start to execute. Assume the true value for time thresold is THRESOLD
public void retriesFor500HttpResponse() throws Exception {
try (CloseableHttpClient httpClient = HttpClients.custom()
.addInterceptorLast(new HttpResponseInterceptor() {
#Override
public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
if (response.getStatusLine().getStatusCode() == 500 && THRESOLD) {
throw new IOException("Retry it");
}
}
})
.build()) {
executeFor500Status(httpClient, STATUS_500_URL);
}
}
private void executeFor500Status(CloseableHttpClient httpClient, String url) throws IOException {
//....
}
I have an application that makes dynamic requests by Retrofit and i want to detect if all have finished with the best practice like I know how to make in javascript with Promise.
In Retrofit today I'm doing like this, I receive an array from objects and i will make x request depending on the size of the array.
So when I start the function I pick the size from an array and every time my Retrofit makes a call I put in my variable successCounter++ and errorCounter++, when sum from this 2 variables is equal my array size, so this is the end of the asynchronous functions.
But I don't know if this is a good practice an example from my peace of code:
String filePath = system.getMp_user_name() + "/categorias/" + mpCategory.getImg();
downloadImage("category", filePath, mpCategory.getImg(),
new SynService.ApiImageCallback() {
public void onResponse(Boolean success, Integer requestCounter, Integer errorRequestCounter){
if(success){
categoryImgSuccessCounter++;
Log.d(TAG, "Imagem baixada e armazenada com sucesso");
if(categoryImgSuccessCounter.equals(arrayCategorySize)) {
HashMap<String, String> responseObj = new HashMap<>();
responseObj.put("success", "1");
responseObj.put("message", "Sincronização Completada com Sucesso");
callback.onResponse(responseObj);
}
} else {
categoryImgErrorCounter++;
Log.d(TAG, "Não foi possível fazer o download da imagem");
HashMap<String, String> responseObj = new HashMap<>();
responseObj.put("success", "0");
responseObj.put("message", "Houve um erro no download das imagens e o processo parou");
callback.onResponse(responseObj);
}
Integer total = categoryImgSuccessCounter + categoryImgErrorCounter;
if(total.equals(arrayCategorySize)) {
categoryImgFinished = true;
}
}
});
How can I detect when all the request from Retrofit is finished without a counter?
In javascript is just this:
async function foo(things) {
const results = [];
for (const thing of things) {
// Good: all asynchronous operations are immediately started.
results.push(bar(thing));
}
// Now that all the asynchronous operations are running, here we wait until they all complete.
return baz(await Promise.all(results));
}
Use the Interceptorof Retrofit to intercept each request and do the counter increment/decrement and shares the callback on the basis of that.
Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.
Interceptor - How to?
If it's okay to add one dependency then add RxJava and it has good number of operator which are useful for multithreading and it's callback.
To wait until all your requests will be done is to use Retrofit2 in conjunction with RxJava2with its zip function.
Zip combine the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function
Add RxJava2CallAdapterFactory as a Call adapter when building your Retrofit instance:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
Your service methods can now use any of the above types as their return type.
interface MyService {
#GET("/user")
Observable<User> getUser();
}
MyService retrofitApi = retrofit.create(MyService.class);
Use that service to create the collection of request and zip those requests.
// You need to use `Observable` type for the response.
List<Observable<?>> requests = new ArrayList<>();
// Make a collection of all requests
requests.add(retrofitApi.getUser());
requests.add(retrofitApi.getUser());
requests.add(retrofitApi.getUser());
// Zip all requests with the Function, which will receive the results.
Observable.zip(
requests,
new Function<Object[], Object>() {
#Override
public Object apply(Object[] objects) throws Exception {
// Objects[] is an array of combined results of completed requests
// do something with those results and emit new event
return new Object();
}
})
// After all requests had been performed the next observer will receive the Object, returned from Function
.subscribe(
// Will be triggered if all requests will end successfully (4xx and 5xx also are successful requests too)
new Consumer<Object>() {
#Override
public void accept(Object o) throws Exception {
//Do something on successful completion of all requests
}
},
// Will be triggered if any error during requests will happen
new Consumer<Throwable>() {
#Override
public void accept(Throwable e) throws Exception {
//Do something on error completion of requests
}
}
);
Consider the following code to listen for an update with long-polling:
Map<String, List<AsyncResponse>> tagMap = new ConcurrentGoodStuff();
// This endpoint listens for notifications of the tag
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#GET
#Path("listen/{tag}")
public void listenForUpdates(
#PathParam("tag") final String tag,
#Suspended final AsyncResponse response) {
tagMap.get(tag).add(response);
}
// This endpoint is for push-style notifications
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#PUT
#Path("update/{tag}/{value}")
public Response updateTag(
#PathParam("tag") final String tag,
#PathParam("value") final String value) {
for(AsyncResponse response : tagMap.get(tag)) {
// Resumes all previously suspended responses
response.resume(value);
}
return Response.ok("cool whatever").build();
}
The client adds a listener with the normal Jersey client's AsyncInvoker, calls the asynchronous task, and then another task calls the update method.
When I'm testing this, I run into a race condition. Right after I add the listener asynchronously on listenForUpdates(), I make an update on the endpoint with updateTag() synchronously. But the update gets run before the listener is added, and the asynchronous response fails to resume.
A solution to this is to call the suspend() method on the response after adding it to the listeners. But it's not clear how to do that, given that #Suspended provides an already-suspended AsyncResponse object. What should I do so that the async response is suspended only after adding to listener? Will that actually call the suspend method? How can I get this to work with the Jersey async client, or should I use a different long-polling client?
For solutions, I'm open to different libraries, like Atmosphere or Guava. I am not open to adding a Thread.sleep() in my test, since that is an intermittent failure waiting to happen.
I ended up using RxJava, but not before coming up with a just-as-good solution using BlockingQueue instead of List in the Map. It goes something like this:
ConcurrentMap<String, BlockingQueue<AsyncResponse>> tagMap = new ConcurrentGoodStuff();
// This endpoint initiates a listener array for the tag.
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#GET
#Path("initListen/{tag}")
public void listenForUpdates(
#PathParam("tag") final String tag) {
tagMap.putIfAbsent(tag, new LinkedBlockingQueue<>());
}
// This endpoint listens for notifications of the tag
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#GET
#Path("listen/{tag}")
public void listenForUpdates(
#PathParam("tag") final String tag,
#Suspended final AsyncResponse response) {
BlockingQueue<AsyncResponse> responses = tagMap.get(tag);
if (responses != null) {
responses.add(response);
}
}
// This endpoint is for push-style notifications
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#PUT
#Path("update/{tag}/{value}")
public Response updateTag(
#PathParam("tag") final String tag,
#PathParam("value") final String value) {
BlockingQueue<AsyncResponse> responses = tagMap.get(tag);
if (responses == null) {
return Response.noContent().build();
}
if (responses.isEmpty()) {
// Block-wait for an async listener
try {
AsyncResponse response = tagMap.poll(15, TimeUnit.SECONDS);
if (response == null) {
return Response.noContent().build();
}
response.resume(value);
} catch (InterruptedException e) {
return Response.noContent().build();
}
} else {
for (AsyncResponse response : responses) {
// Resumes all previously suspended responses
response.resume(value);
}
}
return Response.ok("cool whatever").build();
}
I haven't tested this exact code, but I used some version of it in the past. As long as you call the initListen endpoint synchronously first, you can call the asynchronous listen endpoint and then the synchronous update endpoint and there won't be any significant race condition.
There is a slight hint of a race condition in the update endpoint, but it's minor. The responses blocking queue could become empty on iteration, or it may be updated by multiple sources differently. To alleviate this, I've used the drainTo(Collection) method on a per-request instantiated data structure. This still does not solve the use case where multiple clients may try updating the same tag of listeners, but I do not need this use case.
In iOS I can make an integration test like this
// Setup expecttation to prevent from test ending before all async tasks finish.
let expectation = expectationWithDescription("Sign in");
// call API method for signing in
PersonAPI.signIn("asdf#asdf.co", password: "Free Milk Lane", done:{(response: APIResponse)->Void in
// check response for errors
XCTAssertTrue(response.isSuccessful() == true, response.getMessage());
// mark async operation is completed
expectation.fulfill();
});
// wait until all async operations completed
waitForExpectationsWithTimeout(5.0, handler:nil);
But in Android its not so obvious. Right now I am trying to use Roboelectric & Retrofit but it just doesn't want to cooperate. I have tried lots of things and I think the issue is related to how the threads for are pooled. For example the following code will pass but waits 5 seconds no matter what, even though the API call may only take 1 second:
// Setup signal to prevent from test ending before all async tasks finish.
final CountDownLatch signal = new CountDownLatch(1);
// call API method for signing in
PersonAPI.signIn("asdf#asdf.co", "Free Milk Lane", new IAPICallback() {public void done(APIResponse response) {
// check response for errors
Assert.assertTrue(response.getMessage(), response.isSuccessful());
// mark async operation is completed
signal.countDown();
}});
// wait until all async operations completed
signal.await(5, TimeUnit.SECONDS);
At this point I'm willing to try anything (except mocking). Change out the retrofit, reoboelectric, whatever.
Thanks again
Here's how I made it work. I changed retrofit to do synchronous calls. Then I stored the response in a static variable and completed the assertions on the main thread.
public class PersonAPITest {
private static APIResponse _response = null;
#Test
public void testSignIn() {
// Setup signal to prevent from test ending before all async tasks finish.
final CountDownLatch signal = new CountDownLatch(1);
// call API method for signing in
PersonAPI.signIn("asdf#asdf.co", "Free Milk Lane", new IAPICallback() {public void done(APIResponse response) {
// check response for errors
PersonAPITest._response = response;
// mark async operation is completed
signal.countDown();
}});
// wait until all async operations completed
signal.await(5, TimeUnit.SECONDS);
Assert.assertTrue(PersonAPITest._response.getMessage(), PersonAPITest._response.isSuccessful());
}
}
An example of the retrofit code is below. Basically I just used native Java multi-threading to create the asynchronous method:
public static void getGoogle(final IAPICallback callback) {
new Thread(new Runnable() {
#Override
public void run() {
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint("http://google.com")
.setClient(new OkClient(new OkHttpClient()));
RestAdapter adapter = builder.build();
ITaskAPI service = (ITaskAPI) adapter.create(ITaskAPI.class);
Response result = service.getGoogle();
APIResponse response;
if(result.getStatus() == 200) {
response = new APIResponse(true, "it worked!");
}else{
response = new APIResponse(false, "boo! bad dog!");
}
callback.done(response);
}
}).start();
}
I read answers from similar Q&A
How do you create an asynchronous HTTP request in JAVA? |
Asynchronous programming design pattern |
AsyncTask Android - Design Pattern and Return Values
I see a lot of solutions , but none really satifies me.
Listener way
Once the results are caught, the processing is implemented in onResult method.
public interface GeolocationListener {
public void onResult(Address[] addresses);
public void onError(Exception e);
}
This solution doesn't quite satify me , because I want to handle the results in the main method. I hate this interface because when the response is returned, it is processed in onResult resulting in chains of processing and no way to go back to the "main" method.
The servlet way
public class SignGuestbookServlet extends HttpServlet {
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// ...
resp.sendRedirect("/guestbook.jsp");
}
}
There is no exposed Java code calling the servlet. All the configuration is done in the web.xml
The way I want
Wait for the response like this
Response a = getResponse();
// wait until the response is received, do not go further
// process
Response b = getResponse();
// wait until the response is received, do not go further
process(a,b);
Is there a design pattern to handle the async request and wait for the response like above ? Other way than the listener.
Please no library or framework.
EDIT
Thanks so far the responses. I didn't give you the full picture so I exposed the Geolocation class
I started the implementation . I don't know how to implement the method . Can someone shows "how to" ? He (or she) must also implement the listener to retrieve the results
private Address getFullAddress (String text, AddressListener listener, ... ){
// new Geolocation(text, listener, options).start()
// implements Geolocation.GeolocationListener
// how to return the Address from the onResult ?
}
First, you should not reject the first two methods you discuss. There are very good reasons people are using those techniques and you should try to learn them instead of creating new ones.
Otherwise, you should look at java.util.concurrent:
ExecutorService es = Executors.newFixedThreadPool(2);
...
Future<Response> responseA = es.submit(responseGetter);
Future<Response> responseB = es.submit(responseGetter);
process(responseA.get(), responseB.get());
where responseGetter is of type Callable<Response> (you must implement the method public Response call()).
Asynchronous code can always be made synchronous. The simplest/crudest way is to make the async call, then enter a while loop that just sleeps the current thread until the value comes back.
Edit: Code that turns an asynchronous callback into synchronous code--again, a crude implementation:
import java.util.concurrent.*;
public class MakeAsynchronousCodeSynchronous {
public static void main(String[] args) throws Exception {
final Listener listener = new Listener();
Runnable delayedTask = new Runnable() {
#Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new IllegalStateException("Shouldn't be interrupted", e);
}
listener.onResult(123);
}
};
System.out.println(System.currentTimeMillis() + ": Starting task");
Executors.newSingleThreadExecutor().submit(delayedTask);
System.out.println(System.currentTimeMillis() + ": Waiting for task to finish");
while (!listener.isDone()) {
Thread.sleep(100);
}
System.out.println(System.currentTimeMillis() + ": Task finished; result=" + listener.getResult());
}
private static class Listener {
private Integer result;
private boolean done;
public void onResult(Integer result) {
this.result = result;
this.done = true;
}
public boolean isDone() {
return done;
}
public Integer getResult() {
return result;
}
}
}
You could also use a CountDownLatch as recommended by hakon's answer. It will do basically the same thing. I would also suggest you get familiar with the java.util.concurrent package for a better way to manage threads. Finally, just because you can do this doesn't make it a good idea. If you're working with a framework that's based on asynchronous callbacks, you're probably much better off learning how to use the framework effectively than trying to subvert it.
Could CountDownLatch help you? In the main method, you call getResponse and then countDownLatch.await(). Pass a count down latch to the getResponse method and then count down once getResponse the result from getResponse is finished:
CountDownLatch latch = new CountDownLatch(1);
Response a = getResponse(latch);
latch.await();
latch = new CountDownLatch(1);
Response b = getResponse(latch);
latch.await();
process(a, b);
Your getResponse needs to call latch.countDown() once it's asynch parts return a result.
e.g.:
public Response getResponse(CountDownLatch latch) {
someAsychBloc(final CountDownLatch latch) {
do work
latch.countDown();
}
}
Essentially you need a "listener" of sorts no matter what. This is because you do not know WHEN your return message will come back, if at all (that is one of the downsides of asynchronous processing...what to do if you do not get a return message).
So you either need to implement a listener that waits for events (ie, it is nudged by the returning message to be processed).
Or you could do a hybrid on that by having a separate thread that "polls" (or pulls) a response area on your service to see if the return message exists.
So it really comes down to whether you want more of a "pull" or "push" method of retrieving messages.
The SCA (Service Component Architecture) framework might be something to consider, but depending on what you are doing, it could be overkill too. But something to consider.
EDIT:
I just found this in the Java SE 6 Javadocs that may be helpful. The
interface CompletionService which abstracts the very thing you care
about --> asynchronous work. I suggest you take a look.
If you want a page flow in a web application, you have to handle in the web way : storing some data either in the session, or cookies or hidden fields, etc.
The problem you're trying to tackle, as far as I understand it, doesn't come from asynchronousness but from the stateless http protocole.
Regards,
Stéphane