1. 程式人生 > >HttpClient 網路傳送工具使用技巧

HttpClient 網路傳送工具使用技巧

平時專案中一般用來發送Http請求的工具可以用HttpConnectionURL工具,但是它是Java自帶的,在net包下,而且一般連線不能重用,所以用起來比較麻煩,所以apache下有一個已經封裝好的HttpClient工具包,它的每個版本變化都比較大,使用起來都不一樣,我目前使用的是4.5.3版本。

新增POM依賴
<dependency>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpclient</artifactId>
     <version>4.5.3</version>
</dependency>
程式碼示例
/**
 * 文 件 名:  HttpProxy
 * 版    權:  Quanten Technologies Co., Ltd. Copyright YYYY-YYYY,  All rights reserved
 * 描    述:  <描述>
 * 修 改 人:  zping
 * 修改時間:  2017/9/15 0015
 * 跟蹤單號:  <跟蹤單號>
 * 修改單號:  <修改單號>
 * 修改內容:  <修改內容>
 */
package com.pine.chown.http;

import org.apache.commons.codec.Charsets;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * <HTTP 傳送請求工具>
 *
 * @author Pine Chown
 * @version 2017/9/15 0015
 * @see [相關類/方法]
 * @since [產品/模組版本]
 */
public final class HttpProxy
{
	/**
	 * HTTP CLIENT 連線物件
	 */
	private static CloseableHttpClient httpClient;

	/**
	 * 請求資料型別
	 */
	private static final String APPLICATION_JSON = "application/json";

	/**
	 * 資料型別:octet-stream
	 */
	private static final String APPLICATION_OCTET_STREAM = "application/octet-stream";

	/**
	 * 預設編碼
	 */
	private static final String CONTENT_ENCODE_TYPE = "UTF-8";

	/**
	 * 知識點1:路由(MAX_PER_ROUTE)是對最大連線數(MAX_TOTAL)的細分,整個連線池的限制數量實際使用DefaultMaxPerRoute並非MaxTotal。
	 * 設定過小無法支援大併發(ConnectionPoolTimeoutException: Timeout waiting for connection from pool)
	 */
	private static final int DEFAULT_MAX_TOTAL = 512;

	/**
	 * 針對某個域名的最大連線數
	 */
	private static final int DEFAULT_MAX_PER_ROUTE = 50;

	/**
	 * 知識點2:跟目標服務建立連線超時時間,根據自己的業務調整
	 */
	private static final int DEFAULT_CONNECTION_TIMEOUT = 6000;

	/**
	 * 知識點3:請求的超時時間(建聯後,獲取response的返回等待時間)
	 */
	private static final int DEFAULT_SOCKET_TIMEOUT = 6000;

	/**
	 * 知識點4:從連線池中獲取連線的超時時間
	 */
	private static final int DEFAULT_TIMEOUT = 500;

	/**
	 * 私有構造器
	 */
	private HttpProxy ()
	{
		ConnectionConfig config = ConnectionConfig.custom ().setCharset (Charsets.UTF_8).build ();

		RequestConfig defaultRequestConfig = RequestConfig.custom ().setConnectTimeout (DEFAULT_CONNECTION_TIMEOUT)
				.setSocketTimeout (DEFAULT_SOCKET_TIMEOUT).setConnectionRequestTimeout (DEFAULT_TIMEOUT).build ();

		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager ();
		cm.setDefaultMaxPerRoute (DEFAULT_MAX_PER_ROUTE);
		cm.setDefaultConnectionConfig (config);

		httpClient = HttpClients.custom ().setMaxConnPerRoute (DEFAULT_MAX_PER_ROUTE)
				.setMaxConnTotal (DEFAULT_MAX_TOTAL)
				/*.setRetryHandler((exception, executionCount, context) -> executionCount <= 3 && (exception instanceof NoHttpResponseException
						|| exception instanceof ClientProtocolException
						|| exception instanceof SocketTimeoutException
						|| exception instanceof ConnectTimeoutException))*/.setConnectionManager (cm)
				.setDefaultRequestConfig (defaultRequestConfig).build ();
	}

	/**
	 * @param url    請求地址
	 * @param xml    xml報文
	 * @param header 請求頭
	 * @return 請求返回json
	 * @throws Exception
	 */
	public String doPost (String url, String xml, Map<String, String> header) throws Exception
	{
		CloseableHttpResponse response = null;
		HttpPost post = null;
		HttpEntity entity = null;
		try
		{
			post = new HttpPost (url);

			post.addHeader (HTTP.CONTENT_TYPE, APPLICATION_JSON);
			StringEntity se = new StringEntity (xml, Consts.UTF_8);

			if (null != header && 0 < header.size ())
			{
				Set<String> headerNames = header.keySet ();
				Iterator<String> its = headerNames.iterator ();
				while (its.hasNext ())
				{
					String heardName = its.next ();
					post.addHeader (heardName, header.get (heardName));
				}
			}

			post.setEntity (se);
			response = httpClient.execute (post);

			if (HttpStatus.SC_OK == response.getStatusLine ().getStatusCode ())
			{
				entity = response.getEntity ();
				if (null != entity)
				{
					String responseStr = EntityUtils.toString (entity, CONTENT_ENCODE_TYPE);
					return responseStr;
				}
			}
			else
			{
				return "";
			}
		}
		catch (HttpHostConnectException e)
		{
			e.printStackTrace ();
			// 釋放連線
			if (null != entity)
			{
				EntityUtils.consume (entity);
			}
		}
		catch (Exception e)
		{
			e.printStackTrace ();
			// 釋放連線
			if (null != entity)
			{
				EntityUtils.consume (entity);
			}
		}
		finally
		{
			if (null != post)
			{
				post.releaseConnection ();
			}
			// 釋放連線
			if (null != entity)
			{
				EntityUtils.consume (entity);
			}
			
			if (response != null)
			{
				try
				{
					response.close ();
				}
				catch (Exception e)
				{
					e.printStackTrace ();
				}
			}
		}
		return "";
	}

	/**
	 * @param url         請求地址
	 * @param queryString xml報文
	 * @param header      請求頭
	 * @return 請求返回json
	 * @throws Exception
	 */
	public String doGet (String url, String queryString, Map<String, String> header) throws Exception
	{
		CloseableHttpResponse response = null;
		HttpGet httpGet = null;
		HttpEntity entity = null;
		try
		{
			httpGet = new HttpGet (url);

			//httpGet.addHeader (HTTP.CONTENT_TYPE, APPLICATION_JSON);

			if (null != header && 0 < header.size ())
			{
				Set<String> headerNames = header.keySet ();
				Iterator<String> its = headerNames.iterator ();
				while (its.hasNext ())
				{
					String heardName = its.next ();
					httpGet.addHeader (heardName, header.get (heardName));
				}
			}

			response = httpClient.execute (httpGet);

			if (HttpStatus.SC_OK == response.getStatusLine ().getStatusCode ())
			{
				entity = response.getEntity ();
				if (null != entity)
				{
					String responseStr = EntityUtils.toString (entity, CONTENT_ENCODE_TYPE);
					return responseStr;
				}
			}
			else
			{
				return "";
			}
		}
		catch (HttpHostConnectException e)
		{
			e.printStackTrace ();
			// 釋放連線
			if (null != entity)
			{
				EntityUtils.consume (entity);
			}
		}
		catch (Exception e)
		{
			e.printStackTrace ();
			// 釋放連線
			if (null != entity)
			{
				EntityUtils.consume (entity);
			}
		}
		finally
		{
			if (null != httpGet)
			{
				httpGet.releaseConnection ();
			}
			// 釋放連線
			if (null != entity)
			{
				EntityUtils.consume (entity);
			}

			if (response != null)
			{
				try
				{
					response.close ();
				}
				catch (Exception e)
				{
					e.printStackTrace ();
				}
			}
		}
		return "";
	}

	/**
	 * 靜態內部類
	 */
	private static class SingletonHolder
	{
		private static final HttpProxy HTTP_PROXY = new HttpProxy ();
	}

	/**
	 * 獲取單例
	 *
	 * @return MultiThreadHttpManager
	 */
	public static final HttpProxy getInstance ()
	{
		return SingletonHolder.HTTP_PROXY;
	}
}
解釋:

由於HttpProxy是一個工具類,所以我們採用單例模式,通過考慮單例的實現方式,通過懶載入的方式,我們最終採用靜態內部類的方式實現單例,這樣的好處:

  • 系統啟動外部類初始化,此時內部類還沒有初始化
  • 內部類只有在getInstance()的時候才會被載入(實現了延遲懶載入)
  • 執行緒安全(內部類在載入初始化的時候是執行緒安全的,保證了只有一個例項物件)