`
dongliboyqq
  • 浏览: 11708 次
文章分类
社区版块
存档分类
最新评论

HttpClient的keepalive以高效利用长连接

阅读更多
keepalive的失效时间总结:
客户端(httpclient创建并维护socket连接池):1、HTTP层:通过和服务器端协商的getKeepAliveDuration时间来失效,默认情况下返回-1,即永久有效。2、连接池层:通过连接池的启动job程序IdleConnectionsEvictor来回收过期socket,过期时间配置maxIdleTime来决定。3、linux操作系统层(vi /etc/sysctl.conf):配置net.ipv4.tcp_keepalive_time = 1200  net.ipv4.tcp_keepalive_probes = 5  net.ipv4.tcp_keepalive_intvl = 15这三个值来控制回收时间。
服务器端(被访问端的连接失效时间):1、应用容器层,比如tomcat中keepAliveTimeout="60000"来控制连接的失效时间(如果没有设置keepAliveTimeout,取connectionTimeout="10000"的时间为keepalive的失效时间)。2、操作系统层:和上面一样。
小结:如果服务器端的过期时间小于客户端的过期时间,在临界值状态下就可能会发生如下报错:
错误1:java.net.SocketException: Connection reset
错误2:java.net.SocketException: Broken pipe
错误3:org.apache.http.NoHttpResponseException: *** failed to respond
会导致访问出问题,所以个人建议客户端的过期时间要小于服务器端的过期时间。

一、扫盲阶段
总所周知http1.1(http1.0不是标准,依服务器而定)是支持长连接的,长连接能够保证服务器和客户端的socket能够高效利用,减少握手等额外的开销。httpClient在正常情况下会带上Connection: keep-alive表示我是支持长连接的,当完成一个请求后,视情况决定是否关闭连接。
正常情况下,服务器在返回内容的头中会带上如下信息:

但是如果服务器端连接次数计数达到指定值时,则会在返回内容中添加Connection: close信息,表示该连接将被关闭。

这个时候该connection就失效了,客户端如果下次请求服务器的话,需要重新创建一个新的连接。但是有个问题,是否连接保持是由服务器端决定的,一旦连接超时导致connection close,服务器是不会专门通知客户端的,所以我们不能依赖Connection: close信息来决定是否关闭socket。

二、应用容器超时设置(服务器端有效,客户端连接池不受控制)
我们先来看看tomcat对长连接的配置。下面是一个典型的配置长连接的方式,其中:
keepAliveTimeout:表示在下次请求过来之前,tomcat保持该连接多久。这就是说假如客户端不断有请求过来,且为超过过期时间,则该连接将一直保持。
maxKeepAliveRequests:表示该连接最大支持的请求数。超过该请求数的连接也将被关闭(此时就会返回一个Connection: close头给客户端)。
由于keeplive的不确定性,这对客户端高效的实现http请求带来了一定挑战。在httpClient中对连接的复用采用了多种机制同时保证,但是这需要和服务器端一起配合。如果服务器端和客户端在keepAlive相关的配置不匹配的话,轻则效率低下,重则伤筋动骨,具体有哪些需要注意的地方?下面一一道来。
TOMCAT配置
<Connector port="8088" protocol="org.apache.coyote.http11.Http11NioProtocol"
                connectionTimeout="10000"
                redirectPort="8443"
                URIEncoding="UTF-8"
                maxConnections="5000"
                maxThreads="5000"
                maxHttpHeaderSize="20480"
                keepAliveTimeout="60000"
                maxKeepAliveRequests="300"
               acceptCount="2500" />

三、HTTPClient连接池说明(客户端配置)
http连接池[采用httpclient-4.5.jar]
httpClient提供了连接池实现连接的复用,这是实现高效请求的基础,下面是给httpClient设置连接池的方法:
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
//连接池最大生成连接数200
cm.setMaxTotal(200);
// 默认设置route最大连接数为20
cm.setDefaultMaxPerRoute(20);
// 指定专门的route,设置最大连接数为80
HttpHost localhost = new HttpHost("locahost", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
// 创建httpClient
CloseableHttpClient httpClient = HttpClients.custom()
        .setConnectionManager(cm)
        .build();
有几个需要注意的地方:
1.如果你的客户端连接的目标服务器只有一个,那么大可设置最大route连接数和最大连接池连接数相同,以便高效利用连接池中创建的连接。
2.创建的httpClient对象是线程安全的,如果连接的目标服务器只有一个的话,创建一个全局对象即可。一个对象好比开了一个浏览器,多个线程无需每次请求时专门开一个浏览器,统一一个即可。
3.如果httpClient对象不再使用,记得关闭,释放与服务器保持连接的socket,以便服务器更高效的释放资源。

四、客户端连接池管理,清理过期的连接(客户端有效)
虽然连接池有了,但是由于http连接的特殊性(只有在通讯正在进行(block)时才能够对IO事件做出反应)。一旦连接被放回连接池后,我们无从知道该连接是否还是keepalive的,且此时也无法监控当前socket的状态(即服务器主动关闭了连接,但客户端没有通讯时是不知道当前连接的状态是怎样的)。怎么办呢?httpClient采用了一个折中的方案来检查连接的“状态”,就是由客户端自己通过配置去主动关闭其认为是失效的连接。具体方法如下:
public final class IdleConnectionEvictor {

    private final HttpClientConnectionManager connectionManager;
    private final ThreadFactory threadFactory;
    private final Thread thread;
    private final long sleepTimeMs;
    private final long maxIdleTimeMs;

    private volatile Exception exception;

    public IdleConnectionEvictor(
            final HttpClientConnectionManager connectionManager,
            final ThreadFactory threadFactory,
            final long sleepTime, final TimeUnit sleepTimeUnit,
            final long maxIdleTime, final TimeUnit maxIdleTimeUnit) {
        this.connectionManager = Args.notNull(connectionManager, "Connection manager");
        this.threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory();
        this.sleepTimeMs = sleepTimeUnit != null ? sleepTimeUnit.toMillis(sleepTime) : sleepTime;
        this.maxIdleTimeMs = maxIdleTimeUnit != null ? maxIdleTimeUnit.toMillis(maxIdleTime) : maxIdleTime;
        this.thread = this.threadFactory.newThread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (!Thread.currentThread().isInterrupted()) {
                        Thread.sleep(sleepTimeMs);
                        connectionManager.closeExpiredConnections();
                        if (maxIdleTimeMs > 0) {
                            connectionManager.closeIdleConnections(maxIdleTimeMs, TimeUnit.MILLISECONDS);
                        }
                    }
                } catch (Exception ex) {
                    exception = ex;
                }

            }
        });
    }
我们新建了一个Thread,该Thread每隔5s进行一次清理,把认为是expired或空闲超过30s的连接给关闭掉。这里关闭空闲的连接好理解。问题是关闭expired的连接,什么样的连接才算是expired呢?刚才说过客户端是无法检测到对应的连接是否是alive的,故通过一个配置告诉httpClient每个连接大概什么时候会断掉,httpClient会认为过了该时间的连接就是expired的连接。默认情况下,httpClient认为只要连接上服务器的socket将永远不会断掉。显然这种假设过于乐观。

使用回收job:需要创建并start job,才会生效。
IdleConnectionsEvictor idleConnectionsEvictor = new IdleConnectionsEvictor(clientConnectionManager,
    cfg.getUpdateConnectionsEvictorSleepDelay(), TimeUnit.MILLISECONDS,
    cfg.getMaxUpdateConnectionIdleTime(), TimeUnit.MILLISECONDS);
idleConnectionsEvictor.start();

重点1:connectionManager.closeExpiredConnections();其中一般expiry都会比较大,一般默认过期时间都是一个多月
if(timeToLive > 0L) {
    this.validityDeadline = this.created + tunit.toMillis(timeToLive);
} else {
    this.validityDeadline = 9223372036854775807L;
}

this.expiry = this.validityDeadline;
重点2:connectionManager.closeIdleConnections(maxIdleTimeMs, TimeUnit.MILLISECONDS);连接池中的失效时间为设置的maxIdleTimeMs。
public void closeIdle(long idletime, TimeUnit tunit) {
    Args.notNull(tunit, "Time unit");
    long time = tunit.toMillis(idletime);
    if(time < 0L) {
        time = 0L;
    }

    final long deadline = System.currentTimeMillis() - time;
    this.enumAvailable(new PoolEntryCallback() {
        public void process(PoolEntry<T, C> entry) {
            if(entry.getUpdated() <= deadline) {
                entry.close();
            }

        }
    });
}
//updated时间为创建对象的时间
this.updated = System.currentTimeMillis();

五、客户端HTTP层的keepalive保持时间(客户端有效,默认返回-1,永久有效)
keepalive策略:为了使connMgr.closeExpiredConnections();起到作用,我们需要指定连接keep alive策略,来告诉httpClient,哪些连接大概什么时候会过期,你可以关闭他们。下面是一个设置keep alive 策略的例子:
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) {
                }
            }
        }
        HttpHost target = (HttpHost) context.getAttribute(
                HttpClientContext.HTTP_TARGET_HOST);
        if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
            // Keep alive for 5 seconds only
            return 5 * 1000;
        } else {
            // otherwise keep alive for 30 seconds
            return 30 * 1000;
        }
    }
};
CloseableHttpClient client = HttpClients.custom()
        .setKeepAliveStrategy(myStrategy)
        .build();
这个例子指明在访问www.***.com和其他未知服务器时的keeplive为一个固定值,如果服务器返回keeplive过期时间,则通过服务器告诉客户端该连接大概什么时候过期(注意这不是一个标准的http协议,不是所有服务器都支持)。

六、总结
如果httpClient是访问单一的地址(如rpc请求),通过与服务器配置配合能够更高效的利用客户端的连接池,例如以tomcat为例,设置maxKeepAliveRequests为-1则可以使该连接可以请求无限次数,而设置keepAliveTimeout为一个较大的时间则可以让客户端更有效的利用连接池中的连接。凡事都有前提,如果目标tomcat即用于rpc调用,又用于服务正常连接请求的话,则上面优化反而会大量消耗服务器资源。这时可以讲这两类服务拆分成两个tomcat进行分别处理。另外,如果在服务器前面配有代理的话(apache,nginx),则还需视情况设置代理的keepAlive策略以提高利用率。
分享到:
评论

相关推荐

    springboot-keepalive设置测试

    在这个“springboot-keepalive设置测试”项目中,我们将关注的是如何在Spring Boot应用中配置和测试HTTP长连接(keepalive)功能,以优化服务器与客户端之间的通信效率。 HTTP长连接,也被称为持久连接,允许TCP...

    高效池化-JAVA-HttpClient工具类

    1.高效简单池化的HttpClient工具类,提供单元测试用列。 2.支持基于SpringBoot 2.1.x的自动装载模块,引用依赖即可使用。 3.公司几十个项目都使用该工具类访问第三方的Http/Https+json协议接口。 4.经过上市公司多个...

    httpclient.jar包下载

    HttpClient库允许开发者执行各种HTTP方法(如GET、POST、PUT等),处理重定向、认证、cookie管理等复杂网络交互,同时提供了线程安全和高效的连接管理机制。这个库在处理Web服务API、爬虫程序或任何需要与HTTP服务器...

    httpclient4.5.5所有包

    - **连接管理**:HttpClient 提供了连接池管理器,可以复用已建立的连接,减少网络延迟,提高效率。 - **HTTP 方法**:支持 GET、POST、PUT、DELETE 等多种 HTTP 方法,以及 HEAD、OPTIONS、TRACE 等其他标准方法...

    利用HttpClient发送HTTP请求

    利用HttpClient发送HTTP请求 利用HttpClient发送HTTP请求

    HttpAsyncClient 异步httpClient和同步httpClient连接池的工具类 包含jar

    HttpAsyncClient连接池的使用,项目中频繁发送http请求,同步http阻塞主线程,影响性能,使用 HttpAsyncClient可使性能提高,这里配合连接池使用,效果更好,同时还附带同步httpClient的连接池使用

    JAVA利用HttpClient进行HTTPS接口调用

    在Java编程中,HttpClient是一个非常重要的库,常用于执行HTTP请求。当涉及到HTTPS接口调用时,HttpClient...通过实践和理解这些文件,开发者可以更好地掌握HttpClient的使用,从而编写出高效、安全的网络请求代码。

    httpclient

    《HttpClient详解:构建高效可靠的HTTP通信》 在Java开发中,HttpClient是一款强大的工具,用于执行HTTP请求并处理响应。...在实际项目中,应结合具体需求,灵活运用HttpClient的各类特性,以提高程序的健壮性和性能。

    httpclient-4.5.3.zip

    - **连接管理**:通过`HttpConnectionManager`,HttpClient可以管理多个并发连接,实现高效的连接复用,降低网络延迟。 - **请求和响应处理**:HttpClient提供了`HttpRequestExecutor`和`HttpResponseHandler`接口...

    commons-httpclient-3.1jar包

    2. 空闲连接回收:设置合理的KeepAlive策略,防止内存泄露。 3. SSL证书问题:处理自签名或不受信任的证书,可能需要自定义SSLSocketFactory。 4. 并发处理:在多线程环境中,需正确管理和同步HttpClient实例。 总...

    httpClient官方文档

    在文档中,这一点被明确地指出,以帮助用户正确地利用HttpClient。 文档中提到了HttpClient的基础知识,包括以下几个方面: 1. 请求执行(Request Execution) - HTTP请求(HTTP Request):讲述了如何构建和发送...

    httpClient需要的jar包

    在实际使用HttpClient时,需要根据项目需求选择合适的版本,理解其工作原理,正确配置和使用相关组件,以实现高效且可靠的HTTP通信。同时,及时关注Apache HttpClient的更新,以便利用新特性并修复已知问题。

    HttpClient4.1.2中英文文档

    5. **高性能**:通过高效的缓冲机制和对Keep-Alive连接的支持,HttpClient能实现高效的数据传输。 6. **国际化**:提供的文档中包含了中英文两个版本,便于不同语言背景的开发者理解和使用。 在《HttpClient ...

    Httpclient依赖包

    10. **易用性与扩展性**:HttpClient的设计允许开发者方便地替换或扩展其组件,如HTTP连接器、缓存策略、认证策略等,以满足特定需求。 在使用HttpClient时,开发者需要注意以下几点: 1. **版本兼容性**:...

    httpclient4.5 jar包

    - 改进的连接管理:提供了更高效的连接池管理和重用策略,以减少网络延迟。 - 支持HTTP/2和SPDY协议:这些新协议可以显著提高网络传输效率,减少延迟并支持多路复用。 - 完善的异步处理:支持非阻塞I/O,适应高...

    HTTPClient工具类,完整

    HTTPClient工具类,完整,HTTPClient工具类,完整,HTTPClient工具类,完整HTTPClient工具类,完整,HTTPClient工具类,完整

    httpClient

    HttpClient实例是执行HTTP请求的主要对象,它可以复用连接以提高性能。HttpContext则用于存储请求相关的上下文信息,例如认证凭证或Cookie。HttpMethod代表具体的HTTP操作,如GET、POST或PUT。 1. **配置HttpClient...

    HTTPClient组件的应用

    HTTPClient组件是Apache Jakarta Commons项目中的一个子项目,专为Java开发者提供高效、全面的HTTP协议...同时,HttpClient还提供了丰富的配置选项,如设置超时时间、连接管理策略等,以适应不同场景下的网络通信需求。

Global site tag (gtag.js) - Google Analytics