什么是消息队列?
消息队列(MQ),指保存消息的一个容器,本质是个队列,但这个队列,需要支持高吞吐,高并发,并且高可用。
消息队列发展历程
业界消息队列对比
kafka:分布式的、分区的、多副本的日志提交服务,在高吞吐场景下发挥较为出色。
RocketMQ:阿里自研的,低延迟、强一致、高性能、高可靠、万亿级容量和灵活的可扩展性,在一些实际场景中运用较广。
Pulsar:目前是腾讯在用,是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体、采用存算分离的框架设计。
BMQ:和Pulsar框架类似,存算分离,初期定位是承接高吞吐的离线业务场景,逐步替换掉对应的Kafka集群。
kafka
使用场景
一般用于离线的消息处理当中,如:日志信息、Metrice 数据、用户行为
日志信息: 各种各样的日志信息会先发到 kafka 中进行处理,然后在由 kafka 的各种下游对日志进行分析或者处理。
Metrice 数据:程序在运行当中对程序状态的采集,如QBS、程序查询写入时的一些耗时、通过曲线去评判我当前程序是否处于要给健康的状态。
用户行为:搜索、点赞、评论、收藏等。
如何使用 kafka
创建集群 -> 新增Topic并且设置好分区数量 -> 引入kafka的SDK编写生产者逻辑 -> 编写消费者逻辑
基本概念
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缺点
重启操作
当我们需要重启的 Broker1 上面有一个Leader,那么当我们重启这个 Broker1 之后他会从副本当中重新选取一个 Broker2 将 Folllower 作为 Leader 来对外进行服务。这时,整个集群当中输入的写入并没有停止,
那么当我们重启完成周后,这两个 Broker 之间的数据便会产生一定的差异,那么 Broker1 在重启之后首要任任务便是要去进行数据的同步,
当系统认为数据已经同步完成之后,会进行一个 Leader 的回切,回切的目的是为了解决负载均衡的问题。
重启只能单个进行,时间成本巨大。
替换、扩容、缩容
一样需要重新进行数据同步,会产生数据复制的问题。
kafka 集群不管任何的运维操作,只要有节点的变动,都会有数据复制所带来的时间成本问题,而时间成本又会造成运维的成本很高。\
负载不均衡
将 Partition3 迁移到 Broker2 上面
我们迁移 Partition3 是为了降低 所在机器(Broker)的 IO 负责,而 Partition3 的迁移又会产生数据复制的问题,从而造成 IO 的负载升高(为了解决 IO 文件要引入一个新的 IO 问题),所有这里需要去权衡 两台机器的 IO 去设计一个极其负责的负载均衡策略,而设计这样一个策略的成本也会很高,也会提高运维的成本。
问题总结
1、运维成本高
2、对于负载不均衡的场景,解决方案复杂
2、没有自己的缓存,完全依赖 Page Cache
2、Controller 和 Condinator 和 Broker 在同一进程中,大量的 IO 会造成其性能下降。