这个 Kafka 的专题,我会从系统总体架构,设计到代码落地。和你们一块儿杠源码,学技巧,涨知识。但愿你们持续关注一块儿见证成长!
我相信:技术的道路,十年如一日!十年磨一剑!
Kafka 是一种分布式的,基于发布 / 订阅的消息系统。最初被 LinkedIn 开发,并在 2011 年初开源,2012 年 10 月从 Apache 孵化器破壳而出,成为 Apache 的顶级项目。数据库
Kafka 最初被设计的目的是 LinkedIn 流量和运维数据分析。流量数据包含 PV (Page View) , UV (Unique Visitor) ,搜索数据,详情页数据等。在高并发场景对于这些数据的统计并不是实时的,不是简单的对于数据库的某个字段数据量 +1 这么简单,超大的流量洪峰下并不能由于统计数据将业务主流程阻塞。因此一般会将这些数据记录在文件或大数据存储引擎中,而后周期性的进行统计分析。设计模式
Kafka 被愈来愈多的公司青睐主要和他的特性优点有关:缓存
读
和 写
数据,而且对于数据进行压缩保证高吞吐那为什么须要使用消息队列,或者说在什么场景下 Kafka 更加合适服务器
在大数据,高并发的场景下为了突破性能瓶颈会对系统进行水平扩展和垂直拆分,将一个复杂的系统拆分多个独立,纯净的子系统。数据在各个系统之间流转,可是若是某一个服务处理速度过慢,就会拖累整个链路的性能,造成瓶颈下降整个系统的性能,形成“旱的旱死涝的涝死”的局面。架构
举个简单例子:在淘宝下单时,交易系统完成扣款,后续会有不少动做:提醒卖家发货,生成卖家工做流,核销优惠券,增长购物积分等等,若是这一步所有写到交易系统的扣款代码以后,颇有可能交易系统就会被拖死,下游任何一个环节失败也会致使扣款回滚,而且若是须要添加一个新的动做须要交易去作大量修改,设计确定是不合理的。实际上交易系统在处理完扣款后会发送一个扣款完成消息,下游接这个消息便可,下游失败不会影响核心流程失败,而且各个系统的边界更加清楚,分层更更加合理。并发
现在的应用程序基本都会涉及到多个系统之间的对接,数据在系统之间经过 RPC 进行传递,处理数据的过程失败就会致使数据丢失,除非数据被持久化到磁盘上。而 Kafka 将全部须要流转的数据都 持久化到磁盘上
,保证数据不会丢失。另外还有一个很重要的能力就是保留现场便于后续问题排查跟踪,经历过系统失败可是没法复现的人才会体会到的痛!运维
为了保证磁盘上的数据不会爆炸式疯涨,Kafka 提供了数据清理,数据压缩等功能,清除处理完成的历史数据。分布式
在应用的访问量剧增的状况下,代码优化每每没有直接进行水平扩展来的那么及时。诊断,分析,方案,优化,验证 一系列复杂流程让代码优化看起来只能是一个从长计议的方案。这时止血的方案只能是降级,限流,扩机器 三板斧。Kafka 的扩展性主要就体如今能热扩容,不须要修改参数,不须要修改代码,上机器 -> 注册服务 就完成了扩容。并不是全部系统都具有这个像 调节音量旋钮同样简单的提升系统性能
的能力 ,这里会涉及到扩容以前的数据是否会有热点,新节点对集群的同步,流量重分配等等一系列复杂流程。高并发
系统的部分组件失败不会影响这个系统的运行,消息队列下降了进程间的耦合度,上游或者下游服务挂掉后不会影响其余系统的运行,在服务从新在线后可以继续处理以前未处理的数据,只是会存在必定的延时可是可以保证 最终业务正确性
。性能
强哥:你这瓜保熟吗?哦不,你这队列保序吗?
在大多数场景下,数据处理顺序是相当重要的,顺序错乱极可能致使数据结果错误。除非这个处理过程是无状态的,此时消息只是起到事件触发的做用,触发下游进行计算。Kafka 能够保证分区内部有序而不能保证全局有序。
上图是一个典型的 Kafka 架构图,左边为消息生产者(Producer) ,发送消息到一个特定的主题(Topic),因为 Kafka 的分布式设计每一个 Topic 被分红多个分区,所以发送到每一个 Topic 的消息会被存储到对应的分区。另外若是 Topic 设置了副本,则每一个分区都会有对应的副本。这些 Topic 被不一样的消费者(Consumer)订阅,若是两个消费者在同一个消费者组,那么里面的消费者只能订阅一个固定的分区。
用上图的 Topic A 举例, Producer 1 发送消息到 Topic-A ,消息会在存放在 Broker-2 和 Broker-3 的两个分区上,而且因为 Topic-A 开启了分区备份,因此每一个分区都会由另一个节点 Topic-A' 备份分区数据 。发送到 Broker 的数据会被消费者订阅,因为 Consumer-1 和 Consumer-2 在同一个消费者组中,他们只能消费一个固定分区的消息, Consumer-1 只会接收到 Topic-A Partition-1 的消息,Consumer-2 只会接收到 Topic-A Partition-0 的消息。
在 Kafka 集群中的一个 Kafka Server 就是一个 Broker ,生产者将消息投递到 Broker ,Broker 保证消息的 持久化,容灾,准确性等。同时接受消费者的消息订阅,向消费者分发消息。通常来讲在生产环境一台 Kafka 服务器就是一个 Broker。
Topic 能够认为是用来存储消息的逻辑概念,可简单认为他是一个 信箱
。每条消息发送的时候都须要指定须要发送到哪一个 Topic ,消息被消费的时候也须要指定消费哪一个 Topic 中的消息。
Kafka 为了提升可扩展性以及吞吐量,Topic 被分红多个分区 (Partition) ,每一个 Partition 对应一个 Log,Log 是一个逻辑概念, 它会对应服务器上一个文件夹,这个文件夹下存放的是这个 Partition 下全部的消息数据和消息索引 。在面对海量数据的时候,为了不出现巨大文件出现 I/O 瓶颈,Kafka 又将 Log 分为多个 Segment 。每一个 Segment 包含 log 文件
和 index 文件
文件命名是以该 Segment 第一条消息的 offset 命名。这样说下来其实仍是很绕的直接看下面的架构图,能够仔细留意一下各个部分的标识和数字再结合这段文字,理解起来应该就很轻松了。
另外由于 Kafka 采用顺序 I/O,顺序 I/O 效率很是高,甚至比随机写内存效率更高,这也是 Kafka 高性能的缘由之一。
在生产环境中,咱们通常会开启 Kafka 消息冗余特性,每一个 Partition 都有 1 个或多个副本,咱们称之为 Replication。当分区只有一个副本的时候,该分区数据只保留了一份。每一个分区副本都会选出一个 Leader , Leader 是全部读写请求的 “接口人”
,其他副本均为 Follower 。Follower 做用有两个:拉取 Leader 的 Log 数据作 备份
,在 Leader 失败后做为候选人 参与 Leader 选举
。
消息产出的源头,经过必定的策略推送到 Topic 的各个分区 。这里所说的推送策略就是消息路由机制,Kafka 内置多种策略可选例如:按照消息 Key ,轮训等等,甚至用户能够写扩展代码来自定义路由策略。
消费者(Consumer)
主要工做是从 Broker 拉取消息,进行消费处理。每一个消费者维护本身的消费进度,这样的设计有诸多好处,好比:每一个消费者进度可以轻松的进行区分,而且能够修改单个消费者的消费位点跳过或者从新消费某些消息,避免了位点信息的集中化管理的单点故障问题。
如今的应用程序大部分为分布式的系统,一个应用有几十台上百台服务器,这些服务器上运行着相同的代码,那么一个消息过来,每台服务器都执行一次消费逻辑,岂不是会形成巨大的问题。
因此 Kafka 引入了一个新的概念: 消费者组(Consumer Group)
。咱们能够将这些应用的服务器都放到同一个消费者组中,而 Kafka 规定一条消息只能被同一个消费者组中的一个消费者消费,这样就能完美避免分布式状况下的重复消费问题了。上面所说的状况简单来讲是但愿实现消息被某台服务器独占,也就是 单播
问题。假如咱们但愿这条消息被广播出去,每台收到这个消息的服务器都作处理,例如发消息作日志清理,这种状况称为 广播
, 那咱们只须要将每一个消费者放到不一样的消费者组便可。
Kafka 引入消费者组的概念巧妙解决了单播和广播问题,而没有区分订阅类型,经过一种逻辑概念来屏蔽掉多种订阅实现。
另外在同一个消费者组中的消费者订阅的分区是肯定的,只有在消费者组中的消费者有变化的时候才会进行重分配。例如咱们有四个分区,三个消费者,就会出现一个消费者订阅两个分区的状况。而三个分区四个消费者就会出现有消费者处于空闲状态,形成浪费,因此通常消费者的数量尽可能不要大于 Topic 的分区数。
这是我 2021 年的第一篇博客,年末作回顾的时候才知道去年我过的究竟有多么糟糕。既没有输入也没有输出,虽然工做进入一个新的阶段,会愈来愈忙,但忙不是拒绝成长的借口,必须保证每个月一到两本书的输入,一到两周输出一篇优质文章。 最长的路属于一颗孤独的心,与君共勉
下期我会从总体梳理 Kafka 生产者,包括消息发送客户端,发送端数据缓存从源码角度看看其中的设计模式,代码组织技巧。