在请求访问、消息处理中,通常有两种模式,一种是Pull拉模式,一种是Push推模式。两者各有优缺点,为此响应式编程Reactive Streams提出了结合两者各自优点的Pull-Push模式,即所谓的背压
(Back-Pressure
)处理。
什么是背压
背压,有时也叫反压、回压,这个词对于大多数人都很陌生,笔者也不例外。原因是这个词主要是来自对英文Back-Pressure
的翻译,该词在维基百科的解释:
Back pressure (or backpressure) is a resistance or force opposing the desired flow of fluid through pipes, leading to friction loss and pressure drop. The term back pressure is a misnomer, as pressure is a scalar quantity, so it has a magnitude but no direction. The fluid is what is directed, tending to flow away from high-pressure regions and toward low-pressure regions. If the low-pressure space is more high-pressure than intended (e.g. due to obstructions or tight bends in an exhaust pipe) or the high-pressure space is more low-pressure than intended, this opposes the desired flow and reduces the discharge. Similarly, bending or other operations on a pipe (such as a stock car exhaust system with a particularly high number of twists and bends[1]) can reduce flow rate.[2]
以水管与水龙头举例,上游开始放水,当水龙头的出水速度小于上游放水速度时,水管就会慢慢被水充满,直至在上游源头上外溢,这种反向流动其实就是背压。通过这种反向压力,上游就能知道下游的处理速度以及是否产生了阻塞,从而进一步从源头上进行流量控制,所以背压是实现流控的一种方式。
那为什么back-pressure
要翻译成背压
呢?因为back有后背的意思,可以较为形象的理解就是,当人群通过一个入口时,大家不断地推动前面人的后背往前走,当推不动时,就会从后背传来压力,从而告知后面的人不要再往前推了。
Pull v.s. Push概念
在详细介绍Pull与Push的各自细节前,先介绍下两者在编程方面的使用差别,准确的说是编程范式
的差别。
- Pull: 在逻辑上是一个主动地同步阻塞的编程方式(与命令式编程相同),其逻辑范式是:
var item = datasource.get();//这是个阻塞的过程
do something with item //获取状态item后,继续处理后续逻辑
...
- Push:在逻辑上是一个被动的异步非阻塞的编程方式(与声明式相同),是一种事件通知的方式,其范式是:
datasource.notify(item -> do something)
用更形象的例子举例,就是java中Iterator和Stream的区别,Iterator就是pull-based的,使用方式是iterator.next()这种命令式的方式,而Stream就是push-based的,使用的是map等操作的声明式方式。
Pull模式
Pull模式——通常是同步、阻塞等待响应时间长,因为是同步的,所以编程相对简单,debug也方便。在网络通信方面,每次获取数据都需要先发送request,才能获取到response数据,增加了二分之一的round trip的网络开销,例如http协议。
Push模式
Push模式——通常是异步的、非阻塞的基于消息驱动的方式。由于是异步非阻塞,所以性能要比pull模式高不少。同时由于获取数据不需要发送request,也减少了发送request的二分之一的round trip的网络开销,例如websocket协议。
Push模式从性能上看起来处处要比Pull模式优秀,但Push模式有个问题是,如果消息发布者producer的生产能力大于消息消费者consumer的能力时,为导致压垮consumer,其原因就是缺少了consumer对producer的控制。而反观Pull模式,所有的消息消费都是由consumer发送request给producer才可以,所以相对Pull模式,Push模式中consumer丧失了对producer的控制能力。
Pull-Push模式
为了使Push模式中consumer也能具有对producer的流量控制能力(即背压back-pressure
),Reactive Streams推出了Pull-Push模式
。那么,什么是Pull-Push模式呢?
顾名思义,Pull-Push
中Pull在Push之前,也就是要获取数据(Push),需要先发送一个请求(Pull)。与Pull模式不同的是,每次发送请求request
时,会携带一个请求数据量大小n
的值,用于流量控制。producer接收到请求大小后,会以Push的方式,不断地将数据发送给consumer,直到达到请求的数据量结束为止。
- 当
n==1
时,Pull-Push模式就会降级为Pull模式。 - 当
n==Long.MAX_VALUE
时,Pull-Push模式会升级为Push模式,因为假设每个消息发送间隔是一纳秒,那Long.MAX_VALUE
的数据量则至少需要几百万年,所以可以近似的理解为请求的数据量是无限的,所以也就升级为了Push模式。
总结
本文主要介绍了背压的相关概念,并介绍了Pull、Push模式的区别以及Reactive Streams中的背压实现 —— Pull-Push模式的结合。
原创不易,需要一点正反馈,点赞+收藏+关注,三连走一波~ ❤
如果这篇文章对您有所帮助,或者有所启发的话,请关注公众号【临虹路365号】,您的支持是我们坚持写作最大的动力。