博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HttpClient语法
阅读量:5817 次
发布时间:2019-06-18

本文共 5255 字,大约阅读时间需要 17 分钟。

如果我说,写技术文章会上瘾,你信么?当遇到不熟悉的技术,想办法去理解,去总结,并记录下来,它就会变成你的东西。

记得去年时的一个项目,由于对方没有接入SOA服务,只能基于httpclient进行http请求,当时使用HttpClient时,由于缺乏经验,只能照搬别人的参数模板,使用之后才发现这里的坑有多深,也许这些坑大家都可能踩过,今天就总结下使用httpclient的正确姿势,目前用的比较多的版本是3.1和4.2.3。

HttpClient参数说明

当使用HttpClient的项目上线之后,不出问题还好,一旦出了问题就很难排查,但大部分都是由于对参数不了解,随意设置导致的,下面以4.2.3为例,对参数进行说明:

  1. SO_TIMEOUT "http.socket.timeout"

    设置从socket套接字中读取数据的等待时间,在规定时间内读取不到数据,则抛出异常,一般设为2 * 1000,如果想深入了解这个参数,可以查看本地方法socketRead0的实现:

    private native int socketRead0(FileDescriptor fd, byte b[], int off, int len, int timeout)  throws IOException;
  2. TCP_NODELAY "http.tcp.nodelay"

    确定是否使用Nagle算法,什么是Nagle算法?
    Nagle算法试图最大限度地减少发送的段数,以节省带宽。
    当应用希望减少网络延迟,并提高性能,可以禁用Nagle算法,即开启TCP_NODELAY,数据将提前发送,代价是增加带宽。

  3. SOCKET_BUFFER_SIZE http.socket.buffer-size

    用来设置接收/发送HTTP消息的内部套接字缓冲区的大小。

  4. SO_LINGER http.socket.linger

    以秒为单位设置超时时间,该设置仅影响套接字关闭的情况。
    0 表示禁用该选项,-1 表示使用jre的默认值。

  5. CONNECTION_TIMEOUT http.connection.timeout

    以毫秒为单位定义请求超时时间,即在指定时间内,connection必须建立,否则就按超时处理,如果为0,则永远不会超时。

  6. MAX_LINE_LENGTH http.connection.max-line-length

    设置http请求行的最大值,如果超出则抛出IOException,0和负数表示禁止检查。

  7. MAX_HEADER_COUNThttp.connection.max-header-count

    设置http请求头的最大值,如果超出则抛出IOException,0和负数表示禁止检查。

  8. STALE_CONNECTION_CHECKhttp.connection.stalecheck

    提交请求之前检查连接上会否可用,每次检查需要30毫秒的开销,如果考虑性能问题,应该禁止该检查,默认是开启状态。

  9. SO_KEEPALIVEhttp.socket.keepalive

    是否在一定时间内自动发送一个tcp请求嗅探对方是否存活。

  10. CONN_MANAGER_TIMEOUT http.conn-manager.timeout

    当连接池里没有可用连接时,等待的超时时间,这个值一定要设置,且不能太长,不然会出现大量请求等待。

pool参数说明

HttpClient可以基于连接池管理器PoolingClientConnectionManager重复利用connection,这里有几个参数需要说明一下:

PoolingClientConnectionManager pool = new PoolingClientConnectionManager();pool.setMaxTotal(100);pool.setDefaultMaxPerRoute(50);
  1. MaxTotal:指整个连接池的最大连接数
    根据业务场景和cpu数量进行设置。
  2. DefaultMaxPerRoute:指路由的最大连接数
    有点懵逼了,什么是路由的最大连接数?通俗一点讲就是,根据连接到不同的主机对MaxTotal进行细分:
    a、如果只连接到www.ctrip.com,这时最大的并发量是50,而不是100,有兴趣的同学可以试试,当达到50时会抛出如下异常:Timeout waiting for connection from pool。
    b、如果连接了www.ctrip.com和www.taobao.com,到每个地址最大的并发是50,所以起作用的是DefaultMaxPerRoute,不过MaxTotal是最大上限。

如何使用HttpClient

如果项目的并发量不是很大,使用3.1的版本没有问题,如果并发量很大,最好升级到4.2.3,如下是4.2.3的示例代码:

public class HttpClientCase {    public static void main(String[] args) throws Exception {        HttpParams httpParams = new BasicHttpParams();        httpParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000);        httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 1000);        httpParams.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true);        httpParams.setBooleanParameter(CoreConnectionPNames.SO_KEEPALIVE, true);        httpParams.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, 5000L);        PoolingClientConnectionManager pool = new PoolingClientConnectionManager();        pool.setMaxTotal(100);        pool.setDefaultMaxPerRoute(50);        ExecutorService executorService = Executors.newFixedThreadPool(100);        int i = 1;        while(true && i < 1000) {            executorService.execute(new Task(pool, httpParams, i++));        }    }    public static class Task implements Runnable {        PoolingClientConnectionManager pool;        HttpParams httpParams;        int i;        public Task(PoolingClientConnectionManager pool, HttpParams httpParams, int i) {            this.pool = pool;            this.httpParams = httpParams;            this.i = i;        }        @Override        public void run() {            HttpResponse response = null;            try {                System.out.println(i);                HttpClient client = new DefaultHttpClient(pool, httpParams);                HttpGet httpGet = new HttpGet("http://www.baidu.com");                response = client.execute(httpGet);                // handle response            } catch (Exception e) {                e.printStackTrace();            } finally {                if (response != null) {                    try {                        EntityUtils.consume(response.getEntity());                    } catch (IOException e) {                        //e.printStackTrace();                    }                }            }        }    }}

上述代码中,利用线程池模拟大量请求,看看不同的参数设置会发生什么?

1、如果SO_TIMEOUT设置的太小,容易发生SocketTimeoutException: Read timed out异常;

 

2、如果线程池大小 > DefaultMaxPerRoute,且CONN_MANAGER_TIMEOUT设置过小,容易发生如下异常

 

这些参数都需要根据业务场景进行调优。

需要注意的是,最后必须通过 EntityUtils.consume 释放资源,不然该connection会一直被占用,导致后面的请求无法获取connection,实现如下:

public static void consume(final HttpEntity entity) throws IOException {    if (entity == null) {        return;    }    if (entity.isStreaming()) {        InputStream instream = entity.getContent();        if (instream != null) {            instream.close();        }    }}

Keep-Alive

Keep-Alive是tcp保鲜定时器,当客户端和服务端建立tcp连接之后,闲置了tcp_keepalive_time时间后(即在tcp_keepalive_time时间内双方没有数据交流),服务器内核会尝试向客户端发送侦测包,判断TCP连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到对方的ack包,则会在 tcp_keepalive_intvl时间后再次发送侦测包,一共会尝试 tcp_keepalive_probes次,每次的间隔时间在这里分别是15s、30s、45s、60s、75s。如果尝试tcp_keepalive_probes,依然没有收到对方的ack包,则会丢弃该tcp连接。

tcp连接默认闲置时间是2小时,一般设置为30分钟足够了。

注意事项

1、如果使用Keep-Alive时,response一定要设置Content-Length,否则就不算是长连接,具体原因如下:

 

2、httpclient 3.1内部采用synchronized + wait + notifyAll实现,当发生大量请求时,会造成线程饥饿;httpclient 4.2.3内部采用ReentrantLock(默认非公平) + Condition(每个线程一个)实现,这种情况几乎不会出现。

作者:占小狼
链接:http://www.jianshu.com/p/9769da1071d9
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载于:https://www.cnblogs.com/xxj-bigshow/p/7573869.html

你可能感兴趣的文章
渐变色文字
查看>>
C++ 0X 新特性实例(比较常用的) (转)
查看>>
node生成自定义命令(yargs/commander)
查看>>
各种非算法模板
查看>>
node-express项目的搭建并通过mongoose操作MongoDB实现增删改查分页排序(四)
查看>>
PIE.NET-SDK插件式二次开发文档
查看>>
如何创建Servlet
查看>>
.NET 设计规范--.NET约定、惯用法与模式-2.框架设计基础
查看>>
win7 64位+Oracle 11g 64位下使用 PL/SQL Developer 的解决办法
查看>>
BZOJ1997:[HNOI2010]PLANAR——题解
查看>>
BZOJ1014:[JSOI2008]火星人prefix——题解
查看>>
使用Unity3D引擎开发赛车游戏
查看>>
HTML5新手入门指南
查看>>
淘宝NPM镜像cnpm
查看>>
opennebula 开发记录
查看>>
ubuntu 修改hostname
查看>>
【译】UNIVERSAL IMAGE LOADER.PART 2---ImageLoaderConfiguration详解
查看>>
javascript call()
查看>>
sql 内联,左联,右联,全联
查看>>
C++关于字符串的处理
查看>>