Request-Response是一种信息交换模式,在一次完整的通讯中,大概流程是这样的(文中的通讯都是基于TCP而言的)。
注意:在os层面看来,请求不一定是堵塞的,完全可以用IO复用非堵塞,可以看看常用的几种IO模型
这种通讯方式,有两个可以优化的地方。
一个tcp connection只服务一次Exchange,然后就被关闭了,tcp connection的创建和销毁都是废资源的。可以优化为多次Exchange共享同一个connection。 形象表示如下:
[ConnectionStart][Request1][Response1][ConnectionClose] [ConnectionStart][Request2][Response2][ConnectionClose] ...
=====>
[ConnectionStart][Request1][Response1][Request2][Response2]...[ConnectionClose]
HTTP1.1 支持的Connection: keep-alive 的 Header就是解决这个问题的。
http1.1规范中定义了pipelining,这个功能在浏览器中默认是关闭的,在RFC 2616(https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.2.2)中规定了pipelining:
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.Clients which assume persistent connections and pipeline immediately after connection establishment SHOULD be prepared to retry their connection if the first pipelined attempt fails. If a client does such a retry, it MUST NOT pipeline before it knows the connection is persistent. Clients MUST also be prepared to resend their requests if the server closes the connection before sending all of the corresponding responses.Clients SHOULD NOT pipeline requests using non-idempotent methods or non-idempotent sequences of methods (see section 9.1.2). Otherwise, a premature termination of the transport connection could lead to indeterminate results. A client wishing to send a non-idempotent request SHOULD wait to send that request until it has received the response status for the previous request.
share connection还有两个缺点:
将多个Request打包在一起,同时发送给服务器,服务器处理完后,按照Request顺序返回Response,这就是pipline。http pipeline,redis pipeline,mysql 的batch update也算是pipline。
[ConnectionStart][Request1][Response1][Request2][Response2][ConnectionClose]
只支持幂等HTTP Method和多个Method之间无依赖顺序:Clients SHOULD NOT pipeline requests using non-idempotent methods or non-idempotent sequences of methods (see section 9.1.2).
HTTP Pipeline只规定了Response发送顺序和请求顺序一致,但没有规定是在收到所有Request后再发送Response?
部分浏览器实现,部分未实现,未实现的原因是HTTP pipiline的支持度并不广。 即使实现了的浏览器(Operial),也只针对部分请求(比如多个img)进行pipeline,其他类型的请求都不敢pipeline的。
大多数代理都不支持HTTP pipeline,所以导致HTTP pipeline不实用。
需要Real Server最终按照请求顺序返回响应,在Nginx就是支持HTTP pipeline的。 不过Nginx对HTTP pipeline的处理比较简单,并非并行处理,而仅仅是单线程的串行处理。
怎样在Server端实现HTTP pipeline,在Server端要做的是根据:
HTTP/1.x has a problem called “head-of-line blocking,” where effectively only one request can be outstanding on a connection at a time. HTTP/1.1 tried to fix this with pipelining, but it didn’t completely address the problem (a large or slow response can still block others behind it). Additionally, pipelining has been found very difficult to deploy, because many intermediaries and servers don’t process it correctly. This forces clients to use a number of heuristics (often guessing) to determine what requests to put on which connection to the origin when; since it’s common for a page to load 10 times (or more) the number of available connections, this can severely impact performance, often resulting in a “waterfall” of blocked requests. Multiplexing addresses these problems by allowing multiple request and response messages to be in flight at the same time; it’s even possible to intermingle parts of one message with another on the wire. This, in turn, allows a client to use just one connection per origin to load a page. persistent connections: With HTTP/1, browsers open between four and eight connections per origin. Since many sites use multiple origins, this could mean that a single page load opens more than thirty connections. One application opening so many connections simultaneously breaks a lot of the assumptions that TCP was built upon; since each connection will start a flood of data in the response, there’s a real risk that buffers in the intervening network will overflow, causing a congestion event and retransmits. Additionally, using so many connections unfairly monopolizes network resources, “stealing” them from other, better-behaved applications (e.g., VoIP).
通过在Request上增加一个RequestId标记,Response原样返回这个rid,这样client就能根据rid来对应上请求和响应。这样就能实现多个Exchange共享同一个TCP connection,并且不需Response按序返回。
Dubbo发起请求时,在一个ConcurrentHashMap中保存以RequestId为key,DefaultFuture为Value的值,DefaultFuture包含了这个请求的很多信息。当调用返回时,根据RequestId找到这些信息。 com.alibaba.dubbo.remoting.exchange.support.DefaultFuture#FUTURES
Dubbo的dubbo协议,Motan的montan协议(v1/v2)都是这样设计的: 有兴趣的可以看下这篇博客(Dubbo协议下的单一长连接与多线程并发如何协同工作):https://blog.kazaff.me/2014/09/20/dubbo%E5%8D%8F%E8%AE%AE%E4%B8%8B%E7%9A%84%E5%8D%95%E4%B8%80%E9%95%BF%E8%BF%9E%E6%8E%A5%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B9%B6%E5%8F%91%E5%A6%82%E4%BD%95%E5%8D%8F%E5%90%8C%E5%B7%A5%E4%BD%9C/ com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeChannel.java
除了Dubbo、Motan这种带RequestID的RPC协议外,其实HTTP/2也可以。 HTTP/2利用StreamID完成了上面说的基于RequestId的优化。 QQ图片20190606154513.png 更多关于http2的新特性参考:
我的意见:不算 stackoverflow上有人认为算:https://stackoverflow.com/questions/23419469/is-http-1-1-full-duplex/27164848 有人认为不算:https://www.quora.com/Does-HTTP-provide-a-full-duplex-communication-or-not
我的意见:依然是不算 因为Proxy和HTTPServer会在
自建HTTPServer呢? Full-Duplex Channel over HTTP https://www.innovation.ch/java/HTTPClient/fullduplex.html
Multiplexing of requests is achieved by having each HTTP request/response exchange associated with its own stream (Section 5). Streams are largely independent of each other, so a blocked or stalled request or response does not prevent progress on other stream
也和上面的Stream一个道理。 如果很多图片都来自一个域名下,那么http2的Multiplexing功能可以一个连接加载这些图片。(注意,http2是建立在https上的,浏览器要SSL握手成功才能使用http2)