MultiThreadedHttpConnectionManager
在HttpClient中使用多线程的一个主要原因是可以一次执行多个方法。在执行期间,每一个方法都使用一个HttpConnection实例。由于在同一时间,多个连接只能安全地用于单一线程和方法和有限的资源,就必须确保连接分配给正确的方法。而MultiThreadedHttpConnectionManager完全可以完成这一项工作,就不必去考虑多线程带来安全的问题。
MultiThreadedHttpConnectionManager connectionManager =new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);
以上代码中的HttpClient就在多线程中执行多个方法。当再次调用httpClient.executeMethod()方法时,就会去ConnectionManager中去请求HttpConneciton的实例,就避免线程安全问题
MultThreadedHttpConnectionManager参数配置:
connectionStaleCheckingEnabled:对所有已经创建的connections都适用。除特殊情况外,此值应该设置成true
maxConnectionsPerHost:最大连接数,默认是2
maxTotalConnections:最大活动连接数,默认是20
释放连接
当连接不再使用时,一定要手动释放。原因是HttpClient不能够确定哪个方法不被使用,哪个方法还在使用。这是因为Response body不是由HttpClient来自动读取其数据的,而是由使用HttpClient的应用程序来完成的。当读取Response的数据时,必须使用此方法的连接。这样,在Response的数据在读取前,HttpClient是没有释放连接的。读取完Response数据后,应用程序使用releaseConnection()方法来释放连接。
public class HttpClientUtil { /** * 日志处理类 */ private static final Logger log = LoggerFactory.getLogger(HttpClientUtil.class); // 读取超时 private final static int SOCKET_TIMEOUT = 10000; // 连接超时 private final static int CONNECTION_TIMEOUT = 10000; // 每个HOST的最大连接数量 private final static int MAX_CONN_PRE_HOST = 20; // 连接池的最大连接数 private final static int MAX_CONN = 60; // 连接池 private final static HttpConnectionManager httpConnectionManager; static { httpConnectionManager = new MultiThreadedHttpConnectionManager(); HttpConnectionManagerParams params = httpConnectionManager.getParams(); params.setConnectionTimeout(CONNECTION_TIMEOUT); params.setSoTimeout(SOCKET_TIMEOUT); params.setDefaultMaxConnectionsPerHost(MAX_CONN_PRE_HOST); params.setMaxTotalConnections(MAX_CONN); } /** * 发送主要方法,异常捕获 * * @param httpMethod * @param encoded 编码格式UTF-8 * @return */ public static String doHttpRequest(HttpMethodBase httpMethod, String encoded) { HttpClient httpClient = new HttpClient(httpConnectionManager); resetRequestHeader(httpClient, "10.0.23.178");//伪装ip地址 BufferedReader in = null; String resultString = ""; try { httpClient.executeMethod(httpMethod); in = new BufferedReader(new InputStreamReader(httpMethod .getResponseBodyAsStream(), encoded)); StringBuffer buffer = new StringBuffer(); String line = ""; while ((line = in.readLine()) != null) { buffer.append(line); } resultString = buffer.toString(); } catch (SocketTimeoutException e) { log.error("连接超时" + e.toString()); resultString = returnError("连接超时"); } catch (HttpException e) { log.error("读取外部服务器数据失败" + e.toString()); resultString = returnError("读取外部服务器数据失败"); } catch (UnknownHostException e) { log.error("请求的主机地址无效" + e.toString()); resultString = returnError("请求的主机地址无效"); } catch (IOException e) { log.error("向外部接口发送数据失败" + e.toString()); resultString = returnError("向外部接口发送数据失败"); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } httpMethod.releaseConnection(); } return resultString; } /** * 设置一下返回错误的通用提示,可以自定义格式. * * @param reason * @return */ public static String returnError(String reason) { StringBuffer buffer = new StringBuffer(); buffer.append("<?xml version=\"1.0\" encoding=\"GBK\"?>"); buffer.append("<Response>"); buffer.append("<Success>false</Success>"); buffer.append("<reason>"); buffer.append(reason); buffer.append("</reason>"); buffer.append("</Response>"); return buffer.toString(); } /** *X-Forwarded-For:简称XFF头,它代表代表客户端,也就是HTTP的请求端真实的IP */ public final static String REQUEST_HEADER = "x-forwarded-for"; /** * 将客户IP写入请求头 * 这个设置可以伪装IP请求,注意使用 * * @param client * @param ip * @return */ public static void resetRequestHeader(HttpClient client, String ip) { List<Header> headers = new ArrayList<Header>(); headers.add(new Header(REQUEST_HEADER, ip)); client.getHostConfiguration().getParams().setParameter( "http.default-headers", headers); } }
特别注意,无论执行的方法或是否也不例外被抛出。对于每一个HttpClient.executeMethod方法必须有一个method.releaseConnection ( )来释放连接。
浙公网安备 33010602011771号