使用HTTPclient保持长连接

 更新时间:2021年10月23日 11:59:11   作者:MrHamster  
这篇文章主要介绍了使用HTTPclient保持长连接,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

HTTPclient保持长连接

首先解释一下什么是长连接

当我们向一台服务器发起请求时,我们需要和对方建立一条通道,去传输数据,所谓的短连接,就是说我们建立起了通道,然后在传输完数据,就把通道摧毁,下次需要的时候再重新去建立通道。

长连接呢,就是指,我们建立了一条通道,传递完数据后,先不摧毁,下次如果还需要传输数据,就复用这条通道。

因为通道的建立是需要花费时间的,所以长连接的优势就在于响应速度快,但是服务器压力大,因为同时有很多人在向服务器建立通道,即便有些通道已经传输完数据了,由于长连接的原因,通道也不会被摧毁;短连接呢,则是,响应速度慢,服务器压力小。

由于现在更多的是强调用户的体验,所以长连接目前是最常见的。

如何在java中实现一个长连接呢

其实很简单,只需要在请求的请求头中加入特定的参数 :“Connection”:"keep-alive"即可。这样如果对方支持长连接的话,那么这个连接就会保持长连接了。

问题的关键就来了,在一次压测某个https请求响应速度的代码中,我发现了,当对方响应数据为null,也就是responseBody中带的数据为null时,响应速度特别快,大概在5ms左右,但是一旦对方返回了响应数据,本次响应就可能达到了20ms。

然后请运维同事抓包,发现每次连接,都会耗费时间在用户认证上,其实也就是从某个方面反应出,每次都是新建立了一个连接。

HttpPost httpPost = new HttpPost("xxxxx");
httpPost.addHeader("Connection", "keep-alive");
CloseableHttpClient httpClient = null;
for(int i =0 ;i<5000;i++){
    long t1 = System.currentTimeMillis();
	CloseableHttpResponse response = httpClient.execute(httpPost);
	long t2 = System.currentTimeMillis();
}

上了一段简单的代码,表示一下大概的逻辑,实际的压测代码还包括了线程池,连接池的完整参数等等,如果需要的可以留言或者翻看本人的其他的博客,有写线程池和连接池。

就是这样一段代码,导致每次连接都是新建立的连接,那么原因是什么呢,我们先上代码:

for (int i = 0; i<5000;i++){
     long t1 = System.currentTimeMillis();
      CloseableHttpResponse response = httpClient.execute(httpPost);
      if(null != response.getEntity()){
           EntityUtils.consume(response.getEntity());
      }
      long t2 = System.currentTimeMillis();
 }

我们只需要简单加上三行代码,就可以解决这个问题了,这是为什么呢,让我们点进去源码看一下

在这里插入图片描述

这么一看, 其实这个方法也并没有做什么,只是简单的取到了流去关闭,为什么就保持长连接了呢。

后来仔细读http连接的原理才得知,当一个连接建立,响应数据时,会封装CloseableHttpResponse这个对象里面,其中的Entity对象就是包含着响应体的数据,我们需要用流去获取。如果你不去获取,那么这个数据就会存在于这个对象中,连接池就会认为,这个通道里有未处理的数据,然后它不会去复用这个通道,而是选择重建一个通道。这就完美解释了为什么压测时,对方返回null时,响应速度特别快,而携带返回数据时,响应速度特别慢了。

那么再仔细想想,为什么我们平常不知道这个知识点,却从来没有报过错呢,那是因为正常情况下,我们都是需要会对response做处理,比如String responseContent = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); 类似这种,我们点进源码看,其实也是取到了流,并做了关闭操作。平常还是要多阅读源码,想想源码。

httpclient因为保持永久长连接造成连接吊死的问题

httpclient使用了连接池,如果没有设置keep-alive策略,PoolingHttpClientConnectionManager会默认使用永久连接。

最近在调用京东api时,发现一个请求开始是可以获取到数据的,但隔了两分钟后再请求就会出现read timeout异常。

对比请求成功和请求失败的日志后发现,请求成功的有以下日志“Connection: keep-alive”,“Connection can be kept alive indefinitely”;但请求失败的却打印“Shutdown connection”,“Connection discarded”。

每次失败后再请求都会成功。因此推测中应该是对方服务器端禁止长连接,当连接到达一定时间会就会断开。

后来上网找到keep-alive策略的代码。

添加策略后,问题解决

ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                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 ("bizapi.jd.com ".equalsIgnoreCase(target.getHostName())) {
                    return 60 * 1000;
                }
                else {
                    return 300 * 1000;
                }
   CloseableHttpClient httpClient = httpClientBuilder.setConnectionManager(pollingConnectionManager)
                .setKeepAliveStrategy(keepAliveStrategy).setDefaultRequestConfig(defaultRequestConfig).build();

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 如何用Java 几分钟处理完 30 亿个数据(项目难题)

    如何用Java 几分钟处理完 30 亿个数据(项目难题)

    现有一个 10G 文件的数据,里面包含了 18-70 之间的整数,分别表示 18-70 岁的人群数量统计,今天小编通过本文给大家讲解如何用Java 几分钟处理完 30 亿个数据,这个问题一直以来是项目难题,今天通过本文给大家详细介绍下,感兴趣的朋友一起看看吧
    2022-07-07
  • Java项目中防止SQL注入的四种方案总结

    Java项目中防止SQL注入的四种方案总结

    SQL注入是一种代码注入技术,通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,下面我们就来看看如何在项目中防止SQL注入吧
    2023-10-10
  • Java Map 按Key排序实例代码

    Java Map 按Key排序实例代码

    这篇文章主要介绍了Java Map 按Key排序实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • java中brew安装rabbitmq以及简单实例

    java中brew安装rabbitmq以及简单实例

    RabbitMQ是基于AMQP协议,由Erlang语言开发的开源消息队列系统,广泛应用于分布式系统中,用于应用程序间的消息传递,它支持多种交换机类型,如直连交换机、扇形交换机和主题交换机等,能够满足不同的消息路由需求
    2024-10-10
  • Java新手教程之ArrayList的基本使用

    Java新手教程之ArrayList的基本使用

    ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,这篇文章主要给大家介绍了关于Java新手教程之ArrayList基本使用的相关资料
    2021-06-06
  • 一篇文章带你了解JAVA结构化编程详情

    一篇文章带你了解JAVA结构化编程详情

    下面小编就为大家带来一篇讲解JAVA结构化编程的文章。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-09-09
  • Springboot发送邮件功能的实现详解

    Springboot发送邮件功能的实现详解

    电子邮件是—种用电子手段提供信息交换的通信方式,是互联网应用最广的服务。本文详细为大家介绍了SpringBoot实现发送电子邮件功能的示例代码,需要的可以参考一下
    2022-09-09
  • struts2配置静态资源代码详解

    struts2配置静态资源代码详解

    这篇文章主要介绍了struts2配置静态资源的相关内容,文中涉及了具体代码介绍,需要的朋友可以参考下。
    2017-09-09
  • 关于连接远程redis的流程

    关于连接远程redis的流程

    这篇文章主要介绍了关于连接远程redis的流程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • JavaCV简介与环境搭建详细步骤

    JavaCV简介与环境搭建详细步骤

    JavaCV是一个开源的Java接口,它为几个著名的计算机视觉库(如OpenCV、FFmpeg)提供了Java封装,这篇文章主要给大家介绍了关于JavaCV简介与环境搭建的相关资料,需要的朋友可以参考下
    2024-04-04

最新评论