Строгий и надежный тайм-аут на HttpClient - PullRequest
5 голосов
/ 31 января 2012

Я читаю веб-страницу, используя HttpClient, как это:

        httpclient = new DefaultHttpClient();
        httpget = new HttpGet("http://google.com");
        HttpResponse response = httpclient.execute(httpget);
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream PIS = entity.getContent();
        }  

Мне нужен тайм-аут на всю работу (подключение, ожидание и чтение - все вместе или по отдельности).
Я попытался установить параметры тайм-аута сразу после строки httpclient = new DefaultHttpClient();:

        int timeout=10;
        httpclient.getParams().setParameter("http.socket.timeout", timeout * 1000);
        httpclient.getParams().setParameter("http.connection.timeout", timeout * 1000);
        httpclient.getParams().setParameter("http.connection-manager.timeout", new Long(timeout * 1000));
        httpclient.getParams().setParameter("http.protocol.head-body-timeout", timeout * 1000);

Но это не сработало (Тайм-ауты примерно в 10 раз превышают тайм-аут, который я установил).
Поэтому я попытался потоком отменить запрос через некоторое время, используя httpget.abort() & httpclient.getConnectionManager().shutdown() сразу после строки httpget = new HttpGet("http://google.com");, например:

        (new Timer()).schedule(new java.util.TimerTask() {
            public void run() {
                httpget.abort();
                httpclient.getConnectionManager().shutdown();
            }
        },10000);

но это не имело никакого эффекта (Таймер работает; но эти две строки кода ничего не делают!) !!
Я также пытался использовать это:

URL url = new URL("http://google.com");
URLConnection con = url.openConnection();
con.setConnectTimeout(10000);
con.setReadTimeout(10000);
InputStream PIS = con.getInputStream();

но это было так же, как моя первая попытка (установка параметров тайм-аута в HttpClient) !!

в чем проблема?
Как мне решить проблему с тайм-аутом?

Спасибо

1 Ответ

8 голосов
/ 15 июня 2012

Не решение, а скорее объяснение того, что происходит.

То, что вы делаете, правильно.

Прежде всего, если вы используете Log4J, убедитесь, что вы видите все, что HttpClient хочет показать вам:

log4j.logger.org.apache.http=trace

Затем взгляните на этот класс:

http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/DefaultClientConnectionOperator.html

Этот оператор соединения поддерживает многосетевую сеть, и будет пытаться повторять попытки неудачных соединений последовательно для всех известных IP-адресов до тех пор, пока соединение не будет успешным или все известные адреса не будут отвечать. Обратите внимание, что одно и то же значение CoreConnectionPNames.CONNECTION_TIMEOUT будет использоваться для каждой попытки подключения, поэтому в худшем случае общее истекшее время до истечения времени ожидания может составлять CONNECTION_TIMEOUT * n , где n - это количество IP-адресовзаданный хост.

Это, скорее всего, происходит в вашем случае.

Кроме того, лучше использовать константы из этого интерфейса HttpConnectionParams :

SO_TIMEOUT = "http.socket.timeout"
TCP_NODELAY = "http.tcp.nodelay"
SOCKET_BUFFER_SIZE = "http.socket.buffer-size"
SO_LINGER = "http.socket.linger"
SO_REUSEADDR = "http.socket.reuseaddr"
CONNECTION_TIMEOUT = "http.connection.timeout"
STALE_CONNECTION_CHECK = "http.connection.stalecheck"
MAX_LINE_LENGTH = "http.connection.max-line-length"
MAX_HEADER_COUNT = "http.connection.max-header-count"
MIN_CHUNK_LIMIT = "http.connection.min-chunk-limit"

Вам нужно только два из них:

HttpConnectionParams.CONNECTION_TIMEOUT
HttpConnectionParams.SO_TIMEOUT

Так что лучший способ решить эту проблему - реализовать пользовательский ClientConnectionOperator.resolveHostname метод, который возвращает только один IP-адрес.

...