使用java的HttpClient实现多线程并发

作者:hebedich 时间:2022-09-19 20:35:55 

说明:以下的代码基于httpclient4.5.2实现。

我们要使用java的HttpClient实现get请求抓取网页是一件比较容易实现的工作:


 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;
 }

要多线程执行get请求时上面的方法也堪用。不过这种多线程请求是基于在每次调用get方法时创建一个HttpClient实例实现的。每个HttpClient实例使用一次即被回收。这显然不是一种最优的实现。

HttpClient提供了多线程请求方案,可以查看官方文档的《 Pooling connection manager 》这一节。HttpCLient实现多线程请求是基于内置的连接池实现的,其中有一个关键的类即PoolingHttpClientConnectionManager,这个类负责管理HttpClient连接池。在PoolingHttpClientConnectionManager中提供了两个关键的方法:setMaxTotal和setDefaultMaxPerRoute。setMaxTotal设置连接池的最大连接数,setDefaultMaxPerRoute设置每个路由上的默认连接个数。此外还有一个方法setMaxPerRoute——单独为某个站点设置最大连接个数,像这样:


  HttpHosthost = new HttpHost("locahost", 80);
  cm.setMaxPerRoute(new HttpRoute(host), 50);

根据文档稍稍调整下我们的get请求实现:


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/"));
 }
}

这样就差不多了。不过对于我自己而言,我更喜欢httpclient的fluent实现,比如我们刚才实现的http get请求完全可以这样简单的实现:


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>

并且这个fluent实现天然就是采用PoolingHttpClientConnectionManager完成的。它设置的maxTotal和defaultMaxPerRoute的值分别是200和100:


   CONNMGR = new PoolingHttpClientConnectionManager(sfr);
   CONNMGR.setDefaultMaxPerRoute(100);
   CONNMGR.setMaxTotal(200);

唯一一点让人不爽的就是Executor没有提供调整这两个值的方法。不过这也完全够用了,实在不行的话,还可以考虑重写Executor方法,然后直接使用Executor执行get请求:


Executor.newInstance().execute(Request.Get(url))
       .returnContent().asString();

就这样!

标签:java,HttpClient,多线程
0
投稿

猜你喜欢

  • c#使用file.copy实现文件备份示例

    2021-06-03 05:13:13
  • 详解C#中使用对象或集合的初始值设定项初始化的操作

    2021-12-25 20:04:52
  • C#使用有道ip地址查询接口方法实例详解

    2022-10-08 22:07:31
  • Java中\\n和\\r区别

    2023-01-16 10:38:53
  • C#创建自定义控件及添加自定义属性和事件使用实例详解

    2022-05-30 02:10:12
  • java实现将数字转换成人民币大写

    2023-08-11 05:07:29
  • java虚拟机深入学习之内存管理机制

    2023-07-03 21:15:40
  • springboot使用自定义注解实现aop切面日志

    2023-11-11 09:14:48
  • java如何将int数组转化为Integer数组

    2021-07-19 04:22:37
  • Java 从互联网上爬邮箱代码示例

    2022-02-27 16:40:57
  • SpringBoot统一功能处理实现的全过程

    2021-06-12 11:14:49
  • java判断String类型是否能转换为int的方法

    2022-08-17 23:45:52
  • SpringBoot拦截器Filter的使用方法详解

    2023-06-09 07:33:59
  • C#保存listbox中数据到文本文件的方法

    2023-02-20 10:37:27
  • Android使用Canvas对象实现刮刮乐效果

    2021-11-27 02:53:36
  • VMware虚拟机下hadoop1.x的安装方法

    2023-07-27 08:01:40
  • Spring JPA find分页示例详解

    2023-05-09 00:36:46
  • Spring深入刨析声明式事务注解的源码

    2023-10-23 09:41:48
  • mybatis中resultMap 标签的使用教程

    2022-01-15 11:19:42
  • 详解MyBatis日志如何做到兼容所有常用的日志框架

    2022-05-28 08:24:17
  • asp之家 软件编程 m.aspxhome.com