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

 更新时间:2016年09月11日 15:23:08   投稿:hebedich  
这篇文章主要介绍了使用java的HttpClient实现多线程并发的相关资料,需要的朋友可以参考下

说明:以下的代码基于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();

就这样!

相关文章

  • SpringMVC 参数绑定意义及实现过程解析

    SpringMVC 参数绑定意义及实现过程解析

    这篇文章主要介绍了SpringMVC 参数绑定意义及实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • IDEA Maven Mybatis generator 自动生成代码(实例讲解)

    IDEA Maven Mybatis generator 自动生成代码(实例讲解)

    下面小编就为大家分享一篇IDEA Maven Mybatis generator 自动生成代码的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • 关于rocketmq中日志文件路径的配置指南

    关于rocketmq中日志文件路径的配置指南

    rocketmq是java编写的,也就是可以指定启动参数,下面这篇文章主要给大家介绍了关于rocketmq中日志文件路径的配置指南,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-04-04
  • Java局部变量线程安全原理分析

    Java局部变量线程安全原理分析

    这篇文章主要介绍了Java局部变量线程安全原理分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • Java存储过程调用CallableStatement的方法

    Java存储过程调用CallableStatement的方法

    这篇文章主要介绍了Java存储过程调用CallableStatement的方法,帮助大家更好的理解和学习Java,感兴趣的朋友可以了解下
    2020-11-11
  • SpringCloud让微服务实现指定程序调用

    SpringCloud让微服务实现指定程序调用

    这篇文章主要介绍了SpringCloud让微服务实现指定程序调用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • 详解Java时区处理之Date,Calendar,TimeZone,SimpleDateFormat

    详解Java时区处理之Date,Calendar,TimeZone,SimpleDateFormat

    这篇文章主要介绍了Java时区处理之Date,Calendar,TimeZone,SimpleDateFormat的区别于用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • SpringCloud升级2020.0.x版之OpenFeign简介与使用实现思路

    SpringCloud升级2020.0.x版之OpenFeign简介与使用实现思路

    在微服务系统中,我们经常会进行 RPC 调用。在 Spring Cloud 体系中,RPC 调用一般就是 HTTP 协议的调用。对于每次调用,都要经过一系列详细步骤,接下来通过本文给大家介绍SpringCloud OpenFeign简介与使用,感兴趣的朋友一起看看吧
    2021-10-10
  • 分享一个你不知道的Java异常实现的缺陷

    分享一个你不知道的Java异常实现的缺陷

    Java中一个大家熟知的知识点就是异常捕获,try...catch...finally组合,但是很多人不知道这里面有一个关于Java的缺陷,或者说是异常实现的一点不足之处。本文就通过一个很简单的实验给大家演示下效果玩玩儿,希望大家能觉得有趣
    2022-12-12
  • Springboot配置图片虚拟映射示例代码

    Springboot配置图片虚拟映射示例代码

    这篇文章主要给大家介绍了关于Springboot配置图片虚拟映射的相关资料,文中通过实例代码介绍的非常详细,对大家学习或者使用springboot具有一定的参考学习价值,需要的朋友可以参考下
    2021-11-11

最新评论