Apache HttpClient链接如何实现复用,如何使用连接池

Apache HttpClient链接如何实现复用,如何使用连接池

注:所有getter/setter 略

/**
 * http请求配置
 *
 * @date 2019/12/18 11:06
 */
public class HttpRequestConfig {
    /**
     * 从连接池中获取链接的超时时间,单位毫秒
     * @date 2019/12/17 18:42
     **/
    private int connectionRequestTimeout = 500;
    /**
     * 客户端和服务端建立连接的超时时间,单位毫秒
     * @date 2019/12/17 18:46
     **/
    private int connectTimeout = 500;
    /**
     * 客户端从服务端读取数据的超时时间,单位毫秒
     * @date 2019/12/17 18:46
     **/
    private int socketTimeout = 1000;
}
/**
 * http请求处理抽象类
 * @date 2019/12/18 10:54
 */
public abstract class AbstractHttpRequestHandler {

    private static final Logger logger = LoggerFactory.getLogger(AbstractHttpRequestHandler.class);

    private HttpRequestConfig httpRequestConfig;

    public AbstractHttpRequestHandler(HttpRequestConfig httpRequestConfig) {
        this.httpRequestConfig = httpRequestConfig;
    }

    /**
     * 关闭链接
     * @date 2019/12/18 11:01
     * @throws IOException
     **/
    protected abstract void close() throws IOException;

    /**
     * 获取httpclient
     * @date 2019/12/18 14:19
     * @return org.apache.http.impl.client.CloseableHttpClient
     **/
    protected abstract CloseableHttpClient getCloseableHttpClient();

    /**
     * post请求,请求参数是json格式
     * @date 2019/12/18 10:55
     * @param url
     * @param headers
     * @param jsonParams
     * @return java.lang.String
     * @throws IOException
     **/
    public String post(String url,@Nullable Map<String, String> headers,@Nullable String jsonParams) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        if (StringUtils.isNotBlank(jsonParams)) {
            httpPost.setEntity(new StringEntity(jsonParams, ContentType.APPLICATION_JSON));
        }
        return request(httpPost, headers);
    }
    /**
     * post请求,请求参数是K/V格式的form-data
     * @date 2019/12/18 10:56
     * @param url
     * @param headers
     * @param formData
     * @return java.lang.String
     * @throws IOException
     **/
    public String post(String url, @Nullable Map<String, String> headers,@Nullable Map<String, Object> formData) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        if (formData != null) {
            List<NameValuePair> nvps = new ArrayList<>(formData.size());
            formData.entrySet().forEach(e -> nvps.add(new BasicNameValuePair(e.getKey(), String.valueOf(e.getValue()))));
            httpPost.setEntity(new UrlEncodedFormEntity(nvps));
        }
        return request(httpPost, headers);
    }
    /**
     * get请求,请求参数会自动拼接在url后面
     * @date 2019/12/18 10:57
     * @param url
     * @param headers
     * @param params
     * @return java.lang.String
     * @throws IOException
     **/
    public String get(String url, @Nullable Map<String, String> headers, @Nullable Map<String, Object> params) throws IOException {
        if (params != null) {
            StringBuilder sb = new StringBuilder();
            params.entrySet().forEach(e -> sb.append("&").append(e.getKey()).append("=").append(e.getValue()));
            String param = sb.toString().replaceFirst("&", "?");
            url = url + param;
        }
        return request(new HttpGet(url), headers);
    }

    private String request(HttpRequestBase httpRequestBase, Map<String, String> headers) throws IOException {
        config(httpRequestBase, headers);
        CloseableHttpResponse response = null;
        try {
            response = this.getCloseableHttpClient().execute(httpRequestBase);
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity);
            // 关闭流
            EntityUtils.consume(entity);
            return result;
        } catch (IOException e) {
            throw e;
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    logger.error("", e);
                }
            }
        }
    }

    private void config(HttpRequestBase httpRequestBase, Map<String, String> headers) {
        RequestConfig config = RequestConfig.custom()
                .setConnectionRequestTimeout(this.httpRequestConfig.getConnectionRequestTimeout())
                .setConnectTimeout(this.httpRequestConfig.getConnectTimeout())
                .setSocketTimeout(this.httpRequestConfig.getSocketTimeout())
                .build();
        if (headers != null) {
            headers.entrySet().forEach(e -> httpRequestBase.setHeader(e.getKey(), e.getValue()));
        }
        httpRequestBase.setConfig(config);
    }
}
/**
 * http连接池配置
 * @date 2019/12/17 18:22
 */
public class HttpClientPoolConfig extends HttpRequestConfig {
    /**
     * 每个host的默认最大连接数
     * @date 2019/12/17 18:23
     **/
    private int defaultMaxPerRoute = 500;
    /**
     * 连接池里的最大连接数
     * @date 2019/12/17 18:33
     **/
    private int maxTotal = 1000;
    /**
     * 链接空闲超时回收,单位毫秒
     * @date 2019/12/17 18:36
     **/
    private int idleTimeOut = 5000;
    /**
     * 链接重试次数
     * @date 2019/12/17 19:03
     **/
    private int retryCnt = 3;
    /**
     * 连接池监控间隔时长,单位毫秒
     * @date 2019/12/18 8:59
     **/
    private int monitorInterval = 2000;
}
/**
 * http 连接池
 *
 * @date 2019/12/17 18:52
 */
public class HttpClientPool {
    private static final Logger logger = LoggerFactory.getLogger(HttpClientPool.class);
    private HttpClientPoolConfig httpClientConfig;
    private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager;
    private CloseableHttpClient closeableHttpClient;
    private HttpClientPoolMonitorThread httpClientPoolMonitorThread;

    public HttpClientPool(HttpClientPoolConfig httpClientConfig) {
        Assert.notNull(httpClientConfig, "httpClientConfig argument must not be null");
        this.httpClientConfig = httpClientConfig;
        this.init(httpClientConfig);
    }

    private void init(HttpClientPoolConfig httpClientConfig) {
        this.poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        this.poolingHttpClientConnectionManager.setDefaultMaxPerRoute(httpClientConfig.getDefaultMaxPerRoute());
        this.poolingHttpClientConnectionManager.setMaxTotal(httpClientConfig.getMaxTotal());
        this.createHttpClient();
    }

    private void createHttpClient() {
        HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> {
            if (executionCount >= this.httpClientConfig.getRetryCnt() // 如果已经重试了3次,就放弃
                    || exception instanceof SSLHandshakeException // 不要重试SSL握手异常
                    || exception instanceof InterruptedIOException // 超时
                    || exception instanceof UnknownHostException // 目标服务器不可达
                    || exception instanceof ConnectTimeoutException // 连接被拒绝
                    || exception instanceof SSLException // SSL握手异常
            ) {
                return false;
            } else if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接,那么就重试
                return true;
            }

            HttpClientContext clientContext = HttpClientContext.adapt(context);
            HttpRequest request = clientContext.getRequest();
            // 如果请求是幂等的,就再次尝试
            if (!(request instanceof HttpEntityEnclosingRequest)) {
                return true;
            }
            return false;
        };

        this.closeableHttpClient = HttpClients.custom().setConnectionManager(this.poolingHttpClientConnectionManager).setRetryHandler(httpRequestRetryHandler).build();
        this.httpClientPoolMonitorThread = new HttpClientPoolMonitorThread(this.poolingHttpClientConnectionManager, this.httpClientConfig);
        this.httpClientPoolMonitorThread.start();
    }

    public CloseableHttpClient getCloseableHttpClient() {
        return closeableHttpClient;
    }

    public void close() throws IOException {
        this.closeableHttpClient.close();
        this.httpClientPoolMonitorThread.shutdown();
        this.poolingHttpClientConnectionManager.close();
    }

    /**
     * 监控连接池链接状态的线程
     * @date 2019/12/18 9:09
     **/
    private final static class HttpClientPoolMonitorThread extends Thread {
        private final HttpClientConnectionManager httpClientConnectionManager;
        private final HttpClientPoolConfig httpConfig;
        private volatile boolean shutdown;
        private Object lock = new Object();

        public HttpClientPoolMonitorThread(HttpClientConnectionManager httpClientConnectionManager, HttpClientPoolConfig httpConfig) {
            this.httpClientConnectionManager = httpClientConnectionManager;
            this.httpConfig = httpConfig;
            this.shutdown = false;
        }

        @Override
        public void run() {
            try {
                while (!this.shutdown) {
                    synchronized (lock) {
                        lock.wait(this.httpConfig.getMonitorInterval());
                        // 关闭无效的连接
                        this.httpClientConnectionManager.closeExpiredConnections();
                        // 关闭空闲时间超过IDLE_ALIVE_MS的连接
                        this.httpClientConnectionManager.closeIdleConnections(this.httpConfig.getIdleTimeOut(), TimeUnit.MILLISECONDS);
                    }
                }
            } catch (InterruptedException e) {
                logger.warn("", e);
            }
        }

        public void shutdown() {
            this.shutdown = true;
            synchronized (lock) {
                lock.notifyAll();
            }
        }
    }
}

/**
 * 采用了连接池方式的请求处理器,链接可复用,close方法是关闭连接池
 * @date 2019/12/18 9:16
 */
public class PoolingHttpRequestHandler extends AbstractHttpRequestHandler {
    private static final Logger logger = LoggerFactory.getLogger(PoolingHttpRequestHandler.class);
    private HttpClientPoolConfig httpClientConfig;
    private HttpClientPool httpClientPool;

    public PoolingHttpRequestHandler() {
        this(new HttpClientPoolConfig());
    }

    public PoolingHttpRequestHandler(HttpClientPoolConfig httpClientConfig) {
        super(httpClientConfig);
        this.httpClientConfig = httpClientConfig;
        this.httpClientPool = new HttpClientPool(httpClientConfig);
    }

    @Override
    public void close() throws IOException {
        this.httpClientPool.close();
    }

    @Override
    protected CloseableHttpClient getCloseableHttpClient() {
        return this.httpClientPool.getCloseableHttpClient();
    }


}

/**
 * 连接池方式的http请求处理器的单例
 * 链接池配置采用了默认配置
 * @date 2019/12/18 10:02
 */
public class PoolingHttpRequestHandlerSingleton {
    private PoolingHttpRequestHandler httpHandler;
    private PoolingHttpRequestHandlerSingleton() {
        this.httpHandler = new PoolingHttpRequestHandler();
    }

    private static final class HttpHandlerSingletonInstance {
        private static final PoolingHttpRequestHandlerSingleton HTTP_HANDLER_SINGLETON = new PoolingHttpRequestHandlerSingleton();
    }

    public static PoolingHttpRequestHandlerSingleton getInstance() {
        return HttpHandlerSingletonInstance.HTTP_HANDLER_SINGLETON;
    }

    public PoolingHttpRequestHandler getHttpHandler() {
        return httpHandler;
    }
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用Go语言标准库中的"net/http"包来实现高性能的HTTP客户端,并且支持连接复用。 可以使用http.Transport结构体来设置连接池,并调用http.Client结构体的Do()方法来发送HTTP请求。 例如: ``` tr := &http.Transport{ MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, DisableCompression: true, } client := &http.Client{Transport: tr} resp, err := client.Get("http://example.com") ``` 这样就可以使用连接池来管理连接,并且可以复用连接来提高性能。 ### 回答2: 使用Golang实现高性能的HTTP客户端并支持连接复用可以通过以下步骤来实现: 1. 利用Golang的net/http包创建一个基本的HTTP客户端。 2. 在发送HTTP请求之前,确定是否已经有可用的连接可以复用。可以使用sync.Pool来管理并重用连接。 3. 如果有可用的连接,从连接池中获取一个连接,否则创建一个新的连接。 4. 发送HTTP请求并获取响应。 5. 处理响应并确认是否可以复用连接。在头部检查`Connection`标头是否为`keep-alive`,或者可以使用`Transport.DisableKeepAlives`方法来强制关闭连接。 6. 将连接放回连接池中以备下次使用。 下面是一个简单的示例代码,实现了一个支持连接复用的高性能HTTP客户端: ```go package main import ( "fmt" "io/ioutil" "net/http" "sync" "time" ) var client *http.Client var pool sync.Pool func init() { transport := &http.Transport{ MaxIdleConns: 10, // 连接池最大空闲连接数 IdleConnTimeout: 30 * time.Second, // 空闲连接的超时时间 DisableKeepAlives: false, // 是否允许连接复用 MaxIdleConnDuration: 0, } client = &http.Client{Transport: transport} pool = sync.Pool{ New: func() interface{} { return client }, } } func main() { url := "http://example.com" resp, err := sendRequest(url) if err != nil { fmt.Println("请求发送失败:", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("读取响应失败:", err) return } fmt.Println("响应数据:", string(body)) } func sendRequest(url string) (*http.Response, error) { req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Add("Connection", "keep-alive") client := pool.Get().(*http.Client) defer pool.Put(client) return client.Do(req) } ``` 这个示例代码实现了一个HTTP客户端,该客户端支持连接复用,并使用连接池来管理连接的创建和重用。在`sendRequest`函数中,我们从连接池中获取一个连接,发送HTTP请求,然后将连接放回连接池以备下次使用。这样可以提高性能并减少连接的创建和销毁开销。 需要注意的是,为了实现连接复用,我们在创建http.Transport时将DisableKeepAlives设置为false,并在发送请求时添加Connection标头并设置为keep-alive。这样服务器端在响应后会保持连接,允许客户端复用该连接。 ### 回答3: 使用Golang实现高性能HTTP客户端并支持连接复用,可以通过以下几个步骤实现: 1. 使用`http.Transport`结构体创建一个HTTP传输对象,并设置相关参数。在这个对象中,可以使用`MaxIdleConns`参数设置空闲连接的最大数量,以及`MaxIdleConnsPerHost`参数设置每个主机的最大空闲连接数量。 2. 创建一个`http.Client`对象,并将前面创建的传输对象传递给它,以便进行HTTP请求。还可以设置其他相关的客户端参数,如超时时间等。 3. 使用`client.Do(request)`方法向服务器发送HTTP请求,并获取响应。这个方法返回一个`http.Response`对象,其中包含了响应的状态码、头部信息、响应体等。 4. 使用完毕后,需要调用`response.Body.Close()`方法关闭响应体,以释放资源。 5. 为了实现连接的复用,可以在多个请求之间共享`http.Client`对象,而不是每次请求都创建一个新的对象。这样,连接池中的连接就可以被复用,提高性能。 6. 在多个并发请求的情况下,可以使用协程来发送并发请求,确保每个请求都可以重用连接并独立工作。 综上所述,通过上述步骤,可以使用Golang实现一个高性能的HTTP客户端,并支持连接的复用,从而提高性能和效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值