咱们搞技术要造就见微知著的能力,用逻辑剖析和结构来代替经验主义。例如“背压”(back-pressure)的话题就能够从很简略的模型登程,通过一步步的推导来欠缺解决方案。
生产者 - 消费者模型
这个很简略的模型就是过程内的生产者 - 消费者(producer-consumer)模型。生产者和消费者在不同线程。生产者和消费者通过有界队列连贯,生产者往队列写入音讯,消费者从队列拿取音讯用于计算。若生产者速率过快,队列会写满,生产者暂停写入;若消费者速率过快,队列会排空,消费者暂停拿取。这就是简洁的压力反馈机制。
背压指的是上游速率较慢时反馈给上游的压力,心愿上游也减慢速率,然而实际上也要思考上游速率较快而压力有余的状况。
拉和推
进一步剖析这一模型。如果咱们关注上游拿取数据的形式,这其实是拉模式(pull mode)。上游被动从队列拉取数据,若没有就期待(线程期待队列的信号量),而上游则是在被动往队列推送数据,这其实是推模式(push mode),若推不动就期待(线程期待队列的信号量)。当初上下游在同一个过程内,共享一个队列,拉模式和推模式同时存在。如果是分布式系统,上下游在不同的过程(以及不同的机器),那又如何呢?分布式系统个别会在拉模式和推模式之间二选一,两种模式的压力主导方不同。
在拉模式中:队列在上游,上游的生产者往队列写数据,写满就期待;上游不须要队列,消费者间接向上游申请数据,上游从队列取出数据作为响应返回给上游,这时上游的队列必定是不满的,生产者就能够持续写入数据。这里有一个问题,上游申请上游时处于期待状态,它的计算能力被节约了。更高效的形式是上游超量拉取一些数据,放在上游的外部队列里缓缓解决,同时上游能够申请上游并且期待响应。所以实际上,上游也是有队列的。但这里的重点是,用于跨过程压力反馈的是上游的队列。
在推模式中:队列在上游,生产者间接往上游发送数据,上游若队列已满而不能持续承受数据,就须要告诉上游。一种形式是申请 - 响应(request-response)模型:上游发送带有数据的申请给上游,并且期待上游返回一个“胜利”的响应,若不能解决就返回相应的“谬误”响应,上游减慢或暂停推送,而后再试探何时能复原推送。这种形式显然不是很快,因而也有发射后不必管(fire-and-forget)模型:上游只管发送数据,上游须要从另一个通道发送反馈音讯给上游。然而若上游不及时处理反馈音讯,还持续发送数据,而上游的队列已满,就只能产生网络丢包了,上游依据丢包率来调整推送速率。实际上,若申请 - 响应模型每次批量地把多个数据放在一个申请里,只需为多个数据期待一个响应,而发射后不必管模型其实也是在收到多个数据后发回一个相当于响应的反馈音讯,两者又有什么区别呢?区别只有“谁”来决定批量的大小,申请 - 响应模型是由上游决定批量的大小,发射后不必管模型是由上游决定批量的大小。总之这里的重点是,用于跨过程压力反馈的是上游的队列。
拉模式的次要益处是,队列更凑近上游,反馈更及时。上游总是能够去申请上游,能够通过长轮询(long-polling)来满足实时性,若没有数据就期待在那(Kafka 和 SQS 就是这么干的)。推模式的上游须要很大致力来晓得上游的状态,无论上游是满还是空,上游都要慢一拍才晓得,还可能有丢包的节约。拉模式须要上游每次给上游发申请,这个申请是额定的开销,但只有每次多申请几个数据,申请的开销也不算多大,能承受。
TCP 协定中的背压
既然说到丢包,就说说 TCP 协定中的背压吧,学名 Flow Control。TCP 恰好是推模式,它用滑动窗口(sliding window)来管制推送速率。上游一边推送数据包一边承受 ACK 包,如果滑动窗口的大小是 10,就最多能有 10 个已收回但未被 ACK 的数据包。每当有 ACK,就空出了一个窗口空位,能再推送一个数据包。这个滑动窗口相当于队列。若有数据包未被 ACK,即丢包,能够进行无限次数的重发。未被 ACK 的数据包要暂存在上游的一个缓冲区里以备重发之需,称之为 send buffer。上游收到数据还须要期待程序来解决,要把数据暂存在一个 buffer 里,称之为 receive buffer。TCP 的背压机制(用 buffer)和应用层的背压机制(用 queue)往往是同时存在的,如下图:
滑动窗口只是一个根底机制。为了达到更高效率,TCP 还有拥塞管制(Congestion Control)机制,依据丢包率和一些算法来预测和调整推送速率。咱们编写分布式系统时也能够想一些预测性的方法来提高效率,而不是只靠最根本的压力反馈机制。在一个多级数据流水线中,有上游、中游和上游,压力一级级向上传导是有提早的。推模式能够利用水位线(watermark)来提高效率:上游在队列用量达到某个水位线时,把队列的余量信息反馈给上游,让上游提前调整速率而不是等到上游队列写满才做反馈。拉模式也能够利用水位线,当一个上游对应多个上游时(fan-out traffic),水位线很有帮忙(详情待补充)。
总结
从根本的生产者 - 消费者模型登程,能够一步步推导出像数据流水线这样的分布式系统的背压机制。咱们还晓得了拉模式和推模式,对于大多数利用场景,我比拟举荐拉模式。