1. Обзор
Apache HttpClient — это популярная библиотека Java, предоставляющая эффективные и многофункциональные пакеты, реализующие на стороне клиента самые последние стандарты HTTP. Библиотека предназначена для расширения, обеспечивая при этом надежную поддержку базовых методов HTTP .
В этом руководстве мы рассмотрим дизайн Apache HttpClient API. Мы объясним разницу между HttpClient
и CloseableHttpClient
. Кроме того, мы проверим, как создавать экземпляры CloseableHttpClient
с помощью HttpClients
или HttpClientBuilder
.
Наконец, мы порекомендуем, какой из упомянутых API мы должны использовать в нашем пользовательском коде. Кроме того, мы рассмотрим, какие классы API реализуют интерфейс Closeable
, что требует от нас закрытия их экземпляров для высвобождения ресурсов.
2. Дизайн API
Давайте начнем с рассмотрения того, как устроен API, сосредоточив внимание на его высокоуровневых классах и интерфейсах. На диаграмме классов ниже мы покажем часть API, необходимую для классического выполнения HTTP-запросов и обработки HTTP-ответов :
Кроме того, 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 .