Apache Kafka实战-认识Apache Kafka
前言
最近读完了 DDIA 前五章,回过头来在看 Apache Kafka 实战,很多东西都是不谋而合的,从中也可以看出来很多分布式处理框架在解决自身消息编码设计,复制分区故障转移的方法思维都是差不多的,只是根据各自主攻的场景选择更合适的解决方案。
Kafka
Kafka 是一个消息流处理引擎并支持实时流处理。Kafka 将消息以 topic 为单位进行归纳,将向 Kafka topic 发送消息的程序称为 producer,将订阅 topic 消息的程序称为 consumer。Kafka 以集群的方式运行,由一个或者多个服务组成,每个服务被称作 broker。producer 通过网络向 kafka 集群发送消息,consumer 通过 poll 的方式向 kafka 集群订阅消息。
Kafka 并不只是单纯的消息队列,其实所有的分布式处理框架相对于传统的处理框架都有高可靠,高容错,易于伸缩的特性。Kafka 是怎么实现这些特性的呢?
Kafka 经常用作接收实时数据流,应用解耦合,流量削峰,如何保证 Kafak 集群的高效运行呢?
在数据处理过程中,我们往往作为数据下游消费者,如何编写一个高效的 consumer 呢?Kafka 与其他大数据处理框架(比如 Spark,Flink )是怎么集成的呢?
消息引擎系统
消息引擎系统定义了一组规范,用以在不同系统间传递语义的消息。根据上述定义也可以看出来消息引擎的两个重要因素
- 消息设计
- 传输协议设计
消息设计对应着 DDIA 中的编码与演化这章。计算机中数据的表现形式除了内存数据结构,就是字节序列。内存结构针对不同的场景更高效更快速,字节序列更多是为了存储压缩,还涉及到序列化与反序列化。常见的数据编码格式有 json,xml,csv,二进制字节序列。良好的消息设计对于系统不同组件间的网络传输速率及持久化过程中的磁盘IO,占用存储空间的大小影响很大。
Kafka 的消息设计是什么样子的呢?其采用了二进制字节序列,主要包括头部信息(校验位,时间戳等),key,value。
消息是静态的,需要双方之间传输才有意义,传输的方式叫作传输协议,可能我们听到过最多的就是 RPC。说的直白点,就是传递消息的规范(消息的接受,发送,失败重试,序列化,反序列化等),也可以与一些网络协议做对比。
Kakfa 自己设计了一套二进制传输协议。
Kakka 特性
高吞吐,低延时
吞吐量代表每秒能够处理的消息数或者字节数,是衡量系统性能的一个重要指标。看这个定义,我们肯定是希望吞吐量越高越好,但是正常环境中,不得不考虑延时。延时与吞吐量又什么联系呢。作者这里举了一个比较形象的例子.
- Kafka 处理一条消息需要 2ms,那么计算得到的吞吐量不会超过 500 条消息/秒(1000/2)
- 由一条条发送消息改为批量发送消息,假设发送消息前先等待 8ms,在 8ms 的时间里累积了 1000 条的数据,此时的处理延时为 (8+2)ms,吞吐量为 100000 条/秒(1000/0.01)
可以看到适当增加延时,采用微批处理,吞吐量提升了 200 倍。
Kafka 是如何做到高吞吐,低延时的呢?在数据持久化的过程中应该也用到了微批的思想。
- 大量使用操作系统页缓存,内存操作速度快且命中率高。
- Kafka 不直接参与物理 IO 操作,而是交由操作系统来完成。
- 采用追加写入方式,避免磁盘随机读写。ps: 这里是 DDIA 存储与检索章节的内容,追加写入方式的有点,如何检索,如何在其上建立高效的索引。LSM。
- 使用 sendfile 为代表的零拷贝技术加强网络间的数据传输效率,关于零拷贝,可以参考 zero_copy
总结一下就是,在良好的消息设计前提下,高吞吐可以通过微批的方式进行提高(但是要结合机器性能等因素处理好延时问题)。低延时体现在网络传输和处理性能上(包括数据持久化),针对这个问题,目前大部分分布式框架都采用了零拷贝和 LSM。
消息持久化
为什么需要消息持久化?
- 解耦合 -> Kafka ,也可以说是消息队列比较重要的功能就是应用解耦合,分离生产者与消费者的业务代码逻辑。生产者只管生产消息到 Kafka 服务器持久化,而不用关心消费者何时消费数据。
- 可靠性 -> 生产者数据的备份
- 重复消费 -> 正如新闻一样,一条消息会由很多人看,持久化便于下游的各个消费者消费同一批数据,这个可以了解消息引擎范型中的生产/订阅模型。
Kafka 是如何做到消息持久化的?追加写日志文件,不过方式有所不同,大多数消息持久化都是先放到内存缓冲区,达到阈值在溢写到磁盘。而 Kakfa 是直接写到磁盘,为什么这样做,这样做真的对整体性能有帮助吗?这个是后面的学习过程中需要思考与探讨的问题。
负载均衡和故障转移
与 DDIA 的第五章相对应,对于分布式数据系统,不得不考虑的问题是复制,是每台机器上都保持一模一样的数据呢,还是每台机器上保持不同的分区呢?是只有 leader 节点提供服务还是所有节点都提供服务?如果所有节点都提供服务,怎么保证每个节点上的数据一致性呢?当一台 leader 数据节点挂掉后,如何选举出来其他的节点保证服务呢?
Kafka 是每台节点上都保存一份数据,数据会进行分区,每个分区都有对应的 leader 和 replica。Kafka 通过某种算法选举每个分区的 leader,leader 较均匀的分布在每个节点上,由此实现负载均衡。
Kafka 依赖 zookeeper 实现故障转移,每个节点启动时都会向 zk 注册会话,当某个会话失效后,kafka 会自动选举出来其他服务器作为 leader 继续提供服务。
伸缩性
Kafka 适合水平伸缩,producer,consumer 与 broker 之间的状态全部由 zk 托管,当有新节点加入时,只需要修改配置并像 zk 注册就行。
Kafka 基本概念
topic 和 partition
topic 代指一类消息,由多个 partition 组成。partition 对消息分区。举个例子,商品订单数据可以看做 topic,日常用品订单,娱乐消费订单等可以看做 partition。分区的作用是为了便于查找。如果只有一个分区,我们为了找到娱乐消费订单数据可能还会轮询到很多其他类订单数据。
offset
每条消息的位移。在 producer 和 consumer 端有不同的含义。
- producer 端标识每个消息的位置
- consumer 端标识消费者读到了什么位置
<topic, partition, offset> 可以标识一条数据
replica
副本,冗余存储,保证高可靠。每个 replica 都有一个 leader 和若干个 follower,follower 会主动追随 leader 上的数据,如果该 follower 在 ISR 中,其还可以作为备用 leader。replica 肯定是分布在不同 broker 上的,否则无法实现备份冗余的效果。
leader 和 follower
replica 中涉及到的两个角色概念,只有 leader 对外提供服务,follower 只是被动同步 leader 并作为故障转移后的备选 leader。
ISR
in-sync replica。Kafka 为 partition 动态维护了一个 replica 集合,该集合中所有 replica 保存的消息日志都与 leader replica 保持同步状态,只有这个集合中的 replica 才能被选举成 leader,也只有该集合中的所有 replica 都接收到消息后,Kafka 才会将该条消息置为已提交状态。这个集合被称作 ISR。Kafka 只保证已提交状态消息不会丢失。
正常情况下,partition 下的所有 replica 都在 ISR 中。但在实际生产环境中,由于各种原因,一些 replica 跟不上 leader 就会被踢出 ISR。追上进度后,replica 还会被重新加入到 ISR。