當使用Apache HttpClient組件時,經常會用到它的連接池組件。典型的代碼如下:
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();connectionManager.setMaxTotal(httpConfig.getMaxPoolTotal());connectionManager.setDefaultMaxPerRoute(httpConfig.getMaxDefaultPerRoute());RequestConfig requestConfig = RequestConfig.custom() //.setConnectTimeout(httpConfig.getConnectTimeout()) //.setConnectionRequestTimeout(httpConfig.getConnectionRequestTimeout())//.setSocketTimeout(httpConfig.getSocketTimeout())//.build();HttpClientBuilder httpClientBuilder = HttpClientBuilder.create() //.setConnectionManager(connectionManager) //.setDefaultRequestConfig(requestConfig) //.useSystemProperties();HttpClient httpClient = httpClientBuilder.build();
其中maxTotal表示此連接池的最大連接數,defaultMaxPerRoute表示每路由最大連接數。這里主涉及到route在Apache HttpClient組件中是怎么使用的問題。先分析一下Apache HttpClient的源代碼。以下使用的源代碼版本4.5.14。
org.apache.http.impl.client.InternalHttpClient
?可以看到這個方法傳入的還是HttpHost,也就是請求地址,但下面就通過determineRoute方法生成了一個HttpRoute,并且后續也是使用HttpRoute。
org.apache.http.impl.conn.DefaultRoutePlanner
?可以看到默認情況下,路由中的地址就是主機地址。
org.apache.http.impl.execchain.MainClientExec
?
這里也是使用HttpRoute。
org.apache.http.impl.conn.PoolingHttpClientConnectionManager
這里又開始使用HttpHost。
org.apache.http.impl.conn.DefaultHttpClientConnectionOperator
?????? 這里會解析hostName,如果通過hostName能解析出多個IP地址,則依次使用這些IP嘗試創建socket,只要有一個能創建成功,則停止嘗試。
?????? 從上述代碼中可以看到,是按路由地址創建連接池的,如果路由地址中的主機地址可以解析成多個IP地址時,只會使用第一個可用的IP地址。
????? 一般都是通過域名訪問其它服務,而域名一般可以解析出多個IP,而域名默認情況下就是路由主機地址,只能為其中一個IP建立連接,且能創建的最大連接數就是defaultMaxPerRoute的值。這樣遠遠達不到maxTotal的值。
?????? 當然,有一種辦法是直接使用域名解析出的多個IP訪問其它服務,這樣每個IP就是一個路由,最后創建的總連接數就可以達到maxTotal的值。但這樣的最大弊端是域名解析出的IP一般是動態的,可能會不但變化,程序中寫死IP是非常不靈活的。
?????? 那有沒有兩全其美的辦法,既使用域名訪問其它服務,又使用域名解析后的IP作為路由創建連接池,使用連接數創建到最大值。辦法是有的,只要實現一個RoutePlanner就行。
public class TestRoutePlanner implements RoutePlanner {private final Random random = new Random();public HttpRoute determineRoute(HttpHost host, HttpRequest reqt, HttpContext ctx) {InetAddress[] addrs = InetAddress.getAllByName(host.getHostnName());int idx = random.mextInt(addrs.length);String hostAddr = addrs[idx]getHostAddress();HttpHost newHost = new HttpHost(hostAddr, host.getPort(), host.getSchemeName());return new HttpRoute(newHost, null, false);}
}
?然后將TestRoutePlanner添加到HttpClient中。
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create() //.setConnectionManager(connectionManager) //.setDefaultRequestConfig(requestConfig) //.setRoutePlanner(new TestRoutePlanner())//.useSystemProperties();
?????? 上述TestRoutePlanner寫的比較簡單,性能也不太好,只是不演示功能而已,實際使用時還需要進一步改造。
?
?