Apache HttpClient 4.5 长连接池及 Fluent API 介绍

本文介绍了Apache HttpClient4.5长连接池的实现方式,并对比了传统写法与FluentAPI风格的代码。FluentAPI简化了HTTP请求的发起过程,使得代码更为简洁易读。

Apache HttpClient 4.5 长连接池及 Fluent API 介绍

最近的项目中用到了 Apache HttpComponents 项目中 HttpClient 4.5 长连接池的功能,再研究官方标准写法的同时,突然看到了Apache HttpClient Fluent API,相比之前 HttpClient 的传统书写方式,Fluent API 方便了很多。

在介绍之前先准备一下实例代码中用到的工具类,工具类中添加了 HttpClient 连接的创建方法和构建表单方式的 Http 实体内容的方法:

package cn.cokolin;

import org.apache.commons.collections.MapUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Http 连接池帮助类
 *
 * @author chunlin.qiu
 * @see org.apache.http.client.fluent.Request
 * @see org.apache.http.client.fluent.Executor
 */
public class HttpPoolHelper {

    public static CloseableHttpClient buildPoolHttpClient(int liveSeconds, int maxPerRoute, int maxTotal) {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(liveSeconds, TimeUnit.SECONDS);
        cm.setValidateAfterInactivity(2_000);// 每隔 2 秒才检查连接的有效性
        cm.setDefaultMaxPerRoute(maxPerRoute);// 默认最大预处理路由,如果只有一个路由,可以等于 maxTotal
        cm.setMaxTotal(maxTotal);//连接池最大值
        return HttpClients.custom().setConnectionManager(cm).build();
    }

    public static UrlEncodedFormEntity buildFormEntity(Map<String, String> params) {
        return new UrlEncodedFormEntity(paramToPair(params), StandardCharsets.UTF_8);
    }

    /**
     * @see org.apache.http.message.BasicNameValuePair
     */
    public static List<NameValuePair> paramToPair(Map<String, String> params) {
        List<NameValuePair> result = Collections.emptyList();
        if (MapUtils.isNotEmpty(params)) {
            result = new ArrayList<>(params.size());
            for (Map.Entry<String, String> entry : params.entrySet()) {
                String value = entry.getValue();
                if (value != null) {
                    String key = entry.getKey();
                    result.add(new BasicNameValuePair(key, value));
                }
            }
        }
        return result;
    }

}

如果没有 HTTP 代理、自定义 HTTP 头、自定义 HTTP Cookies、HTTP 访问验证这些东西的话,HttpClient 的长连接的实现是非常简便的。HttpClient 的代码普遍使用了类似工厂模式的设计模式,代码可读性不错。下面就讲讲 Fluent API 的用法。

先提供一个没有 Fluent API 的 Http Post 的写法:

    public static String httpPost(String uri, Map<String, String> params, int timeoutMills) throws IOException {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(uri);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(timeoutMills) // 比 Fluent API 可以多配置一个 timeout
                    .setConnectTimeout(timeoutMills)
                    .setSocketTimeout(timeoutMills)
                    .build();
            httpPost.setConfig(requestConfig);
            httpPost.setEntity(HttpPoolHelper.buildFormEntity(params));
            try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
                return EntityUtils.toString(response.getEntity());
            }
        }
    }

多亏了 Java 1.7 的 try-with-resource 语法,让这段代码可以也可以写得非常短,但里面嵌套了两层的 try 块。
另外来看看 Fluent API 的链式写法:

    public static String fluentPost(String uri, Map<String, String> params, int timeoutMills) throws IOException {
        return Request.Post(uri) //
                .socketTimeout(timeoutMills) //
                .connectTimeout(timeoutMills) //
                .body(HttpPoolHelper.buildFormEntity(params)) //
                .execute().returnContent().asString(StandardCharsets.UTF_8);
    }

使用 Fluent API 后实际可以一行代码搞定这个 HttpPost 请求。


另外来段非 Fluent API 下 Http 连接池的 POST 写法:

    private static CloseableHttpClient httpClient;

    static {
        httpClient = HttpPoolHelper.buildPoolHttpClient(600, 100, 100);
    }

    public static String httpPoolPost(String uri, Map<String, String> params, int timeoutMills) throws IOException {
        HttpPost httpPost = new HttpPost(uri);
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(timeoutMills)
                .setSocketTimeout(timeoutMills)
                .build();
        httpPost.setConfig(requestConfig);
        httpPost.setEntity(HttpPoolHelper.buildFormEntity(params));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            return EntityUtils.toString(response.getEntity());
        }
    }

Fluent API 下 Http 连接池的POST 写法:

private static Executor executor;

    static {
        executor = Executor.newInstance(httpClient);
    }

    public static String fluentPoolPost(String uri, Map<String, String> params, int timeoutMills) throws IOException {
        Request body = Request.Post(uri)
                .socketTimeout(timeoutMills)
                .connectTimeout(timeoutMills)
                .body(HttpPoolHelper.buildFormEntity(params));
        return executor.execute(body).returnContent().asString(StandardCharsets.UTF_8);
    }

Fluent API 下也是两步搞定,不过这一块跟非连接池下的代码很类似,这也说明了 HttpClient 的封装非常不错。

总结,本文主要是为了备注一下 HttpClient 长连接的写法和 Fluent API 的用途,见解非常微浅,请大家见谅。

还有 Fluent API 跟 Spring RestTemplate API 很相似,在不使用 Http 连接池的情况下,可能 Spring RestTemplate API 更实惠。

write by chunlin.qiu


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值