Перейти к основному содержимому

Apache HttpClient против CloseableHttpClient

· 4 мин. чтения

1. Обзор

Apache HttpClient — это популярная библиотека Java, предоставляющая эффективные и многофункциональные пакеты, реализующие на стороне клиента самые последние стандарты HTTP. Библиотека предназначена для расширения, обеспечивая при этом надежную поддержку базовых методов HTTP .

В этом руководстве мы рассмотрим дизайн Apache HttpClient API. Мы объясним разницу между HttpClient и CloseableHttpClient . Кроме того, мы проверим, как создавать экземпляры CloseableHttpClient с помощью HttpClients или HttpClientBuilder .

Наконец, мы порекомендуем, какой из упомянутых API мы должны использовать в нашем пользовательском коде. Кроме того, мы рассмотрим, какие классы API реализуют интерфейс Closeable , что требует от нас закрытия их экземпляров для высвобождения ресурсов.

2. Дизайн API

Давайте начнем с рассмотрения того, как устроен API, сосредоточив внимание на его высокоуровневых классах и интерфейсах. На диаграмме классов ниже мы покажем часть API, необходимую для классического выполнения HTTP-запросов и обработки HTTP-ответов :

./1b15af40ade736f1e054cc6cb52fb6f3.png

Кроме того, Apache HttpClient API также поддерживает асинхронный обмен HTTP-запросами и ответами, а также реактивный обмен сообщениями с использованием RxJava .

3. HttpClient против CloseableHttpClient

HttpClient — это высокоуровневый интерфейс, представляющий базовый контракт для выполнения HTTP-запроса . Он не накладывает ограничений на процесс выполнения запроса. Кроме того, он оставляет такие особенности, как управление состоянием, аутентификация и перенаправления, для отдельных клиентских реализаций.

Мы можем привести любую клиентскую реализацию к интерфейсу HttpClient . Таким образом, мы можем использовать его для выполнения базовых HTTP-запросов через реализацию клиента по умолчанию:

HttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(serviceUrl);
HttpResponse response = httpClient.execute(httpGet);
assertThat(response.getCode()).isEqualTo(HttpStatus.SC_OK);

Однако приведенный выше код приведет к проблеме блокировки в SonarQube . Причина в том, что реализация клиента по умолчанию возвращает экземпляр Closeable HttpClient , который требует закрытия.

CloseableHttpClient — это абстрактный класс, представляющий базовую реализацию интерфейса HttpClient . Однако он также реализует интерфейс Closeable . Таким образом, мы должны закрыть все его экземпляры после использования. Мы можем закрыть их, используя try-with-resources или вызвав метод close в предложении finally :

try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet httpGet = new HttpGet(serviceUrl);
HttpResponse response = httpClient.execute(httpGet);
assertThat(response.getCode()).isEqualTo(HttpStatus.SC_OK);
}

Поэтому в нашем пользовательском коде мы должны использовать класс CloseableHttpClient , а не интерфейс HttpClient .

4. HttpClients против HttpClientBuilder

В приведенных выше примерах мы использовали статический метод класса HttpClients для получения реализации клиента по умолчанию. HttpClients — это служебный класс, содержащий фабричные методы для создания экземпляров CloseableHttpClient :

CloseableHttpClient httpClient = HttpClients.createDefault();

Мы можем добиться того же, используя класс HttpClientBuilder . HttpClientBuilder — это реализация шаблона проектирования Builder для создания экземпляров CloseableHttpClient :

CloseableHttpClient httpClient = HttpClientBuilder.create().build();

Внутри HttpClients использует HttpClientBuilder для создания экземпляров реализации клиента. Поэтому мы должны предпочесть использовать HttpClients в нашем пользовательском коде. Учитывая, что это класс более высокого уровня, его внутреннее устройство может измениться в новых выпусках.

5. Управление ресурсами

Причина, по которой нам необходимо закрывать экземпляры CloseableHttpClient после того, как они выходят за пределы области действия, заключается в завершении работы связанного диспетчера соединений. Кроме того, мы также должны использовать CloseableHttpResponse , чтобы обеспечить правильное освобождение системных ресурсов .

5.1. Закрываемый HttpResponse

CloseableHttpResponse — это класс, реализующий интерфейс ClassicHttpResponse . Однако ClassicHttpResponse также расширяет интерфейсы HttpResponse , HttpEntityContainer и Closeable .

Базовое HTTP-соединение удерживается объектом ответа, что позволяет передавать содержимое ответа непосредственно из сетевого сокета . Поэтому мы должны использовать класс CloseableHttpResponse вместо интерфейса HttpResponse в нашем пользовательском коде. Нам также нужно обязательно вызвать метод close после того, как мы получим ответ:

try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpGet httpGet = new HttpGet(serviceUrl);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
}
}

Следует отметить, что базовое соединение нельзя безопасно повторно использовать, если содержимое ответа не используется полностью. В таких ситуациях соединение будет закрыто и удалено диспетчером соединений.

5.2. Повторное использование клиентов

Закрытие экземпляра CloseableHttpClient и создание нового экземпляра для каждого запроса может оказаться дорогостоящей операцией. Вместо этого мы можем повторно использовать один экземпляр CloseableHttpClient для отправки нескольких запросов :

try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpGet httpGetOne = new HttpGet(serviceOneUrl);
try (CloseableHttpResponse responseOne = httpClient.execute(httpGetOne)) {
HttpEntity entityOne = responseOne.getEntity();
EntityUtils.consume(entityOne);
assertThat(responseOne.getCode()).isEqualTo(HttpStatus.SC_OK);
}

HttpGet httpGetTwo = new HttpGet(serviceTwoUrl);
try (CloseableHttpResponse responseTwo = httpClient.execute(httpGetTwo)) {
HttpEntity entityTwo = responseTwo.getEntity();
EntityUtils.consume(entityTwo);
assertThat(responseTwo.getCode()).isEqualTo(HttpStatus.SC_OK);
}
}

В результате мы избегаем закрытия внутренне связанного диспетчера соединений и создания нового.

6. Заключение

В этой статье мы рассмотрели классический HTTP API Apache HttpClient , популярной клиентской HTTP-библиотеки для Java.

Мы узнали разницу между HttpClient и CloseableHttpClient . Кроме того, мы рекомендуем использовать CloseableHttpClient в нашем пользовательском коде. Далее мы увидели, как создавать экземпляры CloseableHttpClient с помощью HttpClients или HttpClientBuilder .

Наконец, мы рассмотрели классы CloseableHttpClient и CloseableHttpResponse , реализующие интерфейс Closeable . Мы видели, что их экземпляры должны быть закрыты, чтобы высвободить ресурсы.

Как всегда, полный исходный код доступен на GitHub .