Ошибка многопоточного запроса HttpClient 4.5.x: не был арендован из этого пула

Я пишу тестовый образец для httpclient apache-httpclient, чтобы проверить ошибку многопоточного запроса.

Я запустил его в цикле for и создал 2 потока в каждом цикле для запуска тестовых случаев с двумя разными настройками хостов для менеджера пула.

В 10 циклах я всегда сталкивался с 1 ошибкой: Исключение в потоке "Thread-2" java.lang.IllegalStateException: Entry [id:17][route:{}->http://***:8000][state :null] не был арендован из этого пула на org.apache.http.util.Asserts.check(Asserts.java:46)

Есть ли у моего кода какие-либо проблемы? Что я должен делать?

Образец ниже:

private void testHttpClient() {

        HttpHost proxy = new HttpHost("dev.host.com", 8001);
        final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);

        final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(20);
        cm.setDefaultMaxPerRoute(1);
        HttpHost localhost1 = new HttpHost("dev.test1.com", 8001);
        cm.setMaxPerRoute(new HttpRoute(localhost1), 2);
        HttpHost localhost2 = new HttpHost("dev.test2.com", 8000);
        cm.setMaxPerRoute(new HttpRoute(localhost2), 2);

        RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(1000)
            .setSocketTimeout(1000)
            .setConnectionRequestTimeout(1000)
            .build();
        final CloseableHttpClient httpclient1 = HttpClients.custom()
            .setConnectionManager(cm)
            .setRoutePlanner(routePlanner)
            .setDefaultRequestConfig(requestConfig)
            .build();

        RequestConfig requestConfig2 = RequestConfig.custom()
            .setConnectTimeout(1000)
            .setSocketTimeout(1000)
            .setConnectionRequestTimeout(1000)
            .build();
        final CloseableHttpClient httpclient2 = HttpClients.custom()
            .setConnectionManager(cm)
            .setRoutePlanner(routePlanner)
            .setDefaultRequestConfig(requestConfig2)
            .build();

        class HttpClientThead1 implements Runnable {
          public void run() {
            for (int i = 1; i <= 1; i++) {
              System.out.println("HttpClientThead1 Start");
              HttpClientContext context = HttpClientContext.create();
              try {
                HttpGet httpget = new HttpGet("http://dev.test1.com:8001/test/id/10001");
                CloseableHttpResponse response = httpclient1.execute(httpget, context);
                long t = System.currentTimeMillis();
                System.out.println("HttpClientThead1 " + i + ":" + response.getEntity().toString() + " " + t);
                response.close();
              } catch (ClientProtocolException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
              } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
              } finally {
                cm.closeExpiredConnections();
                cm.closeIdleConnections(30, TimeUnit.SECONDS);
              }
            }
          }
        }

    class HttpClientThead2 implements Runnable {
      public void run() {
        for (int i = 1; i <= 1; i++) {
          System.out.println("HttpClientThead2 Start");
          HttpClientContext context = HttpClientContext.create();
          try {
            HttpGet httpget2 = new HttpGet("http://dev.test2.com:8000/test/id/10002");
            CloseableHttpResponse response2 = httpclient2.execute(httpget2, context);
            long t = System.currentTimeMillis();
            System.out.println("HttpClientThead2 " + i + ":" + response2.getEntity().toString() + " " + t);
            response2.close();
          } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          } finally {
            cm.closeExpiredConnections();
            cm.closeIdleConnections(30, TimeUnit.SECONDS);
          }
        }
      }
    }

    for (int j = 1; j <= 10; j++) {
      HttpClientThead1 t1 = new HttpClientThead1();
      HttpClientThead2 t2 = new HttpClientThead2();

      Thread thread1 = new Thread(t1);
      Thread thread2 = new Thread(t2);
      thread1.start();
      thread2.start();
    }
}

person Gary Yeung    schedule 13.09.2017    source источник
comment
Я проверил это, кажется, что моя настройка хоста не работает, если я изменил cm.setDefaultMaxPerRoute(10); тогда все будет в порядке. Почему?   -  person Gary Yeung    schedule 13.09.2017


Ответы (2)


Я получил ответ, потому что я установил прокси в этом httpclient. Поэтому мне нужно также установить его в setMaxPerRoute с информацией о прокси.

Так и должно быть: cm.setMaxPerRoute(new HttpRoute(localhost2, proxy), 10);

person Gary Yeung    schedule 13.09.2017

Я полагаю, вы нашли ошибку в HttpClient; Я сообщил об этом здесь: https://issues.apache.org/jira/browse/HTTPCORE-634. Это происходит только тогда, когда вы используете пул соединений, но не используете соединения повторно.

Http-подключения повторно используются только тогда, когда весь ответ используется. Я изменил ваш код следующим образом:

(...)
            CloseableHttpResponse response2 = httpclient2.execute(httpget2, context);
            HttpEntity entity = response.getEntity();
            // consume the response
            EntityUtils.consumeQuietly(entity);
            response2.close();
(...)

и никаких сбоев с тех пор. Итак, либо потребляйте ответы, либо не используйте пул соединений, и все будет в порядке.

person Daniel Jelinski    schedule 28.06.2020