这个 Kafka 的专题,我会从系统整体架构,设计到代码落地。和大家一起杠源码,学技巧,涨知识。希望大家持续关注一起见证成长!
我相信:技术的道路,十年如一日!十年磨一剑!
Kafka 是一种分布式的,基于发布 / 订阅的消息系统。最初被 LinkedIn 开发,并在 2011 年初开源,2012 年 10 月从 Apache 孵化器破壳而出,成为 Apache 的顶级项目。
Kafka 最初被设计的目的是 LinkedIn 流量和运维数据分析。流量数据包含 PV (Page View) , UV (Unique Visitor) ,搜索数据,详情页数据等。在高并发场景对于这些数据的统计并非实时的,不是简单的对于数据库的某个字段数据量 +1 这么简单,超大的流量洪峰下并不能因为统计数据将业务主流程阻塞。所以通常会将这些数据记录在文件或大数据存储引擎中,然后周期性的进行统计分析。
Kafka 被越来越多的公司青睐主要和他的特性优势有关:
以 O(1) 时间复杂度消息持久化,对于 TB 级别的数据也能够保证 O(1) 的访问效率
支持批量 读 和 写 数据,并且对于数据进行压缩保证高吞吐
支持消息分区,分布式发送,分布式消费,便于水平扩展 (Scale out),具有很高的并发能力
应用场景那为何需要使用消息队列,或者说在什么场景下 Kafka 更加合适
解耦在大数据,高并发的场景下为了突破性能瓶颈会对系统进行水平扩展和垂直拆分,将一个复杂的系统拆分多个独立,纯净的子系统。数据在各个系统之间流转,但是如果某一个服务处理速度过慢,就会拖累整个链路的性能,形成瓶颈降低整个系统的性能,造成“旱的旱死涝的涝死”的局面。
举个简单例子:在淘宝下单时,交易系统完成扣款,后续会有很多动作:提醒卖家发货,生成卖家工作流,核销优惠券,增加购物积分等等,如果这一步全部写到交易系统的扣款代码之后,很有可能交易系统就会被拖死,下游任何一个环节失败也会导致扣款回滚,并且如果需要添加一个新的动作需要交易去做大量修改,设计肯定是不合理的。实际上交易系统在处理完扣款后会发送一个扣款完成消息,下游接这个消息即可,下游失败不会影响核心流程失败,并且各个系统的边界更加清楚,分层更更加合理。
数据持久化如今的应用程序基本都会涉及到多个系统之间的对接,数据在系统之间通过 RPC 进行传递,处理数据的过程失败就会导致数据丢失,除非数据被持久化到磁盘上。而 Kafka 将所有需要流转的数据都 持久化到磁盘上 ,保证数据不会丢失。另外还有一个很重要的能力就是保留现场便于后续问题排查跟踪,经历过系统失败但是无法复现的人才会体会到的痛!
为了保证磁盘上的数据不会爆炸式疯涨,Kafka 提供了数据清理,数据压缩等功能,清除处理完成的历史数据。
扩展性在应用的访问量剧增的情况下,代码优化往往没有直接进行水平扩展来的那么及时。诊断,分析,方案,优化,验证 一系列复杂流程让代码优化看起来只能是一个从长计议的方案。这时止血的方案只能是降级,限流,扩机器 三板斧。Kafka 的扩展性主要就体现在能热扩容,不需要修改参数,不需要修改代码,上机器 -> 注册服务 就完成了扩容。并非所有系统都具备这个像 调节音量旋钮一样简单的提高系统性能 的能力 ,这里会涉及到扩容之前的数据是否会有热点,新节点对集群的同步,流量重分配等等一系列复杂流程。
容灾系统的部分组件失败不会影响这个系统的运行,消息队列降低了进程间的耦合度,上游或者下游服务挂掉后不会影响其他系统的运行,在服务重新在线后能够继续处理之前未处理的数据,只是会存在一定的延时但是能够保证 最终业务正确性 。
保序强哥:你这瓜保熟吗?哦不,你这队列保序吗?
在大多数场景下,数据处理顺序是至关重要的,顺序错乱很可能导致数据结果错误。除非这个处理过程是无状态的,此时消息只是起到事件触发的作用,触发下游进行计算。Kafka 可以保证分区内部有序而不能保证全局有序。
上图是一个典型的 Kafka 架构图,左边为消息生产者(Producer) ,发送消息到一个特定的主题(Topic),由于 Kafka 的分布式设计每个 Topic 被分成多个分区,因此发送到每个 Topic 的消息会被存储到对应的分区。另外如果 Topic 设置了副本,则每个分区都会有对应的副本。这些 Topic 被不同的消费者(Consumer)订阅,如果两个消费者在同一个消费者组,那么里面的消费者只能订阅一个固定的分区。