一、HttpClient 简介
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。Java后台使用httpclient主要目的是为了模拟客户端的请求。
2、HttpClient的请求类型
实现了所有的Http请求类型,相应的类为:HttpGet、HttpPost、HttpDelete、HttpPut
3、Http的使用流程
1)导包
代码语言:javascript复制 1<dependency>
2 <groupId>org.apache.httpcomponents</groupId>
3 <artifactId>httpclient</artifactId>
4 <version>4.5.5</version>
5</dependency>
6<dependency>
7 <groupId>org.apache.httpcomponents</groupId>
8 <artifactId>httpmime</artifactId>
9 <version>4.5</version>
10</dependency>
2)创建连接所需要的配置信息
代码语言:javascript复制 1public class HttpClientConfig {
2
3 static int httpConnectTimeout = 10000;//连接超时时间(单位毫秒)
4 static int httpSocketTimeout = 10000;//socket读写超时时间(单位毫秒)
5 static int httpMaxPoolSize = 100;
6 static int httpMonitorInterval = 3000;
7 static int httpIdelTimeout = 2000;
8
9 public static int getHttpIdelTimeout() {
10 return httpIdelTimeout;
11 }
12
13 public static int getHttpSocketTimeout() {
14 return httpSocketTimeout;
15 }
16
17 public static int getHttpMaxPoolSize() {
18 return httpMaxPoolSize;
19 }
20
21 public static int getHttpMonitorInterval() {
22 return httpMonitorInterval;
23 }
24
25 public static int getHttpConnectTimeout() {
26 return httpConnectTimeout;
27 }
28}
3)封装HttpClientUtils类-包括连接池的信息
代码语言:javascript复制 1public class HttpClientUtils {
2
3 private final static Logger logger = Logger.getLogger(HttpClientUtils.class);
4 private static CloseableHttpClient httpClient;
5 private static PoolingHttpClientConnectionManager manager; // 连接池管理类
6 private static ScheduledExecutorService monitorExecutor; // 监控
7 private final static Object syncLock = new Object(); // 相当于线程锁,用于线程安全
8 private static final int CONNECT_TIMEOUT = HttpClientConfig.getHttpConnectTimeout();// 设置连接建立的超时时间为10s
9 private static final int SOCKET_TIMEOUT = HttpClientConfig.getHttpSocketTimeout();
10 private static final int MAX_CONN = HttpClientConfig.getHttpMaxPoolSize(); // 最大连接数
11 private static final int Max_PRE_ROUTE = HttpClientConfig.getHttpMaxPoolSize();
12 private static final int MAX_ROUTE = HttpClientConfig.getHttpMaxPoolSize();
13
14 /**
15 * 对http请求进行基本设置
16 *
17 * @param httpRequestBase
18 * http请求
19 */
20 private static void setRequestConfig(HttpRequestBase httpRequestBase) {
21 RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(CONNECT_TIMEOUT)
22 .setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
23 httpRequestBase.setConfig(requestConfig);
24 }
25
26 public static CloseableHttpClient getHttpClient(String url) {
27 String hostName = url.split("/")[2];
28 // System.out.println(hostName);
29 int port = 80;
30 if (hostName.contains(":")) {
31 String[] args = hostName.split(":");
32 hostName = args[0];
33 port = Integer.parseInt(args[1]);
34 }
35 if (httpClient == null) {
36 // 多线程下多个线程同时调用getHttpClient容易导致重复创建httpClient对象的问题,所以加上了同步锁
37 synchronized (syncLock) {
38 if (httpClient == null) {
39 httpClient = createHttpClient(hostName, port);
40 // 开启监控线程,对异常和空闲线程进行关闭
41 monitorExecutor = Executors.newScheduledThreadPool(1);
42 monitorExecutor.scheduleAtFixedRate(new TimerTask() {
43 @Override
44 public void run() {
45 // 关闭异常连接
46 manager.closeExpiredConnections();
47 // 关闭5s空闲的连接
48 manager.closeIdleConnections(HttpClientConfig.getHttpIdelTimeout(), TimeUnit.MILLISECONDS);
49 logger.debug("close expired and idle for over 5s connection");
50 }
51 }, HttpClientConfig.getHttpMonitorInterval(), HttpClientConfig.getHttpMonitorInterval(),
52 TimeUnit.MILLISECONDS);
53 }
54 }
55 }
56 return httpClient;
57 }
58
59 /**
60 * 根据host和port构建httpclient实例
61 *
62 * @param host
63 * 要访问的域名
64 * @param port
65 * 要访问的端口
66 * @return
67 */
68 public static CloseableHttpClient createHttpClient(String host, int port) {
69 ConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
70 LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
71 Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
72 .register("http", plainSocketFactory).register("https", sslSocketFactory).build();
73 manager = new PoolingHttpClientConnectionManager(registry);
74 // 设置连接参数
75 manager.setMaxTotal(MAX_CONN); // 最大连接数
76 manager.setDefaultMaxPerRoute(Max_PRE_ROUTE); // 路由最大连接数
77 HttpHost httpHost = new HttpHost(host, port);
78 manager.setMaxPerRoute(new HttpRoute(httpHost), MAX_ROUTE);
79 // 请求失败时,进行请求重试
80 HttpRequestRetryHandler handler = new HttpRequestRetryHandler() {
81 @Override
82 public boolean retryRequest(IOException e, int i, HttpContext httpContext) {
83 if (i > 3) {
84 // 重试超过3次,放弃请求
85 logger.error("retry has more than 3 time, give up request");
86 return false;
87 }
88 if (e instanceof NoHttpResponseException) {
89 // 服务器没有响应,可能是服务器断开了连接,应该重试
90 logger.error("receive no response from server, retry");
91 return true;
92 }
93 if (e instanceof SSLHandshakeException) {
94 // SSL握手异常
95 logger.error("SSL hand shake exception");
96 return false;
97 }
98 if (e instanceof InterruptedIOException) {
99 // 超时
100 logger.error("InterruptedIOException");
101 return false;
102 }
103 if (e instanceof UnknownHostException) {
104 // 服务器不可达
105 logger.error("server host unknown");
106 return false;
107 }
108 if (e instanceof ConnectTimeoutException) {
109 // 连接超时
110 logger.error("Connection Time out");
111 return false;
112 }
113 if (e instanceof SSLException) {
114 logger.error("SSLException");
115 return false;
116 }
117 HttpClientContext context = HttpClientContext.adapt(httpContext);
118 HttpRequest request = context.getRequest();
119 if (!(request instanceof HttpEntityEnclosingRequest)) {
120 // 如果请求不是关闭连接的请求
121 return true;
122 }
123 return false;
124 }
125 };
126 CloseableHttpClient client = HttpClients.custom().setConnectionManager(manager).setRetryHandler(handler)
127 .build();
128 return client;
129 }
130
131 /**
132 * 关闭连接池
133 */
134 public static void closeConnectionPool() {
135 try {
136 httpClient.close();
137 manager.close();
138 monitorExecutor.shutdown();
139 } catch (IOException e) {
140 e.printStackTrace();
141 }
142 }
143
144}
代码语言:javascript复制 1/**
2 * 对http请求进行基本设置
3 *
4 * @param httpRequestBase http请求
5 */
6private static void setRequestConfig(HttpRequestBase httpRequestBase) {
7 RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(CONNECT_TIMEOUT)
8 .setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
9 httpRequestBase.setConfig(requestConfig);
10}
4)form表单提交
代码语言:javascript复制 1public static String doPostForm(String url, Map<String, String> params) {
2 HttpPost httpPost = new HttpPost(url);
3 setRequestConfig(httpPost);
4 String resultString = "";
5 CloseableHttpResponse response = null;
6 try {
7
8 MultipartEntityBuilder builder = MultipartEntityBuilder.create();
9
10 if (params != null) {
11 for (String key : params.keySet()) {
12 builder.addPart(key,
13 new StringBody(params.get(key), ContentType.create("text/plain", Consts.UTF_8)));
14 }
15 }
16
17 HttpEntity reqEntity = builder.build();
18 httpPost.setEntity(reqEntity);
19
20 // 发起请求 并返回请求的响应
21 response = getHttpClient(url).execute(httpPost, HttpClientContext.create());
22 resultString = EntityUtils.toString(response.getEntity(), "utf-8");
23
24 } catch (IOException e) {
25 e.printStackTrace();
26 } catch (Exception e) {
27 e.printStackTrace();
28 } finally {
29 try {
30 if (response != null)
31 response.close();
32 } catch (IOException e) {
33 e.printStackTrace();
34 }
35 }
36 return resultString;
37}
5)File文件上传
代码语言:javascript复制 1public static String uploadFile(String url, String localFile, String fileParamName, Map<String, String> params) {
2 HttpPost httpPost = new HttpPost(url);
3 setRequestConfig(httpPost);
4 String resultString = "";
5 CloseableHttpResponse response = null;
6 try {
7 // 把文件转换成流对象FileBody
8 FileBody bin = new FileBody(new File(localFile));
9
10 MultipartEntityBuilder builder = MultipartEntityBuilder.create();
11
12 // 相当于<input type="file" name="file"/>
13 builder.addPart("files", bin);
14 // 相当于<input type="text" name="userName" value=userName>
15 builder.addPart("filesFileName",
16 new StringBody(fileParamName, ContentType.create("text/plain", Consts.UTF_8)));
17 if (params != null) {
18 for (String key : params.keySet()) {
19 builder.addPart(key,
20 new StringBody(params.get(key), ContentType.create("text/plain", Consts.UTF_8)));
21 }
22 }
23
24 HttpEntity reqEntity = builder.build();
25 httpPost.setEntity(reqEntity);
26
27 // 发起请求 并返回请求的响应
28 response = getHttpClient(url).execute(httpPost, HttpClientContext.create());
29 resultString = EntityUtils.toString(response.getEntity(), "utf-8");
30
31 } catch (IOException e) {
32 e.printStackTrace();
33 } catch (Exception e) {
34 e.printStackTrace();
35 } finally {
36 try {
37 if (response != null)
38 response.close();
39 } catch (IOException e) {
40 e.printStackTrace();
41 }
42 }
43 return resultString;
44}
6) 传输Json数据
代码语言:javascript复制 1public static String doPostJson(String url, String json) {
2 HttpPost httpPost = new HttpPost(url);
3 setRequestConfig(httpPost);
4 String resultString = "";
5 CloseableHttpResponse response = null;
6 try {
7 // 设置ContentType(注:如果只是传普通参数的话,ContentType不一定非要用application/json)
8 // httpPost.setHeader("Content-Type",
9 // "application/json;charset=utf8");
10 httpPost.setHeader("Content-Type", "application/json");
11
12 // 创建请求内容
13 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
14 httpPost.setEntity(entity);
15 // 执行http请求
16 response = getHttpClient(url).execute(httpPost, HttpClientContext.create());
17 resultString = EntityUtils.toString(response.getEntity(), "utf-8");
18 } catch (Exception e) {
19 logger.error("httpclient的get请求失败,url:" url, e);
20 // e.printStackTrace();
21 } finally {
22 try {
23 if (response != null)
24 response.close();
25 } catch (IOException e) {
26 logger.error("IOException的错误", e);
27 // e.printStackTrace();
28 }
29 }
30 return resultString;
31}