Apache's HttpAsyncClient unreliable with multiple requests - java

I'm currently in a project where I have to do multiple, concurrent http requests to a rest service which returns a JSON response. This is a batch operation and the number of requests at any time could range from several hunderd to several thousend.
That's why I thought it would be a good idea to have an async http client so I could have concurrent requests, which dramatically could speed up the process. I first tried ning's async-http-client. Maybe I was doing something wrong, because it was kind of slow for me. About 10 seconds for 1000 requests.
After which I tried Apache's implementation which was much faster at about 4 seconds for 1000 requests. But I can't seem to get the requests to get stable. Most of the time I will get a List with a 1000 responses (like I expect), but sometimes I am just missing a few responses, like 1 or 2.
This is currently my code:
public class AsyncServiceTest {
public AsyncServiceTest(String serviceURI) {
this.httpClient = HttpAsyncClients.custom().setMaxConnPerRoute(100).setMaxConnTotal(20)
.setDefaultRequestConfig(RequestConfig.custom().build()).build();
this.objectMapper = new ObjectMapper();
this.serviceURI = serviceURI;
}
private List<Object> getResults(List<String> queryStrings) throws Exception {
try {
httpClient.start();
final List<HttpGet> requests = new ArrayList<>(addresses.size());
for (String str : queryStrings) {
requests.add(new HttpGet(buildUri(str))); // In this method we build the absolute request uri.
}
final CountDownLatch latch = new CountDownLatch(requests.size());
final List<Object> responses = new ArrayList<>(requests.size());
final List<String> stringResponses = new ArrayList<>(requests.size());
for (final HttpGet request : requests) {
httpClient.execute(request, new FutureCallback<HttpResponse>() {
#Override
public void completed(HttpResponse response) {
try {
stringResponses.add(IOUtils.toString(response.getEntity().getContent(), "UTF-8"));
latch.countDown();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void failed(Exception e) {
latch.countDown();
}
#Override
public void cancelled() {
latch.countDown();
}
});
}
latch.await();
for (String r : stringResponses) {
responses.add(mapToLocation(r)); // Mapping some Strings to JSON in this method.
}
return responses;
} finally {
httpClient.close();
}
}
}
So, in essence, I am wondering if there is something wrong with my code (probably) or is it just because of the way the library works? Because the CountDownLatch is at zero all the time. Or does anyone have a pointer in the right direction (maybe with another library)?

It seemed to be a concurrency problem (thanks to #vanOekel) in my code. The answer is to replace the ArrayList<E> with a Vector<E>, which is in fact thread-safe. Example code:
public class AsyncServiceTest {
public AsyncServiceTest(String serviceURI) {
this.httpClient = HttpAsyncClients.custom().setMaxConnPerRoute(100).setMaxConnTotal(20)
.setDefaultRequestConfig(RequestConfig.custom().build()).build();
this.objectMapper = new ObjectMapper();
this.serviceURI = serviceURI;
}
private List<Object> getResults(List<String> queryStrings) throws Exception {
try {
httpClient.start();
final CountDownLatch latch = new CountDownLatch(queryStrings.size());
final Vector<Object> responses = new Vector<>(queryStrings.size());
for (String str : queryStrings) {
// buildUri: In this method we build the absolute request uri.
httpClient.execute(new HttpGet(buildUri(str)), new FutureCallback<HttpResponse>() {
#Override
public void completed(HttpResponse response) {
try {
// mapToLocation: Mapping some Strings to JSON in this method.
responses.add(mapToLocation(IOUtils.toString(response.getEntity().getContent(), "UTF-8")));
latch.countDown();
} catch (IOException e) {
failed(e);
}
}
#Override
public void failed(Exception e) {
logger.error(e.getLocalizedMessage(), e);
latch.countDown();
}
#Override
public void cancelled() {
logger.error("Request cancelled.");
latch.countDown();
}
});
}
latch.await();
return responses;
} finally {
httpClient.close();
}
}
}
Thanks for all the helpful responses. If anyone has any suggestions regarding optimization of the above code, I will be glad to hear so.

Related

Spring Boot application blocking StreamingResponseBody

I have a Spring Boot application where I created a POST method that sends data in a streaming fashion to the caller. Code below:
#RequestMapping(value = "/mapmatchstreaming", method = RequestMethod.POST)
public ResponseEntity<StreamingResponseBody> handleRequest(#RequestParam(value = "data", required = true) String data, #RequestParam(value = "mnr", required = true) Boolean mnr) {
logger.info("/mapmatchstreaming endpoint");
try {
Semaphore semaphore = new Semaphore(1);
ObjectMapper mapper = new ObjectMapper();
StreamingResponseBody responseBody = new StreamingResponseBody() {
#Override
public void writeTo (OutputStream outputStream) throws IOException {
// For each map
DataReader dataReader = new DataReader(data, "2020.06.011");
for(String mapRoot: dataReader.getMapsFolders()) {
dataReader = new DataReader(data, "2020.06.011");
DistributedMapMatcherStreaming distributedMapMatcher = new DistributedMapMatcherStreaming(dataReader.getTraces(), mapRoot, dataReader.getBoundingBox());
distributedMapMatcher.mapMatchBatch(new DistributedMapMatcherResult() {
#Override
public void onCorrectlyMapMatched(MapMatchedTrajectory mapMatchedTrajectory) {
try {
semaphore.acquire();
outputStream.write(mapper.writeValueAsString(mapMatchedTrajectory).getBytes());
outputStream.flush();
}
catch (Exception e) {
e.printStackTrace();
logger.error(String.format("Writing to output stream error: %s", e.getMessage()));
} finally{
semaphore.release();
}
}
});
}
}
};
return new ResponseEntity<StreamingResponseBody>(responseBody, HttpStatus.OK);
}
catch (Exception e) {
logger.error(String.format("Map-matching result ERROR: %s", ExceptionUtils.getStackTrace(e)));
return new ResponseEntity<StreamingResponseBody>(HttpStatus.BAD_REQUEST);
}
}
It works nicely, but the problem is that if multiple calls arrive to this method, all of them are run in parallel even if I have set server.tomcat.threads.max=1. In the non-streaming version, every next call waits for the current one to complete.
Is it possible to have blocking streaming calls in Spring? Thanks.
EDIT: I temporarily solved by using a global semaphore with only 1 permit, but I don't think this is the ideal solution.

How to call multiple soap calls parallel in java-spring application

I want to make my soap request calls parallel in java.
I'm new to multithreading not getting the way to do it.
public List<TB600Model.Response> getTableDesc(final List<TB600Model.Request> requests)
{
List<TB600Model.Response> responses = new ArrayList<>();
for (TB600Model.Request request : requests)
{
responses.add(
this.modifyDescription(
this.getDescription(
request.getSite()
,request.getDescType()
,request.getKeyData()
,request.getEffMdy()
)
,request.getDescType()
,request.getKeyData()
)
);
}
return responses;
}
enter image description here
For Java 8+ Try to use this method instead :
requests.parallelStream().forEach(request -> {...});
put the code for response.add instead of ...
For earlier version :
ExecutorService es = Executors.newFixedThreadPool(10);
List<Response> responses = new ArrayList<>();
for(Request request: requests){
Future<Response> future = es.submit(new Callable<Response>() {
#Override
public Response call() throws Exception {
//create your response here and return
}
});
try {
responses.add(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}

Java Spring, waiting for AsyncHandlers

apologies for the basic question; I'm new to the Java world and the spring framework. I've built a little example application that makes a bunch of async requests to an external service and returns a list of the responses ('metrics'), but I need to make my application wait until all the responses have come back. Right now I have a (don't hate me) Thread.sleep while I let the results come back, but obviously this is very nasty. Can anyone suggest a better way of architecting this?
Calling class:
#Service
public class MetricService {
#Autowired
private MetricProcessor processor;
private LinkedBlockingQueue<Metric> queue;
#Scheduled(fixedDelay = 60000)
public void queryExternalService() {
List<Metrics> metrics = new ArrayList<>();
metrics = processor.getMetrics();
//this is horrible and I'm a horrible human being
try {
Thread.sleep(10000); //wait for the requests to come back
}
catch (Exception e) {
e.printStackTrace();
}
queue.addAll(metrics);
}
}
Class:
#Component
public class MetricProcessor {
#Autowired
private AsyncClient externalClient;
public List<Metrics> getMetrics() {
List<Metrics> returnObj = new Arraylist<>();
for(Blah blah : bleh) {
Request request = new Request("abc");
externalClient.getMetricAsync(request, new AsyncHandler<request, result>() {
#Override
public void onError(Exception e) {
System.out.println("Error");
}
#Override
public void onSuccess(Request request, Result result) {
returnObj.add(new Metric(result.getKey(), result.getValue()));
}
});
}
return returnObj;
}
}
Any help would be greatly appreciated!
Try a Future.
In MetricService:
public void queryExternalService() {
Future<List<Metrics>> metricsFuture = processor.getMetrics();
try {
queue.addAll(metricsFuture.get(60, TimeUnit.SECONDS));
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
So notice instead of the desired List, your processor provides a reference to a Future which may fulfil that request later:
public Future<List<Metrics>> getMetrics() {
MetricsFuture metricsFuture = new MetricsFuture();
// Need to ask for the metrics to be built
metricsFuture.buildMetrics();
return metricsFuture;
}
private static class MetricsFuture extends AbstractFuture<List<Metrics>> {
// Assuming the requests are asynchronous, this should be a thread-safe list
List<Metrics> returnObj = new CopyOnWriteArrayList<>();
void buildMetrics() {
for(Blah blah : bleh) {
final Request request = new Request("abc");
externalClient.getMetricAsync(request, new AsyncHandler<request, result>() {
#Override
public void onError(Exception e) {
onError(request, e);
}
#Override
public void onSuccess(Request request, Result result) {
addMetrics(new Metrics(result.getKey(), result.getValue()));
}
});
}
}
void onError(Request request, Exception e) {
// Is any error a total failure? This allows us to terminate waiting
setException(e); // alternative we could remove request or keep a list of errors
System.out.println("Error");
}
void addMetrics(Metrics metric) {
returnObj.add(metric);
// Once we have received the expected number of results we can pass that prepare that
// as a result of this future.
if(returnObj.size() == bleh.size()) {
set(returnObj);
}
}
}

Could not create epoll instance. errno=24

what does this runtime-error mean?
I already googled it, some say, it belongs to timers, other say its a socket error and more say, it belongs to pictures. I have sockets and timers (lot of timers) and i have no idea, which of these causes it. SOmetimes it works for over an hour, and other times just for 5 minutes. Any Ideas?
A basic impression of what this error is about is enough. If i would post all the code, where it could happen, this page would be multiple kilometres long ( a little extreme of course, but it is lot of code.)
Found right now, it could belong to too many open files but im not using any extern files in my app.
Seems to be a memory leak, belonging to this part:
public static Runnable connection() throws IOException {
Log.e("Communication", "connection");
new Thread(new Runnable() {
public void run() {
Looper.prepare();
try {
serv = new ServerSocket(port); sock = serv.accept();
reader(); } catch (IOException e) {
e.printStackTrace();
}
}
}).start();
return null;
After deleting a part of the code above everything works fine again. Deleted the looper.prepare() and my app does not die anymore.
public static void sendJsonList(final List<String> jsonStrlist,
final String resturl) {
Thread t = new Thread() {
public void run() {
Looper.prepare();
/* Your HTTP clients code */
try {
for (String jsonStr : jsonStrlist) {
/* Loop logic */
response = client.execute(post);
if (response != null) {
/*reponse handler logic */
}
}
} catch (Exception e) {
e.printStackTrace();
}
Looper.loop();
}
};
t.start();
}

Reading a very large local XML file using GWT

I am building my first Java application using GWT which must read in data from a very large XML file. I am having issues when I try sending a request for the information in the file, and I'm not quite sure if it has to do with the size of the file, or my semantics. In my program I have the following:
static final String xmlurl = "filename.xml";
String xmlData;
...
public void onModuleLoad() {
requestData(xmlurl);
if(xmlData.equals("Error")){
// display error message
return;
} else {
// display the xml
}
void requestData(String url){
final int STATUS_CODE = 200;
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
try {
builder.setTimeoutMillis(2000);
builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
xmlData = "Error"
}
public void onResponseReceived(Request request, Response response) {
if (STATUS_CODE == response.getStatusCode()){
xmlData = response.getText();
} else {
xmlData = "Error";
}
}
}
} catch (RequestException e) {
xmlData = "Error";
}
}
I rewrote the code, so I might have made some typos, but for the actual application it compiles and runs. The issue is that I get a nullpointer exception when I try to display the XML and xmlData is never assigned to anything. I tried putting a while loop that waited for it to store either "Error" or the XML text, but the variable was never assigned to. I have the XML file saved in the war directory of my project, and it seems to be able to find the file. I've searched online for similar examples, but everything seemed to be a bit more complicated than what I'm trying to do, and I'm not sure if I need a servlet or a configuration change for this, or if the file is just too big to read into a String. Any help is appreciated. Thanks.
Parsing xml at client-side (in browser) is quite slow, and should be avoided; delegating this to server-side is usually faster and therefore more user friendly (big files will cause your browser stop responding for a long time).
However the decision is yours ;) Here is what I use for reading files:
Define this helper method:
public static void httpGetFile(final String url, final AsyncCallback<String> callback) {
final RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
rb.setCallback(new RequestCallback() {
public void onResponseReceived(Request request, Response response) {
try {
final int responseCode = response.getStatusCode() / 100;
if (url.startsWith("file:/") || (responseCode == 2)) {
callback.onSuccess(response.getText());
} else {
callback.onFailure(new IllegalStateException("HttpError#" + response.getStatusCode() + " - " + response.getStatusText()));
}
} catch (Throwable e) {
callback.onFailure(e);
}
}
public void onError(Request request, Throwable exception) {
callback.onFailure(exception);
}
});
try {
rb.send();
} catch (RequestException e) {
callback.onFailure(e);
}
}
In your code, it might look like this:
...
httpGetFile(url, new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
xmlData = "Error";
}
public void onSuccess(String xmlText) {
xmlData = xmlText;
}
}
....

Categories

Resources