使用java的HttpClient實(shí)現(xiàn)多線程并發(fā)
說(shuō)明:以下的代碼基于httpclient4.5.2實(shí)現(xiàn)。
我們要使用java的HttpClient實(shí)現(xiàn)get請(qǐng)求抓取網(wǎng)頁(yè)是一件比較容易實(shí)現(xiàn)的工作:
public static String get(String url) {
CloseableHttpResponseresponse = null;
BufferedReader in = null;
String result = "";
try {
CloseableHttpClienthttpclient = HttpClients.createDefault();
HttpGethttpGet = new HttpGet(url);
response = httpclient.execute(httpGet);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffersb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
result = sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != response) response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
要多線程執(zhí)行g(shù)et請(qǐng)求時(shí)上面的方法也堪用。不過(guò)這種多線程請(qǐng)求是基于在每次調(diào)用get方法時(shí)創(chuàng)建一個(gè)HttpClient實(shí)例實(shí)現(xiàn)的。每個(gè)HttpClient實(shí)例使用一次即被回收。這顯然不是一種最優(yōu)的實(shí)現(xiàn)。
HttpClient提供了多線程請(qǐng)求方案,可以查看官方文檔的《 Pooling connection manager 》這一節(jié)。HttpCLient實(shí)現(xiàn)多線程請(qǐng)求是基于內(nèi)置的連接池實(shí)現(xiàn)的,其中有一個(gè)關(guān)鍵的類(lèi)即PoolingHttpClientConnectionManager,這個(gè)類(lèi)負(fù)責(zé)管理HttpClient連接池。在PoolingHttpClientConnectionManager中提供了兩個(gè)關(guān)鍵的方法:setMaxTotal和setDefaultMaxPerRoute。setMaxTotal設(shè)置連接池的最大連接數(shù),setDefaultMaxPerRoute設(shè)置每個(gè)路由上的默認(rèn)連接個(gè)數(shù)。此外還有一個(gè)方法setMaxPerRoute——單獨(dú)為某個(gè)站點(diǎn)設(shè)置最大連接個(gè)數(shù),像這樣:
HttpHosthost = new HttpHost("locahost", 80);
cm.setMaxPerRoute(new HttpRoute(host), 50);
根據(jù)文檔稍稍調(diào)整下我們的get請(qǐng)求實(shí)現(xiàn):
package com.zhyea.robin;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class HttpUtil {
private static CloseableHttpClienthttpClient;
static {
PoolingHttpClientConnectionManagercm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(20);
cm.setDefaultMaxPerRoute(50);
httpClient = HttpClients.custom().setConnectionManager(cm).build();
}
public static String get(String url) {
CloseableHttpResponseresponse = null;
BufferedReaderin = null;
String result = "";
try {
HttpGethttpGet = new HttpGet(url);
response = httpClient.execute(httpGet);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffersb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
result = sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != response) response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static void main(String[] args) {
System.out.println(get("https://www.baidu.com/"));
}
}
這樣就差不多了。不過(guò)對(duì)于我自己而言,我更喜歡httpclient的fluent實(shí)現(xiàn),比如我們剛才實(shí)現(xiàn)的http get請(qǐng)求完全可以這樣簡(jiǎn)單的實(shí)現(xiàn):
package com.zhyea.robin;
import org.apache.http.client.fluent.Request;
import java.io.IOException;
public class HttpUtil {
public static String get(String url) {
String result = "";
try {
result = Request.Get(url)
.connectTimeout(1000)
.socketTimeout(1000)
.execute().returnContent().asString();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
System.out.println(get("https://www.baidu.com/"));
}
}
我們要做的只是將以前的httpclient依賴替換為fluent-hc依賴:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>fluent-hc</artifactId> <version>4.5.2</version> </dependency>
并且這個(gè)fluent實(shí)現(xiàn)天然就是采用PoolingHttpClientConnectionManager完成的。它設(shè)置的maxTotal和defaultMaxPerRoute的值分別是200和100:
CONNMGR = new PoolingHttpClientConnectionManager(sfr);
CONNMGR.setDefaultMaxPerRoute(100);
CONNMGR.setMaxTotal(200);
唯一一點(diǎn)讓人不爽的就是Executor沒(méi)有提供調(diào)整這兩個(gè)值的方法。不過(guò)這也完全夠用了,實(shí)在不行的話,還可以考慮重寫(xiě)Executor方法,然后直接使用Executor執(zhí)行g(shù)et請(qǐng)求:
Executor.newInstance().execute(Request.Get(url))
.returnContent().asString();
就這樣!
相關(guān)文章
關(guān)于log4j日志擴(kuò)展---自定義PatternLayout
這篇文章主要介紹了關(guān)于log4j日志擴(kuò)展---自定義PatternLayout,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
Java多線程的實(shí)現(xiàn)方式比較(兩種方式比較)
Java多線程實(shí)現(xiàn)方式有兩種,第一種是繼承Thread類(lèi),第二種是實(shí)現(xiàn)Runnable接口,兩種有很多差異,下面跟著本文一起學(xué)習(xí)吧2015-11-11
springboot整合sa-token中的redis報(bào)netty錯(cuò)誤問(wèn)題
整合Spring Boot與sa-token-redis-jackson時(shí)遇到Netty版本沖突,通過(guò)將netty-common升級(jí)到與sa-token-redis-jackson兼容的版本4.1.79解決2024-11-11
關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案
這篇文章主要介紹了關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
SpringBoot自動(dòng)裝配之@Enable深入講解
這篇文章主要介紹了SpringBoot自動(dòng)裝配之@Enable,SpringBoot中提供了很多Enable開(kāi)頭的注解,這些注解都是用于動(dòng)態(tài)啟用某些功能的。而其底層原理是使用@Import注?解導(dǎo)入一些配置類(lèi),實(shí)現(xiàn)Bean的動(dòng)態(tài)加載2023-01-01
Spring Boot 中的 CommandLineRunner 原理及使用示例
CommandLineRunner 是 Spring Boot 提供的一個(gè)非常有用的接口,可以幫助你在應(yīng)用程序啟動(dòng)后執(zhí)行初始化任務(wù),本文通過(guò)多個(gè)示例詳細(xì)介紹了如何在實(shí)際項(xiàng)目中使用 CommandLineRunner,感興趣的朋友一起看看吧2025-04-04

