1. 程式人生 > 其它 >httpclient的兩個重要的引數maxPerRoute及MaxTotal

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 讀超時