How can I reuse a HttpClient connection efficiently? - java

I am doing HTTP POSTs very frequently (>= 1/sec) to an API endpoint and I want to make sure I'm doing it efficiently. My goal is to succeed or fail as soon as possible, especially since I have separate code to retry failed POSTs. There is a nice page of HttpClient performance tips, but I'm not sure if exhaustively implementing them all will have real benefits. Here is my code right now:
public class Poster {
private String url;
// re-use our request
private HttpClient client;
// re-use our method
private PostMethod method;
public Poster(String url) {
this.url = url;
// Set up the request for reuse.
HttpClientParams clientParams = new HttpClientParams();
clientParams.setSoTimeout(1000); // 1 second timeout.
this.client = new HttpClient(clientParams);
// don't check for stale connections, since we want to be as fast as possible?
// this.client.getParams().setParameter("http.connection.stalecheck", false);
this.method = new PostMethod(this.url);
// custom RetryHandler to prevent retry attempts
HttpMethodRetryHandler myretryhandler = new HttpMethodRetryHandler() {
public boolean retryMethod(final HttpMethod method, final IOException exception, int executionCount) {
// For now, never retry
return false;
}
};
this.method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler);
}
protected boolean sendData(SensorData data) {
NameValuePair[] payload = {
// ...
};
method.setRequestBody(payload);
// Execute it and get the results.
try {
// Execute the POST method.
client.executeMethod(method);
} catch (IOException e) {
// unable to POST, deal with consequences here
method.releaseConnection();
return false;
}
// don't release so that it can be reused?
method.releaseConnection();
return method.getStatusCode() == HttpStatus.SC_OK;
}
}
Would it make sense to disable the check for stale connections? Should I be looking at using the MultiThreadedConnectionManager? Of course, actual benchmarking would help but I wanted to check if my code is on the right track first.

Much of the performance hit of http connections is establishing the socket connection. You can avoid this by using 'keep-alive' http connections. To do this, it's best to use HTTP 1.1 and make sure that "Content-Length: xx" is always set in requests and responses, "Connecction: close" is correctly set when appropriate and is properly acted upon when received.

Related

Program to make the Java client wait until receiving a response from the server

Currently, I am developing a simple WebRTC application, in which the client and server communicate with each other through RPC. Because of its blocking nature, in essence, meaning if I have a request and send it to the server, I need to wait till I receive the response. I have trouble how to make a client wait till receiving a response from the server then move on. Initially, I did some things like Thread.sleep(), or using ExecutorService and CountDownLatch, they are working, but the responsive of the program is really downgraded, and I have no idea how long the client should sleep for a response from the server. I want to use CompleableFuture in this case, but I still don't have any idea how can I use this in my context, any help is much appreciated!
Here is the client code:
#Override
public CompletableFuture<SessionDescription> join(String sid, String uid, SessionDescription offer) {
String uuid = UUID.randomUUID().toString();
JsonRpcRequestMessage rpcMsg = new JsonRpcRequestMessage();
rpcMsg.setJsonrpc("2.0");
rpcMsg.setId(uuid);
rpcMsg.setMethod("join");
Map<String, Object> params = new HashMap<>();
params.put("offer", offer);
params.put("sid", sid);
params.put("uid", uid);
rpcMsg.setParams(params);
try {
String rpcText = objectMapper.writeValueAsString(rpcMsg);
webSocket.send(rpcText); // here the client sends a text message to the server, then immediately it has to wait to receive a response from the server
// should I use Thread.sleep(n), or something?
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// here I need to return whatever server responses when I send the text above.
}
You could disable the client's view, then create a CompletableFuture by the (delayed) server-response and eventually enable the client's view again, when the CompletableFeature is done.
So your method would look similiar to the following:
#Override
public CompletableFuture<SessionDescription> join(String sid, String uid, SessionDescription offer) {
return CompletableFuture.supplyAsync(() => {
String uuid = UUID.randomUUID().toString();
JsonRpcRequestMessage rpcMsg = new JsonRpcRequestMessage();
rpcMsg.setJsonrpc("2.0");
rpcMsg.setId(uuid);
rpcMsg.setMethod("join");
Map<String, Object> params = new HashMap<>();
params.put("offer", offer);
params.put("sid", sid);
params.put("uid", uid);
rpcMsg.setParams(params);
try {
String rpcText = objectMapper.writeValueAsString(rpcMsg);
webSocket.send(rpcText);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// return the server-response here.
});
}
and you would disable the client's view before calling it and enable it when the CompletableFuture is done:
// disable the view here
join(sid, uid, offer).thenAccept((serverResponse)=>{
// enable the view here
});
This way you don't need to check for Future-Completion manually since you tell Java what to do when the Future is done.

ExternalContext redirect leads to IllegalStateException

I am facing a problem that is coming from one of our users. Somehow, the user manages to make multiple redirects with just one click. And by doing this, the redirect method is called multiple times and this leads to the IllegalStateException. I tried to first check if the response is committed and only call the redirect method if the response is not committed. And it works. Just one redirect request instead of all is being sent to the browser. But I was wondering if it's possible to send multiple redirects statements. Is it possible to create a new request with a new response after the old response was committed ?
Here is the working check for the committed redirect:
public static final boolean redirect(String targetPath) {
try {
if(!isCommitted()) {
exContext().redirect(checkAppendContextPath(targetPath));
return true;
}else{
return false;
}
} catch (IOException e) {
if (LOG.isErrorEnabled()) {
LOG.error(String.format(ERROR_REDIRECT, context().getViewRoot().getViewId(), targetPath));
}
return false;
}
}
private static final boolean isCommitted(){
if(exContext().getResponse() instanceof HttpServletResponse){
if(((HttpServletResponse) exContext().getResponse()).isCommitted()){
return true;
}else{
return false;
}
}else{
return false;
}
}
No, requests cannot be made on the server side. They must originate from the client.
I find it hard to believe that multiple requests are triggered from a single click, unless there is JavaScript involved. If JavaScript is involved, check it for bugs. Else, you could try to prevent multiple clicking at the client side. If you are using PrimeFaces and the redirect is triggered from a p:commandButton, you could try using the PrimeFaces Extensions CommandButtonSingleClickRenderer.

Vertx http server Thread has been blocked for xxxx ms, time limit is 2000

i have written a large scale http server using , but im getting this error when number of concurrent requests increases
WARNING: Thread Thread[vert.x-eventloop-thread-1,5,main] has been blocked for 8458 ms, time limit is 1000
io.vertx.core.VertxException: Thread blocked
here is my full code :
public class MyVertxServer {
public Vertx vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(100));
private HttpServer server = vertx.createHttpServer();
private Router router = Router.router(vertx);
public void bind(int port){
server.requestHandler(router::accept).listen(port);
}
public void createContext(String path,MyHttpHandler handler){
if(!path.endsWith("/")){
path += "/";
}
path+="*";
router.route(path).handler(new Handler<RoutingContext>() {
#Override
public void handle(RoutingContext ctx) {
String[] handlerID = ctx.request().uri().split(ctx.currentRoute().getPath());
String suffix = handlerID.length > 1 ? handlerID[1] : null;
handler.Handle(ctx, new VertxUtils(), suffix);
}
});
}
}
and how i call it :
ver.createContext("/getRegisterManager",new ProfilesManager.RegisterHandler());
ver.createContext("/getLoginManager", new ProfilesManager.LoginHandler());
ver.createContext("/getMapcomCreator",new ItemsManager.MapcomCreator());
ver.createContext("/getImagesManager", new ItemsManager.ImagesHandler());
ver.bind(PORT);
how ever i dont find eventbus() useful for http servers that process send/receive files , because u need to send the RoutingContext in the message with is not possible.
could you please point me to the right direction? thanks
added a little bit of handler's code:
class ProfileGetter implements MyHttpHandler{
#Override
public void Handle(RoutingContext ctx, VertxUtils utils, String suffix) {
String username = utils.Decode(ctx.request().headers().get("username"));
String lang = utils.Decode(ctx.request().headers().get("lang"));
display("profile requested : "+username);
Profile profile = ProfileManager.FindProfile(username,lang);
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
}
here ProfileManager.FindProfile(username,lang) does a long running database job on the same thread
...
basically all of my processes are happening on the main thread , because if i use executor i will get strange exceptions and nullpointers in Vertx , making me feel like the request proccessors in Vertx are parallel
Given the small amount of code in the question lets agree that the problem is on the line:
Profile profile = ProfileManager.FindProfile(username,lang);
Assuming that this is internally doing some blocking JDBC call which is a anti-pattern in Vert.x you can solve this in several ways.
Say that you can totally refactor the ProfileManager class which IMO is the best then you can update it to be reactive, so your code would be like:
ProfileManager.FindProfile(username,lang, res -> {
if (res.failed()) {
// handle error, sent 500 back, etc...
} else {
Profile profile = res.result();
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
});
Now what would be hapening behind the scenes is that your JDBC call would not block (which is tricky because JDBC is blocking by nature). So to fix this and you're lucky enough to use MySQL or Postgres then you could code your JDBC against the async-client if you're stuck with other RDBMS servers then you need to use the jdbc-client which in turn will use a thread pool to offload the work from the event loop thread.
Now say that you cannot change the ProfileManager code then you can still off load it to the thread pool by wrapping the code in a executeBlocking block:
vertx.executeBlocking(future -> {
Profile profile = ProfileManager.FindProfile(username,lang);
future.complete(profile);
}, false, res -> {
if (res.failed()) {
// handle error, sent 500 back, etc...
} else {
Profile profile = res.result();
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
});

nearest equivalent of a webpage reload in java

I am taking some data from a database via a servlet and a db handler java class and hosting it at a url. Since the database is changing I'm taking care only to host the changes rather than the entire db data.
I'm getting the required functionality by a browser i.e after every (manual) reload, I'm getting the data as required by me,
1. at the first page load, entire data gets displayed.
2. at subsequent reloads, I get either null data if there is no change in the database, or the appended rows if the database extends. (the database can only extend).
But then in a java program, I'm not getting the same functionality. The java program using HttpUrlConnection.
This is the code for the java client for servlet...
public class HTTPClient implements Runnable {
private CallbackInterface callbackinterface;
private URL url;
private HttpURLConnection http;
private InputStream response;
private String previousMessage = "";
public HTTPClient() {
try {
url = new URL("http://localhost:8080/RESTful-Server/index.jsp");
http = (HttpURLConnection) url.openConnection();
http.connect();
} catch (IOException e) {
}
}
#Override
public void run() {
while (true) {
try {
String currentmessage = "";
response = http.getInputStream();
if (http.getResponseCode() == HttpURLConnection.HTTP_OK) {
BufferedReader buffread = new BufferedReader(new InputStreamReader(response));
String line;
for (; (line = buffread.readLine()) != null;) {
currentmessage += line;
}
if ((!currentmessage.equals(previousMessage)
|| !previousMessage.equals(""))
&& !currentmessage.equals("")) {
//this.callbackinterface.event(currentmessage);\
System.out.println(currentmessage + "\t" + previousMessage);
}
previousMessage = currentmessage;
Thread.sleep(2500);
} else {
throw new IOException();
}
} catch (IOException | InterruptedException e) {
System.err.println("Exception" + e);
}
}
}
The shown class is a thread which read the connections every 2.5 s. If it gets something significant in the getline(), it will issue a callback to a worker method, which takes care of remaining things.
I am thinking the issues is because of the class variable conn, and that reload as in the browser is not getting replicated..
Any idea how to do this?
You're basically connecting (requesting) only once and trying to read the response multiple times, while it can be read only once. You basically need to create a new connection (request) everytime. You need to move the creation of the connection by url.openConnection() to inside the loop. The line http.connect() is by the way superfluous. You can safely omit it. The http.getInputStream() will already implicitly do it.
See also:
Using java.net.URLConnection to fire and handle HTTP requests

BlazeDS - AMFConnection.call giving HTTP 400 status

I'm trying to use BlazeDS's AMFConnection class to connect to pyamf, but when I call AMFConnection.call(), I get HTTP status 400 (Bad Request - "The request body was unable to be successfully decoded."). I'm more or less following this example: (pyamf.org/wiki/ClientHowTo ... sorry, I'm a new user so I guess I can't use hyperlinks. append a "http://" to those if you want to follow them)
Here's my code:
package amfconnectiontest;
import flex.messaging.io.amf.client.AMFConnection;
import flex.messaging.io.amf.client.exceptions.*;
public class Main {
public static void main(String[] args) {
AMFConnection amfConnection = new AMFConnection();
String url = "http://demo.pyamf.org/gateway/recordset";
String service = "service.getLanguages";
try
{
amfConnection.connect(url);
}
catch (ClientStatusException cse)
{
System.out.println(cse);
return;
}
// Make a remoting call and retrieve the result.
try
{
Object result = amfConnection.call(service);
System.out.println("results: " + result.toString());
}
catch (ClientStatusException cse)
{
System.out.println(cse);
}
catch (ServerStatusException sse)
{
System.out.println(sse);
}
// Close the connection.
amfConnection.close();
}
}
Any ideas?
The ability to en/decode BlazeDS specific messages (implementing ISmallMessage) has landed on the PyAMF trunk (r2726 and up). See the related ticket - http://pyamf.org/ticket/581
This version or one very similar is likely to become 0.5. If you need to connect to a BlazeDS service I would suggest checking out the trunk.

Categories

Resources