一. 背景
1.1 排队论
通过队列来有效提升系统的负载能力
1.2 ReactiveStream设计模式
生产者-消费者+迭代器
-
消费者Subsriber订阅生产者Publisher
-
Publisher有内容了会调用Sub.onSubsribe()
-
Subsriber请求Pub.request(n)
-
Publisher请求Sub.onNext(data)
1.3 流式系统的挑战
-
分布式环境更加复杂
-
系统中每一层子系统都需要具备背压处理能力
二. Storm反压
-
每个Bolt都有一个Backpressure线程检测是否有recv queue是否有阻塞
-
如果有阻塞就会把情况写入zk
-
其他Spout会监听zk,收到反压情况就会停止发送
三. SparkStreaming反压
-
有Fetcher模块实时从Buffer、Processing等节点中收集指标,然后给PID Controller来计算速率,告知Receiver来调整速度
-
Receiver -> Buffer -> Processing -> PID Controller
-
PID (Proportional Integral Derivative,比例积分差分控制算法)
四. Flink反压
4.1 基础知识
-
发送端ResultPartition
-
接收端InputGate
-
都从LocalBufferPool申请空间,task级别
-
LocalBufferPool从NetworkBufferPool申请空间,TaskManager级别
4.2 基础流程
写(Producer)
-
Task.RecordWriter -> ResultPartition
-
ResultPartition -> Netty
-
Netty -> Socket
读(Consumer)
-
Task.RecordReader <- InputGate
-
InputGate <- Netty
-
Netty <- Socket
4.3 1.5之前基于TCP滑动窗口feedback反压(自然背压)
生产者速度大于消费者
1.Consumer处理不过来,InputGate满了
2.InputGate从LBP获取buffer
3.LBP从NBP获取buffer
4.过了段时间IC满->LBP满->NBP满,IC关掉Netty读取,Netty关掉Socket读取
5.过滤段时间Socket的buffer没有消费也满了,通过tcp feedback(window=0)ack给Sender
6.过了一会Sender的buffer也满了
7.Netty写不进去,当Netty的水位到了上界就不可写
8.RS就无法写入数据到Netty,然后不断从LBP、NBP申请buffer直至申请满了
缺点
-
任何一个下游任务背压的时候,整个TM的链路都会受影响
-
流控链路太长,反压生效延迟大
-
tcp做流控会阻塞tcp通信,导致比如checkpoint barrier无法发出
4.4 1.5+ Credit Based反压
1. Producer的RP向Consumer的IG发送消息的时候会带上backlog size告诉下游准备发送多少消息
2. 下游Consumer会返回相应的credit告诉Producer可以发送多少
3. 当消费端有Buffer可用的时候会有探测机制告知Sender恢复发送数据
优点
-
实现任务级背压,只会影响这个任务,不会影响TM的其他任务
-
链路减短,延时降低
-
不会阻塞Socket
参考