Handling multiple threads/outgoing http requests - java

I am working on a Java/Spring web application that for each incoming request does the following:
fires off a number of requests to third party web servers,
retrieves the response from each,
parses each response into a list of JSON objects,
collates the lists of JSON objects into a single list and returns it.
I am creating a separate thread for each request sent to the third party web servers. I am using the Apache PoolingClientConnectionManager. Here is an outline of the code I am using:
public class Background {
static class CallableThread implements Callable<ArrayList<JSONObject>> {
private HttpClient httpClient;
private HttpGet httpGet;
public CallableThread(HttpClient httpClient, HttpGet httpGet) {
this.httpClient = httpClient;
this.httpGet = httpGet;
}
#Override
public ArrayList<JSONObject> call() throws Exception {
HttpResponse response = httpClient.execute(httpGet);
return parseResponse(response);
}
private ArrayList<JSONObject> parseResponse(HttpResponse response) {
ArrayList<JSONObject> list = null;
// details omitted
return list;
}
}
public ArrayList<JSONObject> getData(List<String> urlList, PoolingClientConnectionManager connManager) {
ArrayList<JSONObject> jsonObjectsList = null;
int numThreads = urlList.size();
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
List<Future<ArrayList<JSONObject>>> list = new ArrayList<Future<ArrayList<JSONObject>>>();
HttpClient httpClient = new DefaultHttpClient(connManager);
for (String url : urlList) {
HttpGet httpGet = new HttpGet(url);
CallableThread worker = new CallableThread(httpClient, httpGet);
Future<ArrayList<JSONObject>> submit = executor.submit(worker);
list.add(submit);
}
for (Future<ArrayList<JSONObject>> future : list) {
try {
if (future != null) {
if (jsonObjectsList == null) {
jsonObjectsList = future.get();
} else {
if (future.get() != null) {
jsonObjectsList.addAll(future.get());
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executor.shutdown();
return jsonObjectsList;
}
}
This all works fine. My question is in relation to how well this code will scale as the traffic to my website increases? Is there a better way to implement this? For example, by implementing non-blocking I/O to reduce the number of threads being created. Are there libraries or frameworks that might help?
At the moment, I am using Java 6 and Spring Framework 3.1
Thanks in advance

I wouldn't recommend to implement this as a synchronous service. Do it asynchronously. Get your request, pool the callables, and return a resource location where the client can later request the result.
You've got to be pooling this callables in an executor. Poll the executor in a background process and make avalable the results in the location you returned at the first request. Doing it this way, it would be easier to control your available resuources, and deny cleanly a processing requests if there aren't any more resources available.
Non blocking IO won't reduce the number of threads, it just delegates the "job" to another thread, in order for the service thread not to be blocked and to be able to receive more requests.
use REST.
Receive a POST request, and answer with something like this:
HTTP/1.1 202 Accepted
Location: /result/to/consult/later
The client can then request the resutl at the given location. If the processing has not finished, then answer with:
HTTP/1.1 201 Created
If its done then return a HTTP/1.1 200 OK with the resulting JSON.

Related

Java HttpClient hangs after request takes too long to respond

I have an API that process a lot of information by calling another API. So for example, when the process starts in the API A, it makes a lot of http requests do the API B. It works fine for most requests, but some of them sometimes takes a lot of time to proccess on API B (more than 20 minutes). When it happens, the API B responds to the request successfully, but API A appears to not be aware of this response. So it just hangs infinitely waiting for a response.
Both API's are using kubernetes and nginx as a proxy. I was having some timeout problems, so I changed my timeout to 2000 seconds in my config file and for a while I didn't have any problems.
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/proxy-connect-timeout: "2000"
nginx.ingress.kubernetes.io/proxy-send-timeout: "2000"
nginx.ingress.kubernetes.io/proxy-read-timeout: "2000"
nginx.ingress.kubernetes.io/send-timeout: "2000"
This is how I'm calling the API B from API A:
#Override
public void startProcess(List<Long> ids) {
logger.info("Starting process...");
post("/start", ids);
logger.info("Process finished!");
}
public <T, S> T post(String endpoint, S body, Class<?>... clazz) {
var uri = generateURI(endpoint);
var requestBuilder = HttpRequest.newBuilder(uri).timeout()
.POST(HttpRequest.BodyPublishers.ofString(Serializer.serialize(body)));
addCustomHeaders(requestBuilder, HttpMethod.POST);
var request = requestBuilder.build();
return doRequest(request, clazz);
}
#SneakyThrows
private <T> T doRequest(HttpRequest request, Class<?>... clazz) {
SSLContext context = obterContextoSsl();
HttpClient client = HttpClient.newBuilder()
.sslContext(context)
.version(HttpClient.Version.HTTP_1_1)
.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (!HttpStatus.valueOf(response.statusCode()).is2xxSuccessful()) {
logger.error(String.format("Request error! %d: %s", response.statusCode(), response.body()));
throw new HttpException(String.format("%d: %s", response.statusCode(), response.body()));
}
if (clazz.length == 0 || response.statusCode() == HttpStatus.NO_CONTENT.value())
return null;
return Serializer.deserialize(response.body(), clazz);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
logger.error(ex);
throw new HttpException(ex.getMessage());
} catch (IOException | HttpException ex) {
logger.error(ex);
throw new HttpException(ex.getMessage());
}
}
This is the log from API A:
2022-07-13 17:33:35.323 INFO 1 --- [pool-5-thread-1] c.v.f.m.r.Repository: Starting process...
That's the only log from API A. Even after API B respond successfully to the request, API A just hangs there forever. Maybe somehow the connection is being closed but API A is not aware of it? Does anyone knows a way to fix this?

How to get Youtube Api Json Response

I have implement insert rating method in my app . I am searching more than 1 days to get the response . In this page i have check that insert like method and json is giving response . Look .
But i don't find any way to get the response in my app . How can i solve this ?
#SuppressLint("StaticFieldLeak")
class Insert extends AsyncTask<Object,Object, Object> {
#Override
protected Object doInBackground(Object... objects) {
if (Email!=null){
mCredential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(YouTubeScopes.YOUTUBE))
.setBackOff(new ExponentialBackOff());
mCredential.setSelectedAccountName(Email);
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
YouTube youtubeService = new YouTube.Builder(
transport, jsonFactory, mCredential)
.setApplicationName(LikeInsertActivity.this.getResources().getString(R.string.app_name))
.build();
// Define and execute the API request
try {
YouTube.Videos.Rate request = youtubeService.videos()
.rate(VID, "like");
request.execute();
runOnUiThread(new Runnable() {
public void run() {
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
As #mavriksc mentions Rate.execute() returns Void. This is due to the fact that all these object are based on the superclass
com.google.api.services.youtube.YouTubeRequest<java.lang.Void>
However instead of the execute method you can use other methods defined by AbstractGoogleClientRequest which is a super class of YouTubeRequest.
For example executeUnparsed returns a com.google.api.client.HttpResponse object.
So obtaining that HttpResponse object and checking the StatusCode vs 204 seems to be the solution you want to have.
Example:
try {
final YouTube.Videos.Rate request = youtubeService.videos().rate(VID, "like");
runOnUiThread(new Runnable() {
public void run() {
HttpResponse response = request.executeUnparsed();
// There should be a matching constant for 204 defined somewhere, I haven't found it yet
if (response.getStatusCode() == 204) {
// request successfull
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
Note:
Android forces developers to do certain (time consuming) things (like NetworkCommunication) in a background task to prevent the UI from blocking.
The return type of Rate.execute() is Void. looking at the HTTP it seems like you get a 204 no content on a good response and an exception otherwise.
https://developers.google.com/youtube/v3/docs/videos/rate

How to wait for the HTTP request to get completed in java

I am writing Java code where i am downloading the file from a server and i have to copy the file in my local system when the file download is complete.
I am using the below code:-
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client = builder.readTimeout(600, TimeUnit.SECONDS).writeTimeout(600, TimeUnit.SECONDS)
.connectTimeout(600, TimeUnit.SECONDS).build();
Request downloadRequest = new Request.Builder().url(url + fileName).addHeader("cache-control", "no-cache")
.addHeader("Authorization", token).build();
try {
Response downloadResponse = client.newCall(downloadRequest).execute();
System.out.println(downloadResponse.message());
System.out.println("got response from blob " + downloadResponse.isSuccessful() + " " + fileName);
return downloadResponse;
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
But the request is made asynchronously and before the request is completed then response is returned which is incomplete. Can anyone please help me how can i make a request and wait till the response is completed.
Any help is highly appreciated!
Looks like you're returning the response object (not the response body content).
try something like:
return downloadedResponse.body().string()
My experience with HttpClient is such that the headers return first. The content doesn't necessarily come across the wire unless/until you consume it.
To make a synchronous GET request we need to build a Request object based on a URL and make a Call. After its execution we get back an instance of Response:
#Test
public void whenGetRequest_thenCorrect() throws IOException {
Request request = new Request.Builder()
.url(BASE_URL + "/date")
.build();
Call call = client.newCall(request);
Response response = call.execute();
assertThat(response.code(), equalTo(200));
}
You are already using synchronous method calling.
client.newCall(downloadRequest).execute();
This is a synchronous way of requesting URL. If you want to do the aysynchronous call you need to use enqueue method of Call class.
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException {
// ...
}
public void onFailure(Call call, IOException e) {
fail();
}
});
I think problem is somewhere else. Kindly give more details why you are suspecting the current one as an asynchronous call so that we can do RCA.

How to avoid waiting on main thread when adding headers to requests using retrofit?

I use this to config my retrofit:
RestAdapter restAdapter = new RestAdapter.Builder()
//add headers to requests
.setRequestInterceptor(getAuthenticatedRequestInterceptor())
.setEndpoint(BASE_URL)
.setConverter(new GsonConverter(getGson()))
.build();
and The getAuthenticatedRequestInterceptor() method adds headers to request:
public AccountRequestInterceptor getAuthenticatedRequestInterceptor() {
AccountRequestInterceptor interceptor = new AccountRequestInterceptor();
Map<String, String> headers = new HashMap<>();
String accessToken = null;
try {
accessToken = TokenProvider.getInstance(mContext).getToken();
} catch (InterruptedException e) {
}
headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
interceptor.setHeader(headers);
return interceptor;
}
getToken() method is:
private synchronized string getToken() throws InterruptedException {
if (!isRefreshing()) {
//This is very important to call notify() on the same object that we call wait();
final TokenProvider myInstance = this;
setRefreshing(true);
MyApplication.getRestClient().getAccountService().getRefreshedToken(mLoginData.getRefreshToken())
.subscribe(new Observer<LoginResponse>() {
#Override
public void onCompleted() {
synchronized (myInstance) {
setRefreshing(false);
myInstance.notifyAll();
}
}
#Override
public void onError(Throwable e) {
synchronized (myInstance) {
setRefreshing(false);
myInstance.notifyAll();
}
}
#Override
public void onNext(LoginResponse loginResponse) {
synchronized (myInstance) {
mLoginData = loginResponse;
mAccountProvider.saveLoginData(loginResponse);
myInstance.notifyAll();
}
}
});
}
this.wait();
return mLoginData.getToken();
}
The TokenProvider.getInstance(mContext).getToken() has a wait() on main thread to get the response from an async method and i know that is a bad thing to do but i need this here to wait for the response to take the token from it and then return the token.how can i do this in a separate thread to avoid waiting on the main thread?
Note:
1 - that this is called before any request with retrofit.
2 - I read this and i know i can refresh token after a fail request, but for business reasons i want to avoid having an invalid token.
3 - I call MyApplication.getRestClient().getAccountService().login(loginRequest,callback...‌​) in my Activity and before adding token everything happened in background thread. so I want to use my token and do not block the main thread.
UPDATE: I added the following Interceptor to my new OkHttp:
public class RequestTokenInterceptor implements Interceptor {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Request newRequest;
try {
Log.d("addHeader", "Before");
String token = TokenProvider.getInstance(mContext).getToken();
if (token != null) {
newRequest = request.newBuilder()
.addHeader("Bearer", token)
.build();
} else {
// I want to cancel the request or raise an exception to catch it in onError method
// of retrofit callback.
}
} catch (InterruptedException e) {
Log.d("addHeader", "Error");
e.printStackTrace();
return chain.proceed(request);
}
Log.d("addHeader", "after");
return chain.proceed(newRequest);
}
}
Now how can i cancel the request or raise an exception to catch it in onError method of retrofit callback, if token is null?
It's a little bit strange issue but let me try to help you. :)
As you know you can refresh token after a failed request with retrofit using response interceptor.
Let's try to use interceptor before request.
public class RequestTokenInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Here where we'll try to refresh token.
// with an retrofit call
// After we succeed we'll proceed our request
Response response = chain.proceed(request);
return response;
}
}
And when you're creating your api create a new HttpClient:
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new RequestTokenInterceptor());
And add your http client to your adapter like below:
.setClient(new OkClient(client))
If this works, before every request you'll try to refresh token first and then will proceed your api request. So in ui there'll be no difference with your normal api calls.
Edit:
I'm editing my answer too. If you want to return an error in else case if token null, in else case you can create your custom response:
private Response(Builder builder) {
this.request = builder.request;
this.protocol = builder.protocol;
this.code = builder.code;
this.message = builder.message;
this.handshake = builder.handshake;
this.headers = builder.headers.build();
this.body = builder.body;
this.networkResponse = builder.networkResponse;
this.cacheResponse = builder.cacheResponse;
this.priorResponse = builder.priorResponse;
}
or simply you can return a null response. if you build your custom response and set your code not to 200 such as 401 or 400+ you'll receive that response in Retrofit's callbacks failure method. Than you can do what ever you want.
If you return null you'll get a RuntimeException i think and still you can catch response in your callback's failure method.
After you create your own response in else you can create your custom callback and catch your null response and transform your custom error how ever you want like below:
public abstract class DefaultRequestCallback<T> implements Callback<T> {
public abstract void failure(YourCustomException ex);
public abstract void success(T responseBean);
#Override
public void success(T baseResponseBean, Response response) {
if (response == null) {
// Here we catch null response and transform it to our custom Exception
failure(new YourCustomException());
}
} else {
success(baseResponseBean);
}
}
#Override
public void failure(RetrofitError error) {
// Here's your failure method.
// Also you can transform default retrofit errors to your customerrors
YourCustomException ex = new YourCustomException();
failure(ex);
}
}
This can help you i think.
Edit 2:
You can build a new Response like below. There's a builder pattern in Retrofit's Response class. You can check it from there.
Response response = new Response.Builder().setCode(401).setMessage("Error Message").build();
You could make all long actions in AsyncTask doInBackground method, while in onPre- and onPostExecute you could show/hide some progress bars when user is waiting
Ok, I think if you are calling your getAuthenticatedRequestInterceptor() on the main thread and which in turns call getInstance(),in which i feel you would be creating an object of Type TokenProvider hence when you create this object in the main thread your object.wait() runs on main thread hence to run this on a background thread probably modify your getAuthenticatedRequestInterceptor() method to execute the following lines in a new thread.
try {
accessToken = TokenProvider.getInstance(mContext).getToken();
} catch (InterruptedException e) {
}
headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
interceptor.setHeader(headers);
return interceptor;
but this will have problems for notifying your RestAdapter as the main thread will proceed executing, hence i would suggest
you call getAuthenticatedRequestInterceptor() method first in a new thread and then notify your main thread to build your RestAdapter.This will free your main thread but with the strategy you are employing you will have to wait until you receive the token to make any calls.

How to use HttpAsyncClient with multithreaded operation?

Closely related to this question: How to use HttpClient with multithreaded operation?, I'm wondering if apache HttpAsyncClient is thread safe, or if it, too, requires the use of a MultiThreadedHttpConnectionManager, or a ThreadSafeClientConnManager.
If it does require such a connection manager, does one exist in the async libraries?
I was able to find a PoolingClientAsyncConnectionManager in the async libraries, but I'm not sure if that's what I need.
Alternately, I was thinking of using ThreadLocal to create one HttpAsyncClient object per thread.
Note that unlike the question I referenced earlier, I need state to be independent across sessions, even if multiple sessions hit the same domain. If a cookie is set in session 1, the cookie should not be visible to session 2. For this reason, I've also considered creating a brand new HttpAsyncClient object for every single request, though I get the impression there should be a better way.
Thanks.
You mention "independent across sessions". If this just means cookies then I would think creating your own CookieStore which is cleared when each of your threads goes to use a HttpClient would be enough.
I would use ThreadLocal to create a per-thread client, don't use a shared connection manager, and then clear the cookies aggressively. This answer was useful around cookie clearing:
Android HttpClient persistent cookies
Something like the following code would work. I've overridden the ThreadLocal.get() method to call clear() in case each request is independent. You could also call clear in the execute(...) method.
private static final ThreadLocal<ClientContext> localHttpContext =
new ThreadLocal<ClientContext> () {
#Override
protected ClientContext initialValue() {
return new ClientContext();
}
#Override
public ClientContext get() {
ClientContext clientContext = super.get();
// could do this to clear the context before usage by the thread
clientContext.clear();
return clientContext;
}
};
...
ClientContext clientContext = localHttpContext.get();
// if this wasn't in the get method above
// clientContext.clear();
HttpGet httpGet = new HttpGet("http://www.google.com/");
HttpResponse response = clientContext.execute(httpGet);
...
private static class ClientContext {
final HttpClient httpClient = new DefaultHttpClient();
final CookieStore cookieStore = new BasicCookieStore();
final HttpContext localContext = new BasicHttpContext();
public ClientContext() {
// bind cookie store to the local context
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
}
public HttpResponse execute(HttpUriRequest request) {
// in case you want each execute to be indepedent
// clientContext.clear();
return httpClient.execute(request, httpContext);
}
public void clear() {
cookieStore.clear();
}
}
After load testing both with and without the PoolingClientAsyncConnectionManager, we discovered that we got inconsistent results when we did not use the PoolingClientAsyncConnectionManager.
Amongst other things, we tracked the number of Http calls we were making, and the number of Http calls that were completed (either through the cancelled(...), completed(...), or failed(...) functions of the associated FutureCallback). Without the PoolingClientAsyncConnectionManager, and under heavy load, the two figures sometimes did not match up, leading us to believe that somewhere, some connections were stomping on connection information from other threads (just a guess).
Either way, with the PoolingClientAsyncConnectionManager, the figures always matched, and the load tests were all successful, so we are definitely using it.
The final code we used goes like this:
public class RequestProcessor {
private RequestProcessor instance = new RequestProcessor();
private PoolingClientAsyncConnectionManager pcm = null;
private HttpAsyncClient httpAsyncClient = null;
private RequestProcessor() {
// Initialize the PoolingClientAsyncConnectionManager, and the HttpAsyncClient
}
public void process(...) {
this.httpAsyncClient.execute(httpMethod,
new BasicHttpContext(), // Use a separate HttpContext for each request so information is not shared between requests
new FutureCallback<HttpResponse>() {
#Override
public void cancelled() {
// Do stuff
}
#Override
public void completed(HttpResponse httpResponse) {
// Do stuff
}
#Override
public void failed(Exception e) {
// Do stuff
}
});
}
}

Categories

Resources