• 聊聊HttpComponentsHttpInvokerRequestExecutor


    本文主要研究一下HttpComponentsHttpInvokerRequestExecutor

    HttpComponentsHttpInvokerRequestExecutor

    org/springframework/remoting/httpinvoker/HttpComponentsHttpInvokerRequestExecutor.java

    public class HttpComponentsHttpInvokerRequestExecutor extends AbstractHttpInvokerRequestExecutor {
    
    	private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 100;
    
    	private static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5;
    
    	private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000);
    
    
    	private HttpClient httpClient;
    
    	@Nullable
    	private RequestConfig requestConfig;
    
    
    	/**
    	 * Create a new instance of the HttpComponentsHttpInvokerRequestExecutor with a default
    	 * {@link HttpClient} that uses a default {@code org.apache.http.impl.conn.PoolingClientConnectionManager}.
    	 */
    	public HttpComponentsHttpInvokerRequestExecutor() {
    		this(createDefaultHttpClient(), RequestConfig.custom()
    				.setSocketTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS).build());
    	}
    
    	private static HttpClient createDefaultHttpClient() {
    		Registry schemeRegistry = RegistryBuilder.create()
    				.register("http", PlainConnectionSocketFactory.getSocketFactory())
    				.register("https", SSLConnectionSocketFactory.getSocketFactory())
    				.build();
    
    		PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(schemeRegistry);
    		connectionManager.setMaxTotal(DEFAULT_MAX_TOTAL_CONNECTIONS);
    		connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);
    
    		return HttpClientBuilder.create().setConnectionManager(connectionManager).build();
    	}
    
    	//......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    HttpComponentsHttpInvokerRequestExecutor继承了AbstractHttpInvokerRequestExecutor,其构造器提供了createDefaultHttpClient方法,默认注册了http及https的socketFactory,然后创建了PoolingHttpClientConnectionManager,默认maxTotal为100,defaultMaxPerRoute为5,其requestConfig默认设置了socketTimeout为60s

    doExecuteRequest

    	protected RemoteInvocationResult doExecuteRequest(
    			HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
    			throws IOException, ClassNotFoundException {
    
    		HttpPost postMethod = createHttpPost(config);
    		setRequestBody(config, postMethod, baos);
    		try {
    			HttpResponse response = executeHttpPost(config, getHttpClient(), postMethod);
    			validateResponse(config, response);
    			InputStream responseBody = getResponseBody(config, response);
    			return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
    		}
    		finally {
    			postMethod.releaseConnection();
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    HttpComponentsHttpInvokerRequestExecutor实现了AbstractHttpInvokerRequestExecutor定义的doExecuteRequest方法,执行createHttpPost创建postMethod,然后设置requestBody,之后设置setRequestBody,接着执行executeHttpPost,验证response,读取responseBody,最后在finally里头执行postMethod.releaseConnection()

    createHttpPost

    	protected HttpPost createHttpPost(HttpInvokerClientConfiguration config) throws IOException {
    		HttpPost httpPost = new HttpPost(config.getServiceUrl());
    
    		RequestConfig requestConfig = createRequestConfig(config);
    		if (requestConfig != null) {
    			httpPost.setConfig(requestConfig);
    		}
    
    		LocaleContext localeContext = LocaleContextHolder.getLocaleContext();
    		if (localeContext != null) {
    			Locale locale = localeContext.getLocale();
    			if (locale != null) {
    				httpPost.addHeader(HTTP_HEADER_ACCEPT_LANGUAGE, locale.toLanguageTag());
    			}
    		}
    
    		if (isAcceptGzipEncoding()) {
    			httpPost.addHeader(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
    		}
    
    		return httpPost;
    	}
    
    	protected RequestConfig createRequestConfig(HttpInvokerClientConfiguration config) {
    		HttpClient client = getHttpClient();
    		if (client instanceof Configurable) {
    			RequestConfig clientRequestConfig = ((Configurable) client).getConfig();
    			return mergeRequestConfig(clientRequestConfig);
    		}
    		return this.requestConfig;
    	}
    
    	private RequestConfig mergeRequestConfig(RequestConfig defaultRequestConfig) {
    		if (this.requestConfig == null) {  // nothing to merge
    			return defaultRequestConfig;
    		}
    
    		RequestConfig.Builder builder = RequestConfig.copy(defaultRequestConfig);
    		int connectTimeout = this.requestConfig.getConnectTimeout();
    		if (connectTimeout >= 0) {
    			builder.setConnectTimeout(connectTimeout);
    		}
    		int connectionRequestTimeout = this.requestConfig.getConnectionRequestTimeout();
    		if (connectionRequestTimeout >= 0) {
    			builder.setConnectionRequestTimeout(connectionRequestTimeout);
    		}
    		int socketTimeout = this.requestConfig.getSocketTimeout();
    		if (socketTimeout >= 0) {
    			builder.setSocketTimeout(socketTimeout);
    		}
    		return builder.build();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    createHttpPost先是创建HttpPost,然后设置requestConfig,接着设置locale及gzipEncoding

    setRequestBody

    	protected void setRequestBody(
    			HttpInvokerClientConfiguration config, HttpPost httpPost, ByteArrayOutputStream baos)
    			throws IOException {
    
    		ByteArrayEntity entity = new ByteArrayEntity(baos.toByteArray());
    		entity.setContentType(getContentType());
    		httpPost.setEntity(entity);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    setRequestBody方法这里创建ByteArrayEntity,设置contentType,然后赋值给httpPost

    executeHttpPost

    	protected HttpResponse executeHttpPost(
    			HttpInvokerClientConfiguration config, HttpClient httpClient, HttpPost httpPost)
    			throws IOException {
    
    		return httpClient.execute(httpPost);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    executeHttpPost直接通过httpClient.execute方法执行post请求

    getResponseBody

    	protected InputStream getResponseBody(HttpInvokerClientConfiguration config, HttpResponse httpResponse)
    			throws IOException {
    
    		if (isGzipResponse(httpResponse)) {
    			return new GZIPInputStream(httpResponse.getEntity().getContent());
    		}
    		else {
    			return httpResponse.getEntity().getContent();
    		}
    	}
    
    	protected boolean isGzipResponse(HttpResponse httpResponse) {
    		Header encodingHeader = httpResponse.getFirstHeader(HTTP_HEADER_CONTENT_ENCODING);
    		return (encodingHeader != null && encodingHeader.getValue() != null &&
    				encodingHeader.getValue().toLowerCase().contains(ENCODING_GZIP));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    getResponseBody方法会先判断是否是gzip,是的话创建GZIPInputStream,否则直接取httpResponse.getEntity().getContent()

    小结

    HttpComponentsHttpInvokerRequestExecutor是org.springframework.remoting.httpinvoker包里头的,它继承了继承了AbstractHttpInvokerRequestExecutor,演示了httpclient的基本配置(默认maxTotal为100,defaultMaxPerRoute为5,其requestConfig默认设置了socketTimeout为60s)及其使用(创建request、设置entity、执行请求、解析response)。

  • 相关阅读:
    MMDetection(四):在自己的数据集上训练模型
    windows环境下基于3DSlicer 源代码编译搭建工程开发环境详细操作过程和中间关键错误解决方法说明
    Vue基础知识之组件机制(非单文件组件、单文件组件)(五)
    SpringCloud全系列知识(1)——初识微服务和注册中心
    RocketMQ的介绍和环境搭建
    无需开发,精臣云可轻松连接用户运营、广告推广等行业应用
    电力系统强大的Gurobi 求解器的学习(Python&Matlab)
    Python自动化测试详解
    P2320 [HNOI2006] 鬼谷子的钱袋
    回馈负载的工作原理
  • 原文地址:https://blog.csdn.net/hello_ejb3/article/details/133513235