httpclient 连接池测试

为什么需要使用http连接池

1、降低延迟:如果不采用连接池,每次连接发起Http请求的时候都会重新建立TCP连接(经历3次握手),用完就会关闭连接(4次挥手),如果采用连接池则减少了这部分时间损耗,别小看这几次握手,本人经过测试发现,基本上3倍的时间延迟

2、支持更大的并发:如果不采用连接池,每次连接都会打开一个端口,在大并发的情况下系统的端口资源很快就会被用完,导致无法建立新的连接

连接池实例

连接池管理器代码

import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;
import java.security.NoSuchAlgorithmException;

public class HttpConnectionManager {

    PoolingHttpClientConnectionManager cm = null;

    @PostConstruct
    public void init() {
        LayeredConnectionSocketFactory sslsf = null;
        try {
            sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }


        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
                .register("https", sslsf)
                .register("http", new PlainConnectionSocketFactory())
                .build();
        cm =new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        cm.setMaxTotal(200);
        cm.setDefaultMaxPerRoute(20);
    }

    public CloseableHttpClient getHttpClient() {
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(cm)
                .build();

        /*CloseableHttpClient httpClient = HttpClients.createDefault();//如果不采用连接池就是这种方式获取连接*/
        return httpClient;
    }
}

使用连接池代码

private static CloseableHttpClient getHttpClient(){
        HttpConnectionManager httpConnectionManager= (HttpConnectionManager) AppBeanUtil.getBean("httpConnectionManager");
        return  httpConnectionManager.getHttpClient();
    }

获取连接

public static String getFromUrl(String url, Map<String, String> params,String charset) throws Exception {
        if(StringUtil.isEmpty(charset)){
            charset=defaultCharset;
        }
        CloseableHttpClient httpclient = getHttpClient();
        URIBuilder uriBuilder = new URIBuilder(url);
        uriBuilder.setCharset(Charset.forName(charset));
        if(params!=null){
            Iterator<String> keyIt = params.keySet().iterator();
            while (keyIt.hasNext()) {
                String key = keyIt.next();
                String val = params.get(key);
                uriBuilder.setParameter(key, val);
            }
        }
        URI uri = uriBuilder.build();

        HttpGet httpget = new HttpGet(uri);

        CloseableHttpResponse response = httpclient.execute(httpget);
        try {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String content=getContent(entity.getContent());
                return content;
            }
        } finally {
            response.close();
        }
        return null;
    }

访问url 获取内容

private static String getContent(InputStream instream) throws IOException {
        StringWriter writer = new StringWriter();
        IOUtils.copy(instream, writer, defaultCharset);
        instream.close();
        return writer.toString();
    }

读取数据,这里 instream 需要进行关闭。

测试连接池和不使用连接池的效率测试。

@Test
    public void postTest() throws Exception {

        long start=System.currentTimeMillis();
        for (int i = 0; i < 200; i++) {
            String content=HttpClientPoolUtil.postFromUrl("https://www.cnblogs.com/kingszelda/p/8988505.html",new HashMap<>());
        }
        System.err.println("---------------------------postTest---------------------------");
        System.err.println(System.currentTimeMillis()-start);

        System.err.println("---------------------------postTest---------------------------");
        start=System.currentTimeMillis();
        for (int i = 0; i < 200; i++) {
            String content= HttpClientUtil.postFromUrl("https://www.cnblogs.com/kingszelda/p/8988505.html",new HashMap<>());
        }
        System.err.println(System.currentTimeMillis()-start);


    }

测试结果

---------------------------postTest---------------------------
9791
---------------------------postTest---------------------------
39427

可以看到在使用连接池访问两百次花费时间为 9.7秒,不使用连接池为 39.4秒,时间相差为30秒。

通过计算从发送请求到接收请求约花48毫秒,每建立一次连接需要花费的时间为150毫秒,可见创建连接是需要非常消耗性能的。