httpcomponents httpclient连接池实现与测试

在windows环境下,使用Process Explorer查看连接数和连接状态。

package http.connectionPool;

import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultClientConnectionReuseStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


public class TestConnectionPool {
    private static CloseableHttpClient httpClient;
    private static ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    private static PoolingHttpClientConnectionManager cm;
    static{
        cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(100);
        SocketConfig socketConfig = SocketConfig.custom()
                .setTcpNoDelay(true)     //是否立即发送数据,设置为true会关闭Socket缓冲,默认为false
                .setSoReuseAddress(true) //是否可以在一个进程关闭Socket后,即使它还没有释放端口,其它进程还可以立即重用端口
                .setSoTimeout(5000)       //接收数据的等待超时时间,单位ms
				//注意,此处会导致问题!!!!!!!!!!!!后文分析,会导致长时间持有连接池的锁
                .setSoLinger(60)         //关闭Socket时,要么发送完所有数据,要么等待60s后,就关闭连接,此时socket.close()是阻塞的
                .setSoKeepAlive(true)    //开启监视TCP连接是否有效
                .build();
        cm.setDefaultSocketConfig(socketConfig);
        ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                // Honor 'keep-alive' header
                HeaderElementIterator it = new BasicHeaderElementIterator(
                        response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                while (it.hasNext()) {
                    HeaderElement he = it.nextElement();
                    String param = he.getName();
                    String value = he.getValue();
                    if (value != null && param.equalsIgnoreCase("timeout")) {
                        try {
                            return Long.parseLong(value) * 1000;
                        } catch(NumberFormatException ignore) {
                        }
                    }
                }
                return 10 * 1000;
            }
        };
        httpClient = HttpClients.custom().setConnectionManager(cm)
                .setKeepAliveStrategy(myStrategy)
                .evictExpiredConnections()
                .evictIdleConnections(30,TimeUnit.SECONDS)
                .setConnectionReuseStrategy(DefaultClientConnectionReuseStrategy.INSTANCE)
                .build();
        //定时打印连接池状态
        executorService.scheduleAtFixedRate(() -> {
            System.out.println(cm.getTotalStats());
        },100L,1000L, TimeUnit.MILLISECONDS);
    }
    //测试
    public static void main(String[] args) {
        Random r = new Random();
        for(int i = 0;i<100000;i++){
            int n = r.nextInt(5);
            for(int t = 0;t<n;t++){
                new Thread(()->{
                    HttpGet get = new HttpGet("https://sales.test.cn/group/products?id=22");
                    HttpContext context = HttpClientContext.create();
                    CloseableHttpResponse response = null;
                    try{
                        response = httpClient.execute(get, context);
                        //一定有这行代码,这行触发归还连接到连接池
                        EntityUtils.consume(response.getEntity());
                    } catch (Exception e){
                        e.printStackTrace();
                    } finally {
                        if(response!=null){
                            try {
                                response.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
            try {
                Thread.sleep(200L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

使用jdk自带工具jps查看进程号,然后在Process Explorer中查看连接。 官方文档: In order to ensure proper release of system resources one must close either the content stream associated with the entity or the response itself.The difference between closing the content stream and closing the response is that the former will attempt to keep the underlying connection alive by consuming the entity content while the latter immediately shuts down and discards the connection. 大意就是为了释放系统资源,要么关闭response content stream或者关闭response,两者的区别是关闭流会尝试保持连接但是关闭response会立刻终断连接。源码中也能看到对应的操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值