在正式介绍otter调度模型之前,我们首先得了解TCP/IP协议在解决此类"差网络"环境的一些处理方案,从中借鉴相应的方案.
通过Canal解决Nagle算法,Canal之前是做为otter的一个子项目,为解决otter的数据增量获取的机制,并为otter项目的特点而量身打造了几个feature.
Canal的处理:
a. 构建RingBuffer (可以基于内存控制模式/数量控制模式)
b. 允许客户端指定batchSize获取
i. 内存大小
ii. 记录数
c. 指定定batchSize + timeout获取
i. timeout = -1 ,即时获取,有多少取多少
ii. timeout = 0,阻塞至满足batchSize条件
iii. timeout > 0,阻塞指定的时间或者满足batchSize.
建议值:batchSize=4000(约4M) , timeout=500,内存控制模式
说明:
简单一点说,Select/Load模块会是一个串行机制来保证binlog处理的顺序性,Extract/Transform会是一个并行,加速传输效率.
类似于tcp滑动窗口大小,比如整个滑动窗口设置了并行度为5时,只有等第一个processId Load完成后,第6个Select才会去获取数据。
说明: 将并行化调度的串行/并行处理,进行隐藏,抽象了await/single的接口,整个调度称之为仲裁器。(有了这层抽象,不同的仲裁器实现可以解决同机房,异地机房的同步需求)
模型接口:
这里使用了SEDA模型的优势:
主要包括: 令牌生成(processId) + 事件通知.
令牌生成:
事件通知: (简单原理: 每个stage都会有个block queue,接收上一个stage的single信号通知,当前stage会阻塞在该block queue上,直到有信号通知)
负载均衡算法:
注意点:每个node节点,都会在zookeeper中生成Ephemeral节点,每个node都会缓存住当前存活的node列表,node节点消失,通过zookeeper watcher机制刷新每个node机器的内存。然后针对每次负载均衡选择时只针对当前存活的节点,保证调度的可靠性。
中美网络RTT = 200ms , zookeeper一次写入=10ms
调度成本估算:
a. zookeeper + zookeeper watch (完全分布式)
10 * 4 + 200 * 2 + 200 = 640ms
b. zookeeper + rpc (sticky分布式,尽可能选择同节点)
10 + 100 + 200 = 310ms
c. memory + memory (内存调度,单机房)
0ms
d. memory + rpc (跨机房调度,最优实现,待完成??)
0 + 100 + 100 = 200ms
有了一层SEDA调度模型的抽象,S/E/T/L模块之间互不感知,那几个模块之间的数据传递,需要有一个机制来处理,这里抽象了一个pipe(管道)的概念.
原理:
stage | pipe | stage
基于pipe实现:
在pipe中,通过对数据进行TTL控制,解决TCP协议中的丢包问题控制.