In my web application I have a global static HttpClient that is used in many parts of the application. It is created like this:
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
HttpConnectionManagerParams params = new HttpConnectionManagerParams();
params.setConnectionTimeout( 15000 );
params.setSoTimeout( 15000 );
connectionManager.setParams(params);
httpclient = new HttpClient(connectionManager);
HttpClientParams clientParams = new HttpClientParams();
clientParams.setParameter("http.protocol.allow-circular-redirects", true);
clientParams.setParameter("http.protocol.max-redirects", 4);
httpclient.setParams(clientParams);
The timeouts are fine for most use cased, but in a specific call I would like a shorter timeout. So I have:
GetMethod get = new GetMethod(finalUrl);
get.getParams().setParameter("http.socket.timeout", new Integer(1000));
get.getParams().setParameter("http.connection.timeout", new Integer(1000));
HttpClientUtil.getShortTimeoutInstance().executeMethod(get);
It does not work. The connection timeout is still 15000. Can I set a specific connection timeout in the GetMethod without creating a new HttpClient instance (this is because I believe creating a new HttpClient instance would not be a good idea).
you might want to look at
./impl/conn/tsccm/ThreadSafeClientConnManager.java
method
ClientConnectionRequest requestConnection(final HttpRoute route,
final Object state)
Once the connection pool has been initialized for the route, the originally configured value for socket timeOut will continue to be used unless you extend/override it.
public ClientConnectionRequest requestConnection(
final HttpRoute route,
final Object state) {
final PoolEntryRequest poolRequest = pool.requestPoolEntry(
route, state);
return new ClientConnectionRequest() {
public void abortRequest() {
poolRequest.abortRequest();
}
public ManagedClientConnection getConnection(
long timeout, TimeUnit tunit) throws InterruptedException,
ConnectionPoolTimeoutException {
if (route == null) {
throw new IllegalArgumentException("Route may not be null.");
}
if (log.isDebugEnabled()) {
log.debug("Get connection: " + route + ", timeout = " + timeout);
}
BasicPoolEntry entry = poolRequest.getPoolEntry(timeout, tunit);
return new BasicPooledConnAdapter(ThreadSafeClientConnManager.this, entry);
}
};
}
Related
I"m doing a stress test on vert.x application and send ~10K RPS.
My application send an http async request from a dedicated verticle.
I"m using vert.x http client, and see that around 20 seconds my application sent the http requests successfully.
After 20 seconds i"m starting to get a lot of "Cannot assign requested address" errors.
I tried to deploy more verticles, to set different values to the http client thread pool and nothing helped to solve the issue.
I guess that the issue related to the high throughput in a short time around 1 minute.
Main Class:
public static void main(String[] args) {
final VertxOptions vertxOptions = new VertxOptions()
.setMaxEventLoopExecuteTime(1)
.setMaxEventLoopExecuteTimeUnit(TimeUnit.MILLISECONDS);
final Vertx vertx = Vertx.vertx(vertxOptions);
final Injector injector = Guice.createInjector(new Dependencies(vertx));
CustomCodecRegister.register(vertx.eventBus());
final Stream<Future<String>> deploymentFutures = Stream.of(
deployWorker(vertx, injector, StatsHttpVerticle.class, 10)
).flatMap(stream -> stream);
CompositeFuture.all(deploymentFutures.collect(Collectors.toList()))
.onSuccess(successfulCompositeFuture -> { });}
private static <T> Stream<Future<String>> deployWorker(Vertx vertx, Injector injector, Class<T> workerVerticleClass, int numVerticles) {
final String poolName = workerVerticleClass.getSimpleName()
.toLowerCase()
.replace("verticle", "-worker-pool");
final int numberOfThreads = 50;
final DeploymentOptions options = new DeploymentOptions()
.setWorker(true)
.setWorkerPoolName(poolName)
.setWorkerPoolSize(numberOfThreads);
return IntStream.range(0, numVerticles)
.mapToObj(ignore -> Future.future((Promise<String> promise) ->
vertx.deployVerticle((Verticle) injector.getInstance(workerVerticleClass), options, promise)));
}
EventBusAdapter:
public void send(Map<String, Object> queryParams, HashMap<String, String> headers, boolean followRedirect, Event eventToFire) {
StatsRequest statsRequest = new StatsRequest(queryParams, headers, eventToFire, followRedirect);
eventBus.request(FIRE_GET_METRIC_TO_STATS,statsRequest);
}
WorkerVerticle:
#Override
public void start(Promise<Void> startPromise) throws Exception {
vertx.eventBus().consumer(FIRE_GET_METRIC_TO_STATS, this::fire);
startPromise.complete();
}
private void fire(Message<StatsRequest> message) {
StatsRequest body = message.body();
MultiMap multimapHeader = MultiMap.caseInsensitiveMultiMap();
WebClientOptions webClientOptions = new WebClientOptions();
webClientOptions.setMaxPoolSize(1000);
WebClient httpClient = WebClient.create(vertx, webClientOptions);
httpClient.request(HttpMethod.GET, port, "example.com", "/1x1.gif" + "?" + "queryParamsString")
.followRedirects(false)
.putHeaders(multimapHeader)
.timeout(120000)
.send()
.onSuccess(response -> {
logger.info("All good");
})
.onFailure(err -> {
logger.error("Exception: " + err.getMessage());
});
}
How can i solve this issue?
I've come across some code that's using
Protocol.registerProtocol
in an attempt to block some TLS ciphers for a request, and retry the request with it re-enabled at times, depending on other factors.
But is Protocol.registerProtocol causing a global change - ie will other threads be affected by this?
Here's the code in question:
protected static HostConfiguration buildTLSConfig(String uri, HostConfiguration config,
boolean blockTLS1)
throws MalformedURLException
{
scheme = "https";
if (baseHttps == null)
{
baseHttps = Protocol.getProtocol(scheme);
baseFactory = baseHttps.getSocketFactory();
}
URL newUrl = new URL(uri);
defaultPort = baseHttps.getDefaultPort();
if (blockTLS1)
{
ProtocolSocketFactory customFactory =
new CustomHttpsSocketFactory(baseFactory, TLS_PREFERRED_PROTOCOLS);
Protocol applyHttps = new Protocol(scheme, customFactory, defaultPort);
Protocol.registerProtocol(scheme, applyHttps);
config.setHost(newUrl.getHost(), defaultPort, applyHttps);
}
else
{
Protocol.registerProtocol(scheme, baseHttps);
config.setHost(newUrl.getHost(), defaultPort, baseHttps);
}
return config;
}
Yes, all threads will be affected by the change.
If we look at org.apache.commons.httpclient.protocol.Protocol, we see a global protocol Map:
/** The available protocols */
private static final Map PROTOCOLS = Collections.synchronizedMap(new HashMap());
And registerProtocol() simply modifying it:
public static void registerProtocol(String id, Protocol protocol) {
// . . .
PROTOCOLS.put(id, protocol);
}
At least it's synchronized, so there won't be a race during modification.
I'm constructing my RabbitMQ (RabbitMQ 3.6.5, Erlang R16B03) connection as follows:
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(App.rabbitMQServer);
factory.setVirtualHost(App.rabbitVirtualHost);
factory.setRequestedHeartbeat(5);
factory.setConnectionTimeout(5000);
factory.setAutomaticRecoveryEnabled(true);
factory.setNetworkRecoveryInterval(5000);
Map<String, Object> clientProperties = new HashMap<>();
Map<String, Object> capabilities = new HashMap<>();
capabilities.put("connection.blocked", true);
clientProperties.put("capabilities", capabilities);
factory.setClientProperties(clientProperties);
_connection = factory.newConnection();
_channel = _connection.createChannel();
_connection.addBlockedListener(new BlockedListener() {
public void handleBlocked(String reason) throws IOException {
connectionIsBlocked = true;
log.warn("RabbitMQ connection blocked");
}
public void handleUnblocked() throws IOException {
connectionIsBlocked = false;
log.warn("RabbitMQ connection unblocked");
}
});
The connection looks ok in the management web console - the capabilities are present however the BlockedListener methods are never called. What am I missing?
Incidentally, I have another process written in Go which also never calls its flow listener...
flow := ch.NotifyFlow(make(chan bool))
go func() {
for f := range flow {
shouldFailoverToSQS = f
log.Infof("Flow status changed")
}
}()
I was confusing the BlockedListener and the FlowListener (which is deprecated).
the answer to this must be pretty simple, but I'm still unable to find it. Let's say that I have a working example of Java Asynchronous call that makes use of GET parameters:
final CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();
client.start();
URL url = new URL("http://www.myurl.com?param1=blabla");
try {
final Future<Boolean> future = client.execute(
HttpAsyncMethods.createGet(url),
new MyResponseConsumer(),
null
);
NotifierThread hilo = new NotifierThread(future);
hilo.start();
} finally {
client.close();
}
but what if I want to use POST parameters for "param1" instead of GET?. How can I achieve this?. I was unable to find any method on the HttpAsyncMethods library to do this.
Any help would be appreciated.
Regards
Well, I finally found out how this could be performed and I'm posting it here in case someone else has the same doubt (just as I suspected the answer was not so difficult :-) ):
final CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();
client.start();
URL url = new URL("http://www.myurl.com");
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> param:params.entrySet()) {
sb.append(param.getKey()+"="+param.getValue()+"&");
}
if (sb.length() > 0) {
sb = new StringBuilder(sb.substring(0, sb.length()-1));
}
try {
HttpAsyncRequestProducer prod = HttpAsyncMethods.createPost(
BASE_URL+call,
sb.toString(),
ContentType.APPLICATION_FORM_URLENCODED
);
final Future<Boolean> future = client.execute(
prod,
new MyResponseConsumer(),
null
);
NotifierThread hilo = new NotifierThread(future);
hilo.start();
} finally {
client.close();
}
Regards
I get java.net.SocketTimeoutException when searching in jira. How can I increase the timeout ?
Code:
JiraRestClientFactory restClientFactory = new AsynchronousJiraRestClientFactory();
SearchResult results = null;
try {
URI uri = new URI(jira_url);
restClient = restClientFactory.createWithBasicHttpAuthentication(uri, jira_username, jira_password);
final SearchRestClient searchClient = restClient.getSearchClient();
String jql = searchClient.getFilter(jira_filterid).get().getJql();
// setting max result to 1000 and start with 0
results = searchClient.searchJql(jql, 500, 0).claim();
System.out.println("Took: " + stopWatch.toString() + " to find " + results.getTotal() + " case in jira filter with id " + jira_filterid);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
return results;
The searching should not take so long, i think it is when doing claim().
Exception:
java.lang.RuntimeException: java.net.SocketTimeoutException
at com.google.common.base.Throwables.propagate(Throwables.java:160)
at com.atlassian.httpclient.apache.httpcomponents.DefaultHttpClient$3.apply(DefaultHttpClient.java:256)
at com.atlassian.httpclient.apache.httpcomponents.DefaultHttpClient$3.apply(DefaultHttpClient.java:249)
at com.atlassian.util.concurrent.Promises$Of$2.apply(Promises.java:276)
at com.atlassian.util.concurrent.Promises$Of$2.apply(Promises.java:272)
at com.atlassian.util.concurrent.Promises$2.onFailure(Promises.java:167)
at com.google.common.util.concurrent.Futures$4.run(Futures.java:1172)
at com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:297)
at com.google.common.util.concurrent.ExecutionList.executeListener(ExecutionList.java:156)
at com.google.common.util.concurrent.ExecutionList.execute(ExecutionList.java:145)
at com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:202)
at com.google.common.util.concurrent.SettableFuture.setException(SettableFuture.java:68)
at com.atlassian.httpclient.apache.httpcomponents.SettableFuturePromiseHttpPromiseAsyncClient$1$2.run(SettableFuturePromiseHttpPromiseAsyncClient.java:59)
at com.atlassian.httpclient.apache.httpcomponents.SettableFuturePromiseHttpPromiseAsyncClient$ThreadLocalDelegateRunnable$1.run(SettableFuturePromiseHttpPromiseAsyncClient.java:197)
at com.atlassian.httpclient.apache.httpcomponents.SettableFuturePromiseHttpPromiseAsyncClient.runInContext(SettableFuturePromiseHttpPromiseAsyncClient.java:90)
at com.atlassian.httpclient.apache.httpcomponents.SettableFuturePromiseHttpPromiseAsyncClient$ThreadLocalDelegateRunnable.run(SettableFuturePromiseHttpPromiseAsyncClient.java:192)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:680)
Caused by: java.net.SocketTimeoutException
at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.timeout(HttpAsyncRequestExecutor.java:279)
at org.apache.http.impl.nio.client.LoggingAsyncRequestExecutor.timeout(LoggingAsyncRequestExecutor.java:128)
at org.apache.http.impl.nio.DefaultHttpClientIODispatch.onTimeout(DefaultHttpClientIODispatch.java:136)
at org.apache.http.impl.nio.DefaultHttpClientIODispatch.onTimeout(DefaultHttpClientIODispatch.java:50)
at org.apache.http.impl.nio.reactor.AbstractIODispatch.timeout(AbstractIODispatch.java:169)
at org.apache.http.impl.nio.reactor.BaseIOReactor.sessionTimedOut(BaseIOReactor.java:257)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.timeoutCheck(AbstractIOReactor.java:494)
at org.apache.http.impl.nio.reactor.BaseIOReactor.validate(BaseIOReactor.java:207)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:284)
at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:106)
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:604)
... 1 more
I can not Believe i had to got so deep to change it. you can use reflection to achieve it
try (JiraRestClient client = clientFactory.createWithBasicHttpAuthentication(new URI(jira.getUrl()), jira.getUsername(), jira.getPassword())) {
try {
Field f1 = Class.forName("com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClient").getDeclaredField("httpClient");
Field f2 = Class.forName("com.atlassian.jira.rest.client.internal.async.AtlassianHttpClientDecorator").getDeclaredField("httpClient");
Field f3 = Class.forName("com.atlassian.httpclient.apache.httpcomponents.ApacheAsyncHttpClient").getDeclaredField("httpClient");
Field f4 = Class.forName("org.apache.http.impl.client.cache.CachingHttpAsyncClient").getDeclaredField("backend");
Field f5 = Class.forName("org.apache.http.impl.nio.client.InternalHttpAsyncClient").getDeclaredField("defaultConfig");
Field f6 = Class.forName("org.apache.http.client.config.RequestConfig").getDeclaredField("socketTimeout");
f1.setAccessible(true);
f2.setAccessible(true);
f3.setAccessible(true);
f4.setAccessible(true);
f5.setAccessible(true);
f6.setAccessible(true);
Object requestConfig = f5.get(f4.get(f3.get(f2.get(f1.get(client)))));
f6.setInt(requestConfig, 120 * 1000);
f1.setAccessible(false);
f2.setAccessible(false);
f3.setAccessible(false);
f4.setAccessible(false);
f5.setAccessible(false);
f6.setAccessible(false);
} catch (Exception ignore) {
}
// now you can start using it :)
} catch (URISyntaxException | IOException e) {
logger.error("invalid jira server address: " + jira.getUrl(), e);
throw new RuntimeException("can not access jira server");
}
it will buy you 120 seconds of socket time.
one workaround that seams to work is to take 100 result for each iteration and set startAt
results = searchClient.searchJql(jql, 100, 0).claim();
results1 = searchClient.searchJql(jql, 100, 100).claim();
results2 = searchClient.searchJql(jql, 100, 200).claim();
and so on.
Disclaimer: i am using the Groovy programming language, but the syntax is very similar to Java, so you should be able to reuse the code (hint: in Groovy no semi-colons are needed, the return statement is optional, instead of variable declaration i am using def or final only).
I am using the following library versions (gradle style):
compile "com.atlassian.jira:jira-rest-java-client-core:4.0.0"
compile "com.atlassian.fugue:fugue:2.2.1"
Here we have the standard rest client definition:
JiraRestClient getJiraRestClient()
{
// read user specific Jira password settings and build authentification
final inputFile = new File("${System.getProperty('user.home')}/jiraSettings.json")
final authInfo = new JsonSlurper().parseText(inputFile.text)
// setting up the jira client
def restClient = new AsynchronousJiraRestClientFactory()
.createWithBasicHttpAuthentication(
jiraServerUri,
authInfo.jiraUser.toString(),
authInfo.jiraPassword.toString())
restClient
}
I dived into the createWithBasicHttpAuthentication function and extracted and adapted the code (only getClientOptions - I set the socket timeout to 45 seconds, look at HttpClientOptions default settings):
JiraRestClient getJiraRestClient()
{
return new AsynchronousJiraRestClient(jiraServerUri, getHttpClient());
}
HttpClientOptions getClientOptions()
{
def options = new HttpClientOptions();
options.socketTimeout = 45000L;
options
}
DisposableHttpClient getHttpClient()
{
final DefaultHttpClientFactory defaultHttpClientFactory =
new DefaultHttpClientFactory(new AsynchronousHttpClientFactory.NoOpEventPublisher(),
new AsynchronousHttpClientFactory.RestClientApplicationProperties(jiraServerUri),
new ThreadLocalContextManager() {
#Override
public Object getThreadLocalContext() {
return null;
}
#Override
public void setThreadLocalContext(Object context) {}
#Override
public void clearThreadLocalContext() {}
});
final HttpClient httpClient = defaultHttpClientFactory.create(getClientOptions())
return new AtlassianHttpClientDecorator(httpClient, getAuthenticationHandler()) {
#Override
public void destroy() throws Exception {
defaultHttpClientFactory.dispose(httpClient);
}
}
}
BasicHttpAuthenticationHandler getAuthenticationHandler()
{
// read user specific Jira password settings and build authentification
final inputFile = new File("${System.getProperty('user.home')}/jiraSettings.json")
final authInfo = new JsonSlurper().parseText(inputFile.text)
return new BasicHttpAuthenticationHandler(
authInfo.jiraUser.toString(),
authInfo.jiraPassword.toString())
}
The downside is that I might be forced to adapt this code when I switch to a new version of jira-rest-java-client, but I really need this because the timout is just to short, even with heavy use of paging.
java.util.concurrent.Future class has V get(long timeout, TimeUnit unit).
Adding timeout helped me:
String jql = searchClient.getFilter(jira_filterid).get(120, TimeUnit.SECONDS).getJql();