httpclient的兩個重要的引數maxPerRoute及MaxTotal
技術標籤:Httpclient-簡介Spring-Template模版類
httpclient封裝了java中進行http網路請求的底層實現,是一個被廣泛使用的元件。
httpclient是支援池化機制的,這兩個引數就是表示池化設定的。
服務之間傳送http請求常用的有Apache的Fluent以及spring的restTemplate等
Apache的Fluent,以及spring的restTemplate都是對httpclient進行了封裝
以Apache的Fluent為例說明
其Executor類
/** * An Executor for fluent requests * <p/> * A {@link PoolingHttpClientConnectionManager} with maximum 100 connections per route and * a total maximum of 200 connections is used internally. */
最大100 connections per route 以及 最大200個 connection
CONNMGR = new PoolingHttpClientConnectionManager(sfr);
CONNMGR.setDefaultMaxPerRoute(100);
CONNMGR.setMaxTotal(200);
CLIENT = HttpClientBuilder.create().setConnectionManager(CONNMGR).build();
其程式碼中是寫死的
這兩個引數的含義是什麼呢?
下面用測試程式碼說明一下
測試端
public class HttpFluentUtil { private Logger logger = LoggerFactory.getLogger(HttpFluentUtil.class); private final static int MaxPerRoute = 2; private final static int MaxTotal = 4; final static PoolingHttpClientConnectionManager CONNMGR; final static HttpClient CLIENT; final static Executor EXECUTOR; static { LayeredConnectionSocketFactory ssl = null; try { ssl = SSLConnectionSocketFactory.getSystemSocketFactory(); } catch (final SSLInitializationException ex) { final SSLContext sslcontext; try { sslcontext = SSLContext.getInstance(SSLConnectionSocketFactory.TLS); sslcontext.init(null, null, null); ssl = new SSLConnectionSocketFactory(sslcontext); } catch (final SecurityException ignore) { } catch (final KeyManagementException ignore) { } catch (final NoSuchAlgorithmException ignore) { } } final Registry<ConnectionSocketFactory> sfr = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", ssl != null ? ssl : SSLConnectionSocketFactory.getSocketFactory()).build(); CONNMGR = new PoolingHttpClientConnectionManager(sfr); CONNMGR.setDefaultMaxPerRoute(MaxPerRoute); CONNMGR.setMaxTotal(MaxTotal); CLIENT = HttpClientBuilder.create().setConnectionManager(CONNMGR).build(); EXECUTOR = Executor.newInstance(CLIENT); } public static String Get(String uri, int connectTimeout, int socketTimeout) throws IOException { return EXECUTOR.execute(Request.Get(uri).connectTimeout(connectTimeout).socketTimeout(socketTimeout)) .returnContent().asString(); } public static String Post(String uri, StringEntity stringEntity, int connectTimeout, int socketTimeout) throws IOException { return EXECUTOR.execute(Request.Post(uri).socketTimeout(socketTimeout) .addHeader("Content-Type", "application/json").body(stringEntity)).returnContent().asString(); } public static void main(String[] args) { HttpUtil httpUtil = new HttpUtil(); String url = "http://localhost:9064/app/test"; // 服務端sleep 5秒再返回 for (int i = 0; i < 5; i++) { // MaxPerRoute若設定為2,則5執行緒分3組返回(2、2、1),共15秒 new Thread(new Runnable() { @Override public void run() { try { String result = HttpFluentUtil.Get(url, 2000, 2000); System.out.println(result); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } }
伺服器端
很簡單的springmvc
@GetMapping(value="test")
public String test() throws InterruptedException {
Thread.sleep(1000);
return "1";
}
測試1:測試端MaxPerRoute=5 MaxTotal=4
伺服器端結果
可以看到先接收4個請求,處理完成後,再接收下一次剩餘的1個請求。即其一次最多接收MaxTotal次請求
測試2:測試端MaxPerRoute=2 MaxTotal=5
伺服器端結果
可以看到接收2個請求,2個請求,1個請求,即說明maxPerRoute意思是某一個服務每次能並行接收的請求數量
什麼場景下要設定?
知道了兩個引數的含義,那麼在什麼情況下要對這兩個引數進行設定呢?
比如說下面的場景
服務1要通過Fluent呼叫服務2的介面。服務1傳送了400個請求,但由於Fluent預設只支援maxPerRoute=100,MaxTotal=200,比如介面執行時間為500ms,由於maxPerRoute=100,所以要分為100,100,100,100分四批來執行,全部執行完成需要2000ms。而如果maxPerRoute設定為400,全部執行完需要500ms。在這種情況下(提供併發能力時)就要對這兩個引數進行設定了。
設定的方法
Apache Fluent可以使用上面測試的HttpFluentUtil工具類來執行請求
RestTemplate類似使用下面的方式
@Bean
public HttpClient httpClient() {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(restTemplateProperties.getMaxTotal());
connectionManager.
setDefaultMaxPerRoute(restTemplateProperties.getDefaultMaxPerRoute());
connectionManager.
setValidateAfterInactivity(restTemplateProperties.getValidateAfterInactivity());
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(restTemplateProperties.getSocketTimeout())
.setConnectTimeout(restTemplateProperties.getConnectTimeout())
.setConnectionRequestTimeout(restTemplateProperties.getConnectionRequestTimeout()).build();
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.build();
}
@Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(httpRequestFactory());
}
其中RestTemplateProperties通過配置檔案來配置
max-total
default-max-per-route
connect-timeout 獲取連線超時
connection-request-timeout 請求超時
socket-timeout 讀超時