pipeline模式的理解

原创内容,转载请注明出处

Posted by Weakyon Blog on November 7, 2015

对于C/S模型而言,传统的请求和回复是这样的

  
req ------------->
    <------------- rsp
req ------------->
    <------------- rsp
req ------------->
    <------------- rsp

而pipeline模式是这样的

  
req1 ------------->
req2 ------------->
req3 ------------->
req4 ------------->
    <------------- rsp1
req5 ------------->
    <------------- rsp2
    <------------- rsp3
req6 ------------->
    <------------- rsp4

pipeline模式下的客户端可以狂吐数据,而不用等待服务端回复,服务端会按顺序返回rsp。

传统模式需要很多连接才能达到的吞吐率,pipeline模式几个连接就可以达到了

一般在客户端机器较少,且每个客户端使用多进程,且每个进程都是单线程这样的情况下,这种使用较为常见。

但是也增加了开发成本,比如rsp4出错了,此时已经到req6了。那么req4,req5,req6都得重传。

而重传必然要保留req4,req5,req6的信息,这样对于客户端的内存会开销较大。

而对于服务器端来说,如果处理压力太大,使得网络接受的内容大量挤压在应用的接受缓冲区,那么也会占用大量的内存。

传统模式在这种情况下,各个连接互相不影响。失败了重传一个就行了。客户端和服务端占用内存较少。


那么如何判别服务端是否能支持pipeline模式呢

第一种是天然设计上就支持的,例如redis,由于它是单进程单线程的,所以他的请求和回复必然是顺序的。

第二种是由于天然不支持而后天进行支持的,例如一个多线程的程序,rsp3有可能比rsp1提前发送到客户端。此时在协议包里需要增加一个序列号字段。

这个序列号由客户端产生,对每个req都编号,加入通信协议发送到服务端,服务端会对每个rsp加上对应的req的序列号,这样客户端就能正确的识别rsp和req的关系了。

多线程程序除非网络驱动写的比较奇葩,否则一旦有客户端和服务端对应的序列号,那必然能支持pipeline。


传统模式和pipeline两种优劣,见仁见智。

对我而言,最简单的就是最高效的。我更倾向于传统模式,多线程下的pipeline模式较为复杂(要考虑客户端和服务端的内存压力),我目前hold不住。

但是pipeline的变种,我叫做“打包发”这种方式,还是比较容易处理的

  
req1 ------------->
req2 ------------->
req3 ------------->
req4 ------------->
    <------------- rsp1
    <------------- rsp2
    <------------- rsp3
    <------------- rsp4
req5 ------------->
req6 ------------->
req7 ------------->
req8 ------------->
    <------------- rsp5
    <------------- rsp6
    <------------- rsp7
    <------------- rsp8

虽然req一次发送了多个,但是达到例如四个这个阀值,就等待rsp全部返回,然后再继续发req

这样客户端和服务端对于内存的占用都较小。且在req较小的情况下,比起传统模式还是能提升比较高的性能。

07 Nov 2015