http中的长连接和短连接你真的了解吗?

做web开发的老铁应该都知道http协议,它是前后端通信中非常常用的一种通信协议,HTTP(HyperText Transfer Protocol)即超文本传输协议,是互联网上应用最为广泛的一种网络协议。

HTTP协议是一个基于请求-响应模型的协议,客户端(浏览器、移动客户端等)发起请求,服务端接收请求后进行处理并返回响应。

在HTTP协议中,有两种连接方式:长连接和短连接,它们主要区别在于连接的持续时间和资源利用率。

长/短 连接到底是啥

长连接,又称持久连接(Persistent Connection),是指在一个TCP连接上可以连续发送多个请求和响应报文,在通信双方的任何一方主动关闭连接之前,连接一直保持。长连接在HTTP/1.1中是默认启用的,通过设置请求头部的Connection字段为Keep-Alive来实现,

短连接,又称非持久连接(Non-Persistent Connection),是指客户端与服务器每进行一次HTTP操作,就建立一次连接,任务结束后则断开连接。在HTTP/1.0中,短连接是默认的连接方式。

该用哪个

虽然我们知道了长连接和短连接是什么了,但是在实际的使用场景中,该如何选择呢?

下面我们来对比一下,长连接和短连接各自的使用场景。

使用长连接

可以减少了TCP连接的建立和关闭次数,降低了网络资源消耗。例如,对于一个网站,用户在浏览多个页面时,使用长连接可以避免频繁建立和关闭连接,节省了网络资源。

同时可以提高数据传输效率,减少了网络延迟。例如,在一个文件传输过程中,使用长连接可以避免每次传输一个文件都需要重新建立连接,从而提高了文件传输的效率。

当然长连接也有一些不足之处:

长连接会占用更多的服务器资源,如内存、文件描述符等。例如,当有大量客户端同时连接到服务器时,服务器需要为每个客户端保持一个连接,可能导致服务器资源耗尽。

当客户端数量较多时,可能导致服务器资源耗尽。

使用短连接

短连接相对长连接,不会长时间的占用服务器资源。适用于请求量较小、连接数较多的场景。例如,在一个物联网应用中,大量设备定时向服务器发送数据,短连接可以避免长时间占用服务器资源。

短连接的不足之处也很明显:

短连接需要频繁建立和关闭TCP连接,增加了网络资源消耗。例如,在一个网站中,用户访问多个页面时,使用短连接需要为每个页面重新建立连接,消耗了较多的网络资源。

如何管理长连接

上文说了,长连接和短连接的使用场景和不足之处,那么我们具体该怎么使用呢?

其实在平时的业务开发的过程中,我们经常使用的方案是这样的:

如果并发量不大的系统中可以使用长连接,同时设置长连接的超时过期时间,

超时过期:如果一个链接上长时间没有请求的话,那么就断开这个链接, 比如,nginx 提供的 keepalive_timeout 参数

如果是并发量比较大的系统,那么就要使用长连接了,因为在大并发的情况下,使用短连接需要频繁建立和断开连接,产生很多不必要的消耗,除此之外,链接断开的过程中会在 time_wait 状态下停留2MSL的时间间隔,这段时间链接使用的四元组会被占用,这个对客户端和服务端都有一定的影响,但是这种状态对服务端和客户端的影响是不一样的,有老铁知道其中的差异吗?

而且这里还要注意一点,很多服务端在实现长连接的过程中会控制单个长连接上请求的个数,也是说,一个链接上请求个数超过一定数量后,服务端就会强制断开这个连接,比如 nginx 的 keepalive_requests 参数

如果并发量比较大,同时这限制的阈值比较小的话,还是会存在大量的 time_wait状态的链接。

实操一下

下面我们使用java语言中的技术栈,通过一个简单的例子来模拟一下,长连接和短连接。

短连接实现

客户端
   public static void main(String[] args) {
        HttpClient client = HttpClients.createDefault();
        for (int i = 0; i < 10; i++) {
            InputStream in = null;
            HttpGet httpGet = new HttpGet("http://127.0.0.1:8086/req");
            Header[] headerArrays = new Header[]{new BasicHeader("Connection", "close")};
            httpGet.setHeaders(headerArrays);
            try {
                HttpResponse response = client.execute(httpGet);
                System.out.println(EntityUtils.toString(response.getEntity(), "UTF-8"));
                in = response.getEntity().getContent();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                IOUtils.closeQuietly(in);
            }
        }
    }
服务端
    @GetMapping(value = "/req")
    public String req(HttpServletRequest request){
        String remoteHost = request.getRemoteHost(); // client ip
        int remotePort = request.getRemotePort(); // client port
        System.out.println( remoteHost + ":" + remotePort);
        return "req";
    }

在上述代码中

服务端使用spring-boot中自带的tomcat作为http服务器,处理请求的逻辑也比较简单,当收到客户端的请求后,打印出客户端发送请求时,创建链接使用的ip和端口号。

客户端使用http-client作为http的客户端。

实现短连接的关键在于客户端发送请求使用如下的http请求头:

"Connection", "close"

该请求头的含义表示,告诉服务端:我这次发送的http请求使用的是短连接,你处理完请求后,主动断开连接。

通过查看服务端的日志可以发现,每次请求使用的端口号,都是不同的,表示每次发起请求都是新建的链接。

在这里插入图片描述

长连接实现

客户端
    public static void main(String[] args) {
        HttpClient client = HttpClients.createDefault();
        for (int i = 0; i < 10; i++) {
            InputStream in = null;
            HttpGet httpGet = new HttpGet("http://127.0.0.1:8086/req");
            Header[] headerArrays = new Header[]{new BasicHeader("Connection", "Keep-Alive")};
            httpGet.setHeaders(headerArrays);
            try {
                HttpResponse response = client.execute(httpGet);
                System.out.println(EntityUtils.toString(response.getEntity(), "UTF-8"));
                in = response.getEntity().getContent();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                IOUtils.closeQuietly(in);
            }
        }
    }
服务端
    @GetMapping(value = "/req")
    public String req(HttpServletRequest request){
        String remoteHost = request.getRemoteHost(); // client ip
        int remotePort = request.getRemotePort(); // client port
        System.out.println( remoteHost + ":" + remotePort);
        return "req";
    }

上面实现长连接的代码和实现短连接时基本相同,差异的地方在于客户端发送请求使用的请求头做了调整:

"Connection", "Keep-Alive"

再次观察服务端打印的日志,输出的客户端端口号都是一样的:

在这里插入图片描述

管理长连接

上文说了,长连接长时间保持着,会占用系统资源,所以tomcat等众多http服务器提供了一些参数,来控制长连接的“长度”,

1.长连接有效时间:

如果一个链接上一定时间内没有请求的话,那么服务端就会主动断开连接。
可以在服务端添加tomcat相关的配置:

server:
  port: 8086
  servlet:
    context-path: /
  tomcat:
    keep-alive-timeout: 2s

上述配置 keep-alive-timeout 来配置长连接的空闲超时时间。

我们可以修改客户端程序进行验证,修改后的客户端程序如下:


  public static void main(String[] args) {
      HttpClient client = HttpClients.createDefault();
      for (int i = 0; i < 10; i++) {
          InputStream in = null;
          HttpGet httpGet = new HttpGet("http://127.0.0.1:8086/req");
          Header[] headerArrays = new Header[]{new BasicHeader("Connection", "Keep-Alive")};
          httpGet.setHeaders(headerArrays);
          try {
              HttpResponse response = client.execute(httpGet);
              System.out.println(EntityUtils.toString(response.getEntity(), "UTF-8"));
              in = response.getEntity().getContent();
              Thread.sleep(3000); // 等待3s
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              IOUtils.closeQuietly(in);
          }
      }
  }

客户端程序每隔3s发送一次请求,间隔时间超过长连接空闲超时时间2s。

重新启动客户端和服务端程序,服务端程序打印日志如下:
在这里插入图片描述

2.长连接上请求个数限制:

如果长连接上通过请求超过一定的数量,那么服务端也会主动断开连接。可以在服务端添加tomcat相关的配置:

server:
  port: 8086
  servlet:
    context-path: /
  tomcat:
        max-keep-alive-requests: 2

上述配置 max-keep-alive-requests 来配置长连接最大请求个数,这里配置为2,表示一个长连接上最多可以通过2个请求。

重新启动服务端和客户端程序,服务端程序打印日志如下:
在这里插入图片描述

从日志可以看出,只有连续两个请求的链接端口号是相同的。

  • 17
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值