消息中间件 - kafka | 青训营笔记

113 阅读6分钟

什么是消息队列?

消息队列(MQ),指保存消息的一个容器,本质是个队列,但这个队列,需要支持高吞吐,高并发,并且高可用。

消息队列发展历程

业界消息队列对比

kafka:分布式的、分区的、多副本的日志提交服务,在高吞吐场景下发挥较为出色。

RocketMQ:阿里自研的,低延迟、强一致、高性能、高可靠、万亿级容量和灵活的可扩展性,在一些实际场景中运用较广。

Pulsar:目前是腾讯在用,是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体、采用存算分离的框架设计。

BMQ:和Pulsar框架类似,存算分离,初期定位是承接高吞吐的离线业务场景,逐步替换掉对应的Kafka集群。

kafka

使用场景

一般用于离线的消息处理当中,如:日志信息、Metrice 数据、用户行为

日志信息: 各种各样的日志信息会先发到 kafka 中进行处理,然后在由 kafka 的各种下游对日志进行分析或者处理。

Metrice 数据:程序在运行当中对程序状态的采集,如QBS、程序查询写入时的一些耗时、通过曲线去评判我当前程序是否处于要给健康的状态。

用户行为:搜索、点赞、评论、收藏等。

如何使用 kafka

创建集群 -> 新增Topic并且设置好分区数量 -> 引入kafka的SDK编写生产者逻辑 -> 编写消费者逻辑

image.png

基本概念

Topic:逻辑队列,不同 Topic 可以建立不同的 Topic

可以理解每个不同的业务场景,一个业务场景就是一个 Topic,对于当前的业务来说,所有的数据都存储在这个 Topic 当中

Partition:Topic 中分区的概念 不同的分区,消息可以进行并发处理

Cluster:物理集群,每个集群中可以建立多个不同的 Topic

Producer:生产者,负责将业务消息发送到 Topic 中

Consumer:消费者,负责消费 Topic 中的消息

ConsumerGroup:消费者组,不同组 Consumer 消费进度互不干涉

Offset

每个partition内部都会存储不同的消息,每个消息都有唯一的 Offset ,Offset在Partition是递增的。

Replica

每个副本(Replica)有不同的角色
ISR: 是 Kafka 为某个分区维护的一组同步集合,即每个分区都有自己的一个 ISR 集合,处于 ISR 集合中的副本,意味着 follower 副本与 leader 副本保持同步状态,当两个副本差距过大时候,就会被踢出副本。只有处于 ISR 集合中的副本才有资格被选举为 leader。一条 Kafka 消息,只有被 ISR 中的副本都接收到,才被视为“已同步”状态。现在是通过时间进行判断。
好处: 比如我目前的 Leader所在的机器 宕机了,我可以从 ISR 当中选择一个副本让他成为 leader 让他继续去为生产者和消费者进行服务。

数据复制

每个 Broker 代表 fafka 中的一个节点,所有的节点聚合在一起就是集群。
其中第二个 Broker扮演者一个 Controller 的角色,负责对集群当中的所有副本以及 Broker 进行分配。
图中所有的 Topic-Partiton 所在的位置都是由 Controller 计算出来的,然后再告诉 Broker 该如何进行处理。

Kafka 架构

ZooKeeper:与controller配合去存储一些集群的元信息,包括分区分配信息等。 的元素级信息等等。

一条消息的自述

从一条消息的视角,看看为什么 kafka 能支撑那么高的吞吐?

Producer

批量发送

数据压缩

Broker

数据的存储

消息文件结构

在一个 Broker 中存放着一些 partition 的一些副本,最终是以日志的形式写入磁盘,对于某一条日志,他会有序的切成多个日志段(LogSegment),每个日志段包含四个文件(.log、.index、.timeindex、其他文件)

磁盘结构

顺序写入

如何找到消息

偏移量索引文件

里依旧通过二分法找到 26 ,根据 838找到 26 ,然后便利找出 28

时间戳索引文件

通过时间戳找到对应的 offset,然后在找到最终数据,与上面的类似

零拷贝

Consumer

消息的接收

手动分配

每个 Consumer 其实是一条进程,写入的时候就告诉他要拉取谁谁谁。(告诉Consumer1要拉去Partitin1, 2, 3,等。)
缺点:
1、比如我让 Consumer3拉取Partiton7,8,而 Consuerm3 挂掉了,那么 Partiton7,8 他们的数据流就会直接断掉了。
2、如果想要新添加一个 Consumer4 那么为了让他去拉分片,那么就要去把 Consumer1 2停掉,然后将要拉取的分片让出来,然后再启动等问题。

自动分配 - High Level

Conrdinater 去帮助每个 Gcomsumer Group 中的 Consumer 去自动分配 Partiton(Rebalance)。
当 Consumer3 宕机后,Coordinatror 会感知到这个Consumer3不在 Group 内部了,它就会将 Consumer3 踢掉,将3中的 partiton7,8 分配给 Consumer1,2 去进行处理。
当新添加 Consumer4 后,同样的 Conrdinater 也会感知到有新的成员加入,那么他就会重新计算(如:Consumer1 上有多出来的,3 6 ),将多出来的 Partition 分配给 Consumer4 去进行处理,以此来让 Consumer 来达到一个稳定的状态。

Rebalance

kafka缺点

重启操作

image.png 当我们需要重启的 Broker1 上面有一个Leader,那么当我们重启这个 Broker1 之后他会从副本当中重新选取一个 Broker2 将 Folllower 作为 Leader 来对外进行服务。这时,整个集群当中输入的写入并没有停止,
那么当我们重启完成周后,这两个 Broker 之间的数据便会产生一定的差异,那么 Broker1 在重启之后首要任任务便是要去进行数据的同步,
当系统认为数据已经同步完成之后,会进行一个 Leader 的回切,回切的目的是为了解决负载均衡的问题。
重启只能单个进行,时间成本巨大。

替换、扩容、缩容

image.png 一样需要重新进行数据同步,会产生数据复制的问题。
kafka 集群不管任何的运维操作,只要有节点的变动,都会有数据复制所带来的时间成本问题,而时间成本又会造成运维的成本很高。\

负载不均衡

image.png 将 Partition3 迁移到 Broker2 上面 image.png 我们迁移 Partition3 是为了降低 所在机器(Broker)的 IO 负责,而 Partition3 的迁移又会产生数据复制的问题,从而造成 IO 的负载升高(为了解决 IO 文件要引入一个新的 IO 问题),所有这里需要去权衡 两台机器的 IO 去设计一个极其负责的负载均衡策略,而设计这样一个策略的成本也会很高,也会提高运维的成本。

问题总结

1、运维成本高
2、对于负载不均衡的场景,解决方案复杂
2、没有自己的缓存,完全依赖 Page Cache
2、Controller 和 Condinator 和 Broker 在同一进程中,大量的 IO 会造成其性能下降。