起因
- 虽然数据管道各种开源/商业项目的数量上没有OLAP数据库那么多、选型那么复杂
- 但是在面对flume、rabbitmq、kafka等等的有时候是消息队列、有时候是数据管道/采集的组件总还是存在不少疑惑
- 我什么时候应该用这个,什么时候应该用那个呢?
- 最佳的答案可能是一张在所有维度对比所有数据管道项目的大表格,但显然这样的选型表格不存在
- 因此我们需要了解各数据管道的底层逻辑,从而在自己心里构建选型决策树,才能为面对的场景进行恰当的选型
检索
- 读综述类论文的时候,文中总是会提出一个属性矩阵,并依此描述当前已有的实体,从而总结归纳当前,继而预测领域未来的发展
- 当我在各项目的介绍文章,以及各类对比文章中寻找这样的属性时, 发现能够找到很多
- 如
17 个方面,综合对比 Kafka、RabbitMQ、RocketMQ、ActiveMQ 四个分布式消息队列
- 大部分网络上的对比文章过多的陷入了各类实现的细节,
- 就像台式机与笔记本都是由cpu、gpu、主板、内存、硬盘和外设等组成
- 这时候对比台式和笔记本内存条的长短和频率是没用的,一定有更接近本质的属性能够完美区分出彼此
- 台式机和笔记本这个本质属性就是移动性,而在数据管道这个领域,我认为是推拉模型
推拉
- 推与拉,英文push与pull,用于描述两个实体间传递信息时的双方主动性
- 无论push/pull,信息传递的方向是不变的,都是由信息发送方传递到信息接收方
- push与pull描述的是信息传递过程由哪方触发的区别
- push/pull模型对发送/接收两端的要求
- push模型由发送方按自己需求主动发起,接收端必须及时响应
- pull模型由接收端按自己需求主动发起,发送端必须及时响应
- 这里的要求包括处理速度、缓存能力、各类保证等等能力的要求
- 理解push/pull模型对收发双方能力要求的不同,是理解众多数据管道项目底层理念的关键!
- 数据管道,顾名思义,作为信息传递的管道,有管道入口与管道出口两端
- 两端均需与其他实体进行信息传递,所以两端也就都存在选择pull还是push的问题
- 因此如果把入口/出口看作两个维度,把pull/push看作每个维度的定义域,我们可以画出一个平面坐标轴,四个象限,即
^
| OUT
|
| p
Data Pump | u Data Queue
IN.pull OUT.push | s IN.push OUT.push
Flume | h Rabbitmq
|
|
+--------------------------------------->
pull | push IN
| p
Null | u Data River
IN.pull OUT.pull | l IN.push OUT.pull
Flume + Kafka | l Kafka
|
+
- 上图是用ASCIIFlow画的图,中文导出后汉字更宽图会乱,所以用了英文
分析
- 下面的分析都是围绕上一小节的平面坐标系中的四个象限
- 每个象限中举例的开源项目与所处象限的关系是
- 该场景为该项目的最典型使用场景
- 众多数据管道项目都是跨象限的,但一定有它自己的主场
- 就像NUC也具有了相当的移动性、便携性,但仍改变不了他是台式机
Data Pump
- 中文直译数据泵,同样顾名思义
- 水泵是用来把水从一个水池抽到另一个水池里的
- 数据泵是用来把信息/事件从一个数据池(sink)抽到另一个数据池里的
- 举例
- logstash监控并读取日志文件的末尾,并将其送入elasticsearch
- 此时不断增长的日志文件是数据池,elasticsearch作为搜索数据库同样也是数据池
- flume依次读取hdfs中的文件,并将其送入mongodb
- 此时hdfs作为分布式文件系统是数据池,同时mongodb作为文档nosql数据库也是数据池
- 分析
- 入口使用pull模型,对信源有更高的要求(存储缓存等)
- 出口使用push模型,对信宿有更高的要求
- 所以数据泵是一个小机灵鬼,把更为复杂的工作都交给了与之通信的实体
- 他的工作是把信息从一个地方读出,再存到另一个地方,数据泵名副其实
- 因为数据泵的本质工作是读取并传递,所以绝大部分来说数据泵都会有插件机制
- 通过source和sink的插件设计,数据泵能够灵活与各类数据池交互,也能够广泛的借用开源社区的力量
- 当然这也方便了我们对其进行二次开发,一举多得
- 场景
- 使用数据泵最常用的场景,就是数据收集
- 一旦收集到数据,就立马发送给下一个实体
- 虽然flume、fluentd等数据管道同样提供了基于内存的或者基于硬盘的数据缓存/队列能力
- 但我们在这里有意的进行忽略,主要关注其本质的部分
Data Queue
- 中文直译消息队列
- 下了飞机我们等机场大巴的时候,会在大巴车门口排起长队 ,大巴接满了一车的人开走了,队伍中的人就会一起等下一班车过来,队列用来应对随着航班降落而产生的人流突发性的增长,同时能保证秩序和公平
- 同样在消息队列中,消息队列被动的接收信源端发送过来的事件/数据并在本地缓存,同时尽可能快的推送给事件的消费端,在事件被消费过后就丢弃此事件相关信息
- 举例
- 使用rabbitmq消息队列异步调用远程服务rpc,在openstack各项目中使用的非常多
- 监控服务产生了告警将其送入消息队列,消息经过路由以及复制后通过短信邮件IM等多种方式通知出去
- 分析
- 入口使用push模型,对自己有着更高的要求(存储缓存等)
- 出口使用push模型,对信宿有更高的要求
- 消息队列与数据泵相比,在入口处提升了对自己消息排队缓存能力的要求,队列的名称也就从此而来
- 在队列中除了简单的传递,也能够进行路由、复制、过滤等复杂的逻辑
- 但消息队列还是希望尽快的将消息传递到信宿(消费)端,从而降低自己的资源负载
- 若此时消费者端已经过载,则消息会产生积压甚至丢失的情况
- 场景
- 最常用的场景就是发布订阅
- 消息发送者将消息发送给队列,队列将消息推送给所有的订阅者
- 虽然一般对消息队列的讨论对比都包括了出口处的push/pull模型,但此处依旧选择性忽略,关注其更本质的部分
Data River
- 中文直译数据河流(我瞎起的)
- 管道与河流同样是用于传输水资源的,但是河流的蓄水能力要比管道强的多得多,从而对洪峰的容忍能力也要高出N个量级
- 在这个象限业界只有一个公认的标杆,即Kafka
- 举例
- 通过flume采集的数据先全部送入kafka存储削峰,再由另一个flume读取kafka中的消息写入hbase等持久化存储
- 在OLAP的kappa架构中,一切原始数据、中间结果、最终结果都只落地Kafka,既做队列,又做存储
- 分析
- 入口使用push模型,对自己有着更高的要求(存储缓存等)
- 出口使用pull模型,对自己同样有着更高的要求
- 为了做到这么高的要求,kafka通过利用操作系统文件缓存、硬盘顺序读写等提高了读写性能
- kafka通过topic分区、备份等方式提高了系统的可扩展性和可靠性
- kafka支持消息的永不删除策略,这也是与消息队列和核心区别之一,也是kafka能临时顶起来当数据库的基础
- 可以看到kafka把累活难活全部留给了自己,这就是kafka牛逼的地方
- 我想这也是kafka存在于每一个大数据平台方案中的原因,真滴瑞斯拜
- 场景
- 所有需要做消息削峰、高性能持久化缓存的地方,都会使用kafka
- 因为kafka已经做好了最难的部分,入口push、出口pull也只需要别人适配自己,保持自己代码的简洁性
- 同时kafka与其他系统的对接问题,有众多的数据泵以及kafka connector来做,他们生来就是做这个的
Null
- 第三象限,即左下角的象限,处于一个比较尴尬的地位
- 入口使用pull模型,对信源有更高的要求
- 出口使用pull模型,对自己有更高的要求
- 然而信宿pull谁都是pull,为什么不去直接pull信源咧?
- 要说是为了利用此象限项目的信源接口适配丰富性,以及缓存能力
- 那么这种需求也会被(pull)flume(push) + (push)kafka(pull) = pull pull 完美替代
- 我目前也没有看到专门为此象限而生的项目,自然也就没有名字了
Finally
- 当可以用一个坐标系去描述当前数据管道纷繁生态中的每个项目后
- 只要我们好好考虑一下当前需要的是数据泵,还是消息队列 or kafka
- 就能够非常方便的去进入对应的领域进行选型了
- 而在这个时候网上的各类实现细节、性能跑分对比,对我们来说也就更有价值了
- 坐标系让我有了选型的底气,因为数据管道再怎么样,也逃不出这张图