V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
asilin
V2EX  ›  程序员

HTTP 长连接/持久链接中,必须等待上一个请求的回应数据完全接收后,才能开启下一次请求么?

  •  
  •   asilin · 2019-01-10 17:33:00 +08:00 · 3691 次点击
  •   查看本主题需要登录
    这是一个创建于 2142 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天在写一个 python web 服务时,突然想到如果 HTTP 长连接只管发送请求,不等回应,那怎么判断返回的数据属于哪一次的请求呢?

    进而想到,只有上一次的请求的回应数据完全接收后(根据 Content-Length 头部),才能进行下一次的请求。 也就是说,长连接实际上是一个阻塞 loop,所以才需要有“连接池”的存在,不断的创建新的 TCP 链接,来实现请求的并发。

    为了验证自己的想法,在看 urllib3 的部分代码。

    第 1 条附言  ·  2019-01-10 18:22:00 +08:00

    服务端“队首阻塞”的概念,确实解决了请求排队等待的问题!

    由这个问题,也想到了为什么网络上有那么多“TCP 粘包”的问题了。
    因为 HTTP 1.0/1.1 协议使用了简单的纯文本来通信,而不是二进制,导致了很多人就认为 HTTP 就是 "TCP + 纯文本",进而想到需要如何解决边界定位问题,也就是所谓的“粘包”

    由此可见,HTTP 2.0 才是正常的 ISO 七层协议的实现,增加了第六层表示层
    如果一开始就接触 HTTP 2.0,也就不会有所谓的“粘包”问题了,因为TCP发送数据时,数据本身就是结构化数据,实现了自我的边界定位!

    7 条回复    2019-01-10 18:45:08 +08:00
    q397064399
        1
    q397064399  
       2019-01-10 17:43:37 +08:00
    HTTP2 解决了这个问题
    q397064399
        2
    q397064399  
       2019-01-10 17:43:47 +08:00
    q397064399
        3
    q397064399  
       2019-01-10 17:45:00 +08:00
    对于同一个 tcp 连接,http1.1 允许一次发送多个 http1.1 请求,也就是说,不必等前一个响应收到,就可以发送下一个请求,这样就解决了 http1.0 的客户端的队首阻塞。但是,http1.1 规定,服务器端的响应的发送要根据请求被接收的顺序排队,也就是说,先接收到的请求的响应也要先发送。这样造成的问题是,如果最先收到的请求的处理时间长的话,响应生成也慢,就会阻塞已经生成了的响应的发送。也会造成队首阻塞。

    可见,http1.1 的队首阻塞发生在服务器端。
    q397064399
        4
    q397064399  
       2019-01-10 17:48:12 +08:00
    看了一下 没错的话 应该是请求一股脑子全过去,然后服务器根据请求的顺序 然后一股脑子写回来,不过这样对应用 API 处理有很大的问题,有可能多个请求在同一个 tcp 连接下,然后第一请求 遇到一个较长阻塞的慢 SQL 查询 直接拖慢了整体的请求返回时间
    goofool
        5
    goofool  
       2019-01-10 17:54:18 +08:00
    https://tools.ietf.org/html/rfc2068#section-8.1

    A client that supports persistent connections MAY "pipeline" its
    requests (i.e., send multiple requests without waiting for each
    response). A server MUST send its responses to those requests in the
    same order that the requests were received.
    hilbertz
        6
    hilbertz  
       2019-01-10 17:59:44 +08:00
    这就是 http 能流行的因素之一,简单,99%的应用都能凑活着用
    zhujinliang
        7
    zhujinliang  
       2019-01-10 18:45:08 +08:00
    HTTP1.0 时代规定了一次 HTTP 请求的过程是:建立 TCP 链接,发送请求头,发送请求 body,服务器返回响应头,返回 body 数据,断开 TCP 链接。请求头和响应头都是文本,以两次换行为结束标志,判断数据发送结束更简单粗暴,TCP 链接断开了就是数据发送完了。
    HTTP1.1 协议进行了扩充,要求如果打算复用链接( Keep-Alive ),传送数据时要么响应头指定 Content-Length,要么用 chunked 编码,这两种方法都可以判断一次请求的数据发送完成。在一次请求结束之后,TCP 链接才可被复用进行下一次请求,否则根本无法判断当前传送的数据属于哪次请求的。
    毕竟 HTTP1.1 还是基于纯文本的协议,无法做出“管道”的概念,如果在 TCP 链接之上再建一层“管道”逻辑,每个请求在各自的管道中进行,管道间互不影响,才能做到在一个 TCP 链接上并发处理请求。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1478 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 17:13 · PVG 01:13 · LAX 09:13 · JFK 12:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.