-
Notifications
You must be signed in to change notification settings - Fork 410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Async HTTP Request Connection Timeout Not Work Correctly #610
Comments
After going through public class Main {
public static void main(String[] args) throws IOReactorException {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(1)
.setSocketTimeout(500)
.setRedirectsEnabled(true)
.build();
HttpUriRequest httpUriRequest = new HttpGet("http://255.255.33.44:8001/");
httpUriRequest.addHeader("Connection", "close");
IOReactorConfig reactorConfig = IOReactorConfig.custom()
// .setConnectTimeout(500)
// .setSoTimeout(500)
.setShutdownGracePeriod(1)
.build();
DefaultConnectingIOReactor defaultConnectingIOReactor = new DefaultConnectingIOReactor(reactorConfig);
Registry registry = RegistryBuilder.create()
.register("http", NoopIOSessionStrategy.INSTANCE)
.build();
PoolingNHttpClientConnectionManager connectionManager = new PoolingNHttpClientConnectionManager(defaultConnectingIOReactor, registry);
HttpClientContext context = new HttpClientContext();
context.setRequestConfig(requestConfig);
connectionManager.setMaxTotal(1);
CloseableHttpAsyncClient client = HttpAsyncClients.custom()
.setConnectionManager(connectionManager)
.build();
client.start();
long startTime = System.nanoTime();
client.execute(httpUriRequest, context, new FutureCallback<HttpResponse>() {
@Override
public void completed(HttpResponse httpResponse) {
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
long stopTime = System.nanoTime();
System.out.print("=====completed, timeElapsed: ");
System.out.print(TimeUnit.MILLISECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS));
System.out.println(" ms");
System.out.print(statusCode);
}
@Override
public void failed(Exception e) {
System.out.print("=====failed, timeElapsed: ");
System.out.print(TimeUnit.MILLISECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS));
System.out.println(" ms");
System.out.print("=====error msg: " + e.getMessage());
try {
connectionManager.shutdown();
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public void cancelled() {
httpUriRequest.abort();
try {
connectionManager.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
} When So it seems like code execute time is about ~1000ms, it takes at least 1 second from the start of the request to fail if the connect timeout is less than ~1000ms. But synchronous requests do not have such high latency (interval from request to failure), I guess because asynchronous requests require a lot of setup code to be processed, including the creation of connection pools, so the creation of asynchronous request clients is time-consuming expensively. |
After some research, I figure out a way to solve my problem which used Here is the main code(gist): ;; lein repl
(ns test (:require [clojure.core.async :as async]
[clj-http.client :as client]))
;; define test data
(def urls ["http://www.google.com" "http://www.google1.com" "http://255.255.33.44:8001/"])
;; use async/go to build a async http client
(defn fetch-async
[url]
(let [ret (async/promise-chan)]
(async/go
(try
(let [res (client/get url {:socket-timeout 500
:connection-timeout 500})]
(async/put! ret {:result :success :msg (:status res)}))
(catch Exception e (async/put! ret {:result :error :msg (.getMessage e)}))))
ret))
;; use async/go to build a async http client but disable apache http client retry feature
(defn fetch-async-without-retry
[url]
(let [ret (async/promise-chan)]
(async/go
(try
(let [res (client/get url {:socket-timeout 500
:connection-timeout 1000
:retry-handler (fn [ex try-count http-context]
(if (> try-count 0) false true))})]
(async/put! ret {:result :success :msg (:status res)}))
(catch Exception e (async/put! ret {:result :error :msg (.getMessage e)}))))
ret))
;; test fetach a domain url
(time (async/<!! (fetch-async "http://www.google1.com")))
;; test fetach an ip url
(time (async/<!! (fetch-async "http://255.255.33.44:8001/")))
;; block main thread to get all response data
(let [res-promise (map #(fetch-async %) urls)]
(map #(async/<!! %) res-promise))
;; metric the execute time
(let [res-promise (map #(fetch-async %) urls)]
(map #(time (async/<!! %)) res-promise))
(time (let [res-promise (map #(fetch-async %) urls)]
(map #(async/<!! %) res-promise)))
(let [res-promise (map #(fetch-async %) urls)]
(time (map #(async/<!! %) res-promise))) and the result is good.
From the above test results, we can see that after the first request takes 500ms (which is the connect-timeout we set), the other requests are executed asynchronously and the total time is almost 500ms at the end. Moreover, the async/go process is very lightweight and does not consume as many resources and execution time as a thread does. |
Note that here: (async/go
(try
(let [res (client/get url {:socket-timeout 500
:connection-timeout 1000
:retry-handler (fn [ex try-count http-context]
(if (> try-count 0) false true))})]
(async/put! ret {:result :success :msg (:status res)}))**** you're doing a blocking call (
|
Backgroud
When using clj-http async requests some do not exist URL and set the connection timeout, but the connection timeout is delayed 2-3 seconds.
Test
The test version is
3.12.2
, and the test way islein repl
, my lein config in~/.lein/profiles.clj
:and then, run
lein repl
to go to the repl environment.Example 1
Expectation
Cause the URL does not exist, so the time will be conn-timeout which is ~1ms.
Test Result
"Elapsed time: 3019.764098 msecs"
{:error "Timeout connecting to [/255.255.33.44:8001]"}
Example 2
Expectation
Cause the URL does not exist, so the time will be conn-timeout which is ~5000ms.
Test Result
"Elapsed time: 7203.527474 msecs"
{:error "Timeout connecting to [/255.255.33.44:8001]"}
Example 3
Expectation
Cause the URL does not exist, so the time will be conn-timeout which is ~5000ms.
Test Result
"Elapsed time: 5010.862797 msecs"
"caught exception: connect timed out"
Example 4
Expectation
Cause the URL does not exist, so the time will be conn-timeout which is ~1ms.
Test Result
"Elapsed time: 8.649284 msecs"
"caught exception: connect timed out"
Bug
As in the above test, Example 1 and Example 2 are test requests in
async
mode, and Example 3 and Example 4 are control requests insync
mode. Thesync
mode request is as expected, but the async mode request is 2-3 seconds longer than the set conn-timeout.The text was updated successfully, but these errors were encountered: