Я мигрирую Apache httpclient с версии 4.5 на версию 5.3, и в настоящее время мое приложение динамически выбирает из набора локальных IP-адресов.
Пример кода:
HttpGet request = new HttpGet(url);
request.setRequestConfig(RequestConfig.custom()
.setLocalAddress(someLocalInetAddress)
.build());
В httpclient 5.x функция setLocalAddress() была удалена, и мне нужен новый способ назначения локального адреса.
Я знаю, что могу создать ConnectionSocketFactory на уровне ConnectionManager, но у меня это не работает, поскольку для этого потребуется явная настройка того, когда использовать каждый IP-адрес (насколько я понимаю). У меня есть другая логика, которая определяет IP, и мне просто нужно установить его для запроса.
ОБНОВЛЯТЬ:
Я нашел «хак», который заключается в создании собственного HttpContext и передаче его следующим образом.
//create a new HttpRoute route and set its local address
final HttpContext context = HttpClientContext.create();
context.setAttribute(HTTP_ROUTE, httpRoute);
client.execute(request, context, handler);
Проблема в том, что он переопределяет весь httpRoute, а не только localAddress.





Не существует такого понятия, как локальный адрес для каждого запроса. Это не имеет никакого смысла. Локальный адрес можно определить только на уровне HTTP-маршрута.
Подкласс DefaultRoutePlanner и переопределите его метод #determineLocalAddress, чтобы предоставить собственную стратегию разрешения локальных адресов для каждого HTTP-маршрута.
try (CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(new DefaultRoutePlanner(DefaultSchemePortResolver.INSTANCE) {
@Override
protected InetAddress determineLocalAddress(final HttpHost firstHop, final HttpContext context) throws HttpException {
return (InetAddress) context.getAttribute("local-address");
}
})
.build()) {
final ClassicHttpRequest request = ClassicRequestBuilder.get()
.setHttpHost(new HttpHost("https", "httpbin.org"))
.setPath("/headers")
.build();
final HttpClientContext clientContext = HttpClientContext.create();
clientContext.setAttribute("local-address", InetAddress.getByAddress(new byte[] { 127, 0, 0, 1}));
System.out.println("Executing request " + request);
httpclient.execute(request, clientContext, response -> {
System.out.println("----------------------------------------");
System.out.println(request + "->" + new StatusLine(response));
EntityUtils.consume(response.getEntity());
return null;
});
}
Нет, не следует создавать более одного экземпляра HttpClient для каждой отдельной службы HTTP. Использование HttpContext для передачи в стратегию параметров, специфичных для контекста, на самом деле является правильным способом сделать это.
Спасибо за редактирование основного поста, теперь это имеет смысл! Я пропустил возможность добавлять собственные атрибуты и использовать их для разрешения локального адреса.
Я обновил основной пост частичным решением. Проблема с планировщиком маршрутов заключается в том, что он требует поточно-безопасной реализации, но существующая логика выбора IP-адреса очень сложна и не является поточно-ориентированной. Должен ли я создать HttpClient для каждого IP-адреса и передавать клиентов в качестве параметров метода?