I have a processor that reads messages from Apache Kafka and sends the data to a REST Endpoint.
The server only has 4 cores and 4 GB ram, out of which a max of 2GB is allocated to the java process
Messages are produced and consumed at the rate of 4k/second.
After running couple of minutes, the program goes out of memory.
What is the best way to call http rest end-points asynchronously and not wait for response
How to manage the httpClient connection? I was under the impression that I need to start the client an never close it so I can reuse the connection
Do you see any issues with the below code
public class SomeProcesor implements BProcessor {
private ThreadPoolExecutor exec = (ThreadPoolExecutor) Executors.newFixedThreadPool(4);
private CompletionService<Boolean> pool = new ExecutorCompletionService<Boolean>(exec);
CloseableHttpAsyncClient httpclient = null ;
#Override
public void begin() {
httpclient = HttpAsyncClients.createDefault();
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(5000).setConnectTimeout(5000).setSocketTimeout(5000).build();
HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig).build();
// Start the client
httpclient.start();
}
#Override
public void process(MessageAndMetadata<?, ?> mMData, List<Map> events) {
List<Map<String, Object>> listMap = new ArrayList<>();
// loop and extract the data from events into the above List
//..
//..
// submit to seperate thread to post to HTTP
pool.submit(new HttpThread(listMap);
}
private class HttpThread implements Callable<Boolean> {
List<Map<String, Object>> listMap = null;
public HttpThread(List<Map<String, Object>> listMap) {
this.listMap = listMap;
}
#Override
public Boolean call() throws Exception {
return postToHttp(listMap);
}
}
private Boolean postToHttp(List<Map<String, Object>> listMap) throws UnsupportedEncodingException {
for (Map<String, Object> map : listMap) {
try {
HttpPost postRequest = new HttpPost("https://myserver:8080/services/collector");
postRequest.addHeader(HttpHeaders.ACCEPT, "application/json");
postRequest.addHeader(HttpHeaders.CONTENT_TYPE, "application/json");
postRequest.addHeader(HttpHeaders.CONNECTION, "keep-alive");
StringEntity input = new StringEntity(methodToConvertMapToJSON(map));
input.setContentType("application/json");
postRequest.setEntity(input);
httpclient.execute(postRequest, null);
} catch (Exception e) {
return false;
} catch (Throwable th) {
return false;
}
}
return true;
}
}
need consume the http response or release connection, otherwise the connection will consume resources.
change
httpclient.execute(postRequest, null);
to
HttpResponse response = httpclient.execute(postRequest, null).get();
if(response.getStatusLine().getStatusCode() != 200) {
// do something
}
// release the connection, better add to a finally clause
postRequest.releaseConnection();
Related
RestTemplate example is below.
public class SimpleClient {
private final String URL;
private AsyncRestTemplate rest = new AsyncRestTemplate(new Netty4ClientHttpRequestFactory());
private RestTemplate restTemplate = new RestTemplate(new Netty4ClientHttpRequestFactory());
public SimpleClient(String url) {
this.URL = url;
Netty4ClientHttpRequestFactory nettyFactory = new Netty4ClientHttpRequestFactory();
try {
nettyFactory.setSslContext(SslContextBuilder.forClient().build());
} catch (SSLException e) {
e.printStackTrace();
}
rest = new AsyncRestTemplate(nettyFactory);
}
#Override
public ResponseEntity<ResponseData> doSendByPOST(RequestData data,Class<ResponseData> clazz) {
List<HttpMessageConverter<?>> messageConvertors = new ArrayList<>();
messageConvertors.add(new MappingJackson2HttpMessageConverter());
rest.setMessageConverters(messageConvertors);
restTemplate.setMessageConverters(messageConvertors);
HttpHeaders headers = new HttpHeaders();
ObjectMapper objectMapper = new ObjectMapper();
StringWriter writer = new StringWriter();
try {
objectMapper.writeValue(writer, data);
} catch (IOException e) {
e.printStackTrace();
}
headers.set(HttpHeaders.CONTENT_LENGTH,String.valueOf(writer.toString().getBytes(Charset.forName("UTF-8")).length));
headers.set(HttpHeaders.CONTENT_TYPE,"application/json");
HttpEntity<ResponseData> request = new HttpEntity<ResponseData>(headers);
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
try {
parts.add("requestData", objectMapper.writeValueAsString(data));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// return restTemplate.exchange(this.URL,HttpMethod.POST ,request, clazz, parts);
ListenableFuture<ResponseEntity<ResponseData>> entity = rest.exchange(this.URL,HttpMethod.POST ,request, clazz, parts);
return extractResponseEntity(entity);
}
// ...
}
Netty read data from request channelRead method
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
DefaultHttpRequest defaultHttpRequest = (DefaultHttpRequest) msg;
if (EmptyHttpHeaders.is100ContinueExpected(defaultHttpRequest)) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.CONTINUE));
}
boolean keepAlive = EmptyHttpHeaders.isKeepAlive(defaultHttpRequest);
handle = frontController.dispatchRequest(defaultHttpRequest);
}
if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg;
ByteArrayOutputStream body = new ByteArrayOutputStream(64);
ByteBuf content = httpContent.content();
if (content.isReadable()) {
//body.write(content.array());
content.readBytes(body,content.readableBytes());
//body.append(content.toString(CharsetUtil.UTF_8));
FullHttpResponse response = handle.handle(body);
if(response == null){
response = prepareDefaultResponse();
}
response.headers().set("content-type", "application/json");
response.headers().set("content-length", response.content().readableBytes());
response.headers().set("connection", HttpHeaderValues.KEEP_ALIVE);
}
if (msg instanceof LastHttpContent) {
//process request
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
The code below is working fine but I guess there is a problem with blocking io and nonblocking io. When the request is dispatched, I can not reach the HttpContent I only get HttpRequest as a msg parameter. Spring resttemplate waits for a response but Netty does not care :)
if (msg instanceof HttpRequest) {
DefaultHttpRequest defaultHttpRequest = (DefaultHttpRequest) msg;
if (EmptyHttpHeaders.is100ContinueExpected(defaultHttpRequest)) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.CONTINUE));
}
boolean keepAlive = EmptyHttpHeaders.isKeepAlive(defaultHttpRequest);
handle = frontController.dispatchRequest(defaultHttpRequest);
}
My problem is how to get response from netty server by rest template.
I have tried many ways to accomplish full req/resp.
When restTemplate request to Netty server it hangs the thread so I can not move on the distributed in memory cache implementation.
Hanging in RestTemplate.java Line : 681
Method waits forever when using Netty4ClientHttpRequestFactory.
response = request.execute();
From my understanding, you read HTTP post request that from Rest Client as HttpRequest Object lets call it first case so that means you don't even branch on the if (msg instanceof HttpContent) {} case (second one) your HTTP server just writes the default response without any content or header that you're setting in the second case. If this is the cause for the blocking on the client side you have to fill that default response just like on the second case an see what client do.
I think netty API provides this
https://netty.io/4.1/api/io/netty/handler/codec/http/DefaultFullHttpResponse.html
Also this example could give you an idea of what could be wrong server side.
http://www.seepingmatter.com/2016/03/30/a-simple-standalone-http-server-with-netty.html
how to make post call asynchronously (jersy)? I do not want to get any response from this url
public void callAPI(String url,int Id,long se_eln,String reportName String ,String startDate, String endDate){
Map map= new HashMap();
map.put("Id", Id);
map.put("reportName",reportNameString);
map.put("startDate", startDate);
map.put("endDate", endDate);
map.put("accountId", se_eln);
try {
//System.out.println("calling post method");
String str = new ObjectMapper().writeValueAsString(dataMismatchMap);
//
//PostMethod postMethod = new PostMethod(url);
PostMethod postMethod = new PostMethod(url);
RequestEntity requestEntity = new StringRequestEntity(str);
postMethod.setRequestEntity(requestEntity);
postMethod.setRequestHeader("Content-Type", "application/json;charset=utf-8");
HttpClient httpclient = new HttpClient();
int result = httpclient.executeMethod(postMethod);
//System.out.println("result is "+result);
webre
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
how to make post call asynchronously (jersy)? I do not want to get any response from this url
i think there is way to make async calls in apache http client, as i can see you already using it. at simplest you can put request call in simple thread and let let it execute. if i find on making async call through http client will update answer...
This is for get but you can modify to use post replace .rx().get(); by rx().post(...); .
rx.Observable<Response> observable = Rx.newClient(RxObservableInvoker.class)
// .target("http://javaresteasydemo-ravikant.rhcloud.com/rest/hello/getDataNoZip/")
.target("http://jerseyexample-ravikant.rhcloud.com/rest/jws/getDataAsClient")
.register(JacksonFeature.class).request().header("key", "12345").rx().get();
observable.subscribe(new Action1<Response>() {
#Override
public void call(Response response) {
try {
System.out.println(" Inside call ");
// System.out.println(response.readEntity(List.class));
//List<java.util.LinkedHashMap> lst = response.readEntity(List.class);
ObjectMapper ob = new ObjectMapper();
List<User> pojos = ob.convertValue(response.readEntity(List.class), new TypeReference<List<User>>() {
});
for (User user : pojos) {
System.out.println(user.getPost());
}
} catch (Exception e) {
e.printStackTrace();
}
System.exit(0);
}
});
in this below code i can not get result from getValue from Foo class and thats return null but that must be return value. result is correct and this function could not return that. for example this is my class:
public class JsonService {
private JSONObject json = new JSONObject();
public JsonService(final String username, final String password) throws Exception{
json.put("username", username);
json.put("password", password);
}
public class Foo implements Runnable {
private String result;
#Override
public void run() {
HttpClient client = new DefaultHttpClient();
HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
HttpResponse response;
try {
HttpPost post = new HttpPost("http://www.example.com/json_android.php");
StringEntity se = new StringEntity( json.toString());
se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
post.setEntity(se);
response = client.execute(post);
if(response!=null){
InputStream stream = response.getEntity().getContent();
result = convertToString(stream);
/*
I can Log result with below line
*/
Log.e("response is: ", result);
/*
result is {"username = ":"Hello"}
*/
}
} catch(Exception e) {
e.printStackTrace();
Log.e("Error", String.valueOf(e));
}
}
public String getValue() {
/*
return in this fucntion not working
*/
return result;
}
}
public String request() throws ExecutionException, InterruptedException {
Foo foo = new Foo();
new Thread(foo).start();
return foo.getValue();
}
How to get result correctly from Foo and return that from reauest() ? please help me. thanks
Use a FutureTask and implement a Callable.
public class Foo implements Callable<String> {
#Override
public String call() {
String result = null;
HttpClient client = new DefaultHttpClient();
HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
HttpResponse response;
try {
HttpPost post = new HttpPost("http://www.example.com/json_android.php");
StringEntity se = new StringEntity( json.toString());
se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
post.setEntity(se);
response = client.execute(post);
if(response!=null){
InputStream stream = response.getEntity().getContent();
result = convertToString(stream);
/*
I can Log result with below line
*/
Log.e("response is: ", result);
/*
result is {"username = ":"Hello"}
*/
}
} catch(Exception e) {
e.printStackTrace();
Log.e("Error", String.valueOf(e));
}
return result;
}
}
and than use it with the FutureTask
public String request() throws ExecutionException, InterruptedException {
Foo foo = new Foo();
FutureTask<String> fooFuture = new FutureTask<String>(foo);
new Thread(fooFuture).start();
return fooFuture.get();
}
the method of get from class FutureTask (at the above answer return fooFuture.get();) is a blocking function that means when you call it from UI thread it blocks the UI thread so cause your app sends you ANR error. My suggestion to solve it is using LocalBroadCastManager and when the computation finished it sends a local broadcast and in your UI you will get the result at the onReceive from the Extra that is embedded in the intent.
another solution is using ruOnUiThread or passing the handler from UI thread. and last one is using post method of widgets.
First,
I've searched a lot to find a solution but were not able to find an appropriate one.
Environment
(productive) Mongoose WebServer replies to simple GET-requests (all data are transmitted via QueryString)
Apache HttpClient (single instance!) used to make hundreds of thousands single requests sequentially.
Apache HttpClient interacting with mongoose works quite well
// after each request
getMethod.releaseConnection();
...
Problem
(Mock) implementation of WebServer with Sun HttpServer works fine with FireFox / Curl
Using Apache HttpClient as with running against productive Server, performance is horrible (~ 1 request/second) on client
Using Apache HttpClient with following code found on the net results in
vast performance gain on client
resource waste due to as many open sockets in CLOSE_WAIT state as requests processed (until no more FDs ara available!)
Code:
HttpConnectionManager mgr = httpClient.getHttpConnectionManager();
if (mgr instanceof SimpleHttpConnectionManager) {
((SimpleHttpConnectionManager)mgr).shutdown();
}
Obviously I am mising something in the http server implementation, which causes this extreme "sloweness"
Any hint/help is appreciated.
Thanks in advance!
Code
HttpServer
public static void main(String[] args) throws Exception {
//System.setProperty("sun.net.httpserver.maxIdleConnections", "10");
//System.setProperty("sun.net.httpserver.idleInterval", "2000");
HttpServer server = HttpServer.create();
server.bind(new InetSocketAddress("localhost", 11111), -1);
InetSocketAddress addr = server.getAddress();
HttpContext contextSearch = server.createContext("/search.to",
new TrufflesSearchHandler());
contextSearch.getFilters().add(new ParameterFilter());
server.setExecutor(null); // creates a default executor
server.start();
}
HttpHandler
static class SearchHandler implements HttpHandler {
private JSONParser jsonParser = new JSONParser();
public void handle(HttpExchange exchange) throws IOException {
Map<String, Object> params = (Map<String, Object>) exchange
.getAttribute("parameters");
String expectedResponse = "";
int expectedHitPlace = -1;
try {
expectedResponse = (String) params.get("expectedResponse");
expectedHitPlace = Integer.parseInt((String) params
.get("expectedHitPlace"));
} catch (Exception e) {
e.printStackTrace();
}
JSONArray resultArray = null;
try {
resultArray = (JSONArray) jsonParser.parse(new String(Base64
.decodeBase64(expectedResponse)));
fillResponseWithDummyData(resultArray, expectedHitPlace);
} catch (ParseException e) {
e.printStackTrace();
}
String response = "{ \"results\": " + resultArray + "}";
Headers headers = exchange.getResponseHeaders();
headers.add("Connection", "keep-alive");
headers.add("Content-Type", "text/plain");
headers.add("Content-length", "" + response.getBytes().length);
// headers.add("Keep-Alive", "timeout=5 max=10");
exchange.sendResponseHeaders(200, 0);
// exchange.sendResponseHeaders(200, response.getBytes().length);
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.flush();
os.close();
// exchange.close();
}
ParameterFilter
#SuppressWarnings("restriction")
public class ParameterFilter extends Filter {
#Override
public String description() {
return "Parses the requested URI for parameters";
}
#Override
public void doFilter(HttpExchange exchange, Chain chain)
throws IOException {
parseGetParameters(exchange);
parsePostParameters(exchange);
chain.doFilter(exchange);
}
private void parseGetParameters(HttpExchange exchange)
throws UnsupportedEncodingException {
Map<String, Object> parameters = new HashMap<String, Object>();
URI requestedUri = exchange.getRequestURI();
String query = requestedUri.getRawQuery();
parseQuery(query, parameters);
exchange.setAttribute("parameters", parameters);
}
private void parsePostParameters(HttpExchange exchange)
throws IOException {
if ("post".equalsIgnoreCase(exchange.getRequestMethod())) {
#SuppressWarnings("unchecked")
Map<String, Object> parameters =
(Map<String, Object>)exchange.getAttribute("parameters");
InputStreamReader isr =
new InputStreamReader(exchange.getRequestBody(),"utf-8");
BufferedReader br = new BufferedReader(isr);
String query = br.readLine();
parseQuery(query, parameters);
}
}
private void parseQuery(String query, Map<String, Object> parameters)
throws UnsupportedEncodingException {
String encoding = System.getProperty("file.encoding");
if (query != null) {
String pairs[] = query.split("[&]");
for (String pair : pairs) {
String param[] = pair.split("[=]");
String key = null;
String value = null;
if (param.length > 0) {
key = URLDecoder.decode(param[0],
encoding);
}
if (param.length > 1) {
value = URLDecoder.decode(param[1],
encoding);
}
if (parameters.containsKey(key)) {
Object obj = parameters.get(key);
if(obj instanceof List<?>) {
List<String> values = (List<String>)obj;
values.add(value);
} else if(obj instanceof String) {
List<String> values = new ArrayList<String>();
values.add((String)obj);
values.add(value);
parameters.put(key, values);
}
} else {
parameters.put(key, value);
}
}
}
}
}
I am trying to implement request retries after response isn't received.
I was reading about it in the Httpclient tutorial. the HttpRequestRetryHandler gets invoked only once and then it throw exception . what do I do wrong here?
updated
I added one if condition in the exception handling for SocketTimeoutException.
but what to do from there? how can I retry?
private void setRetry(int executionCount)
{
myRetryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(IOException exception,int executionCount,HttpContext context) {
if (executionCount >= 4) {
// Do not retry if over max retry count
System.out.println("retry count");
return false;
}
if (exception instanceof NoHttpResponseException) {
System.out.println("NoHttpResponseException exception");// Retry if the server dropped connection on us
return true;
}
if (exception instanceof SSLHandshakeException) {
// Do not retry on SSL handshake exception
System.out.println("SSLHandshakeException exception");
return false;
}
if (exception instanceof java.net.SocketTimeoutException) {
// Do not retry on SSL handshake exception
System.out.println("java.net.SocketTimeoutException exception");
return false;
}
HttpRequest request = (HttpRequest) context.getAttribute( ExecutionContext.HTTP_REQUEST);
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
if (idempotent) {
System.out.println("idempotent exception");
// Retry if the request is considered idempotent
return true;
}
return false;
}
};
}
public String postHttpReqest(int retries,
int socketTimeoutMillis,
int isSSL,
String target,
String url,
String base_url,
int port,
LinkedHashMap<String, String> lHashMapParams) throws Exception
{
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 50000);
HttpConnectionParams.setSoTimeout(httpParams, socketTimeoutMillis);
defaulthttpclient = new DefaultHttpClient(httpParams);
setRetry(retries); // here i set the handler
defaulthttpclient.setHttpRequestRetryHandler(myRetryHandler);
String line = "";
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
for (String key : lHashMapParams.keySet()) {
String val = lHashMapParams.get(key);
params.add(new BasicNameValuePair(key,val));
}
UrlEncodedFormEntity query = new UrlEncodedFormEntity(params);
HttpPost httppost = new HttpPost(url+":"+Integer.toString(port)+"//"+base_url);
httppost.setEntity(query);
HttpResponse response_ = defaulthttpclient.execute(httppost);
HttpEntity entity = response_.getEntity();
if (entity != null) {
line = EntityUtils.toString(entity);
System.out.println(line);
}
return line;
}
In the server, I set break point so it will hold the connection and will not return response.
The error I am getting in the Httpclient :
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:149)
at org.apache.http.impl.io.SocketInputBuffer.fillBuffer(SocketInputBuffer.java:110)
at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:260)
at org.apache.http.impl.conn.LoggingSessionInputBuffer.readLine(LoggingSessionInputBuffer.java:115)
at org.apache.http.impl.conn.DefaultResponseParser.parseHead(DefaultResponseParser.java:98)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:252)
at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:281)
at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:247)
at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:219)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:298)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:633)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:454)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)
at com.fts.lb.connector.ut.HttpClientImpl.postHttpReqest(HttpClientImpl.java:183)
Um .. your RetryHandler is specifically returning false for a SocketTimeoutException.
So I would expect it to throw an Exception 100% of the time, which it is.
If you want the Request to be retried when the client times out before receiving a response, then you need to have your RetyrHandler return true for SocketTimeOutExceptions. You might also want to increase your SocketTimeOut before retrying.
Googlein a little bit I found this bug in the HttpClient 4.1 client (only on Windows).
For my problem I already use the latest 4.1.1, but maybe I have to implement the retryHandler.
I hope it will be helpful.