Kafka消息队列的学习 | 青训营笔记

139 阅读7分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记。学习了消息队列在项目中的应用,以及Kafka的基本知识。

一、什么是消息队列?

1.简介

消息队列(MQ),指保存消息的一个容器,本质是一个队列。消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削峰等问题,实现高性能,高可用,可伸缩和最终一致性架构。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ。

2.应用场景

2.1应用解耦

2.2异步提速

2.3削峰填谷

劣势:系统可用性降低—系统引入的外部依赖越多,系统稳定性越差;

系统复杂性提高—异步调用

一致性问题

3.常见的消息队列

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

RocketMQ:实时场景中运用较广

二、Kafka

1.简介

kafka是一个分布式消息队列。Kafka对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群由多个kafka实例组成,每个实例(server)称为broker。

2.使用场景

日志收集:某公司可以用kafka收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop

消息系统:解耦生产者和消费者、缓存消息等

用户活动追踪:kafka可以被用来记录web用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。

运营指标:kafka也经常用来记录运营监控数据。包括收集各种分布式的应用数据,生产各种操作中的几种反馈,比如报警和报告。

3.架构

Broker:消息中间处理结点,一个kafka结点就是一个broker,一个或者多个broker可以组成一个kafka集群

Producer:消息生产者,就是向kafka broker发消息的客户端

Consumer :消息消费者,向kafka broker取消息的客户端

Consumer Group(CG):消费者组,每个Consumer属于一个特定的Consumer Group,一条消息可以被多个不同的CG消费,但是一个 CG中只能有一个Consumer消费该消息。依据此,kafka可以实现消息的广播(发给所有的consumer)和单播 (发给任意一个consumer)。一个topic可以有多个CG。topic的消息会复制(不是真的复制,是概念上的)到所 有的CG,但每个partion只会把消息发给该CG中的一个consumer。如果需要实现广播,只要每个consumer有 一个独立的CG就可以了。要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的 分组而不需要多次发送消息到不同的topic;

Topic :逻辑队列,可以理解成每一个不同的业务场景就是一个不同的topic,对于这个业务来说,所有的数据都存储在这个topic中

Partition:通常topic中会有多个分片,不同分片之间的消息是可以并发来处理的,这样可以提高单个topic的吞吐,partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序;

Offset:kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找;对于每一个Partition来说,每一条消息都有一个唯一的Offset,消息在partition内的相对位置信息,并且严格递增

Cluster:kafka的物理集群,每个集群可以新建多个不同的topic

Replica:分片的副本,分布在不同机器上,可用来容灾;Leader对外服务,Follower异步去拉取leader的数据进行同步,如果leader挂了,可以将Follower提升成leader再对外进行服务

follower:leader处理所有针对这个partition的读写请求,⽽follower被动复制leader,不提供读写(主要是为了保证多副本数据与消费的⼀致性),如果leader所在的broker挂掉,那么就会进⾏新leader的选举

ISR:同步中的副本,对于follower来说,始终和leader是有一定差距的,但当这个差距较小的时候,就可以将这个follower存放在ISR集合中;如果ISR中的节点服务器性能差,会被踢出ISR集合

ZooKeeper:在集群的基础上,还有一个模块zookeeper,存储了集群的元数据信息,比如副本的分配信息等

4.为什么kafka能支持高吞吐?

从一条消息的角度

Producer:批量发送,可以减少I/O次数,从而加强发送能力

通过压缩,减少消息大小,目前支持Snappy(默认)、Gzip、LZ4、ZSTD压缩算法

Broker:将消息的副本以日志(log)的方式写入磁盘;

写入数据时,移动磁头到对应磁道,磁盘转动,找到对应扇区,最后写入,寻到寻道成本较高,因此可以用顺序写来减少时间成本

读数据的时候,偏移量索引文件,分两步,第一步:通过二分找到小于目标文件的最大文件;第二步:通过二分找到小于目标 offset最大的索引位置,再遍历找到offset。

零拷贝:传统数据拷贝过程需要三次内存拷贝。Consumer从Broker读取数据时,通过sendfile系统调用的方式,将磁盘读取到os内核缓冲区后,直接转到socket buffer进行网络发送;Producer生产的数据持久化到Broker,采用mmap文件映射,实现顺序的快速写入。

Consumer:如何解决在一个Consumer Group中,分配哪一个Consumer消费Partition的问题?手动分配和自动分配

1.手动分配,也就是Low Level的的消费方式,优点是启动较快,在写代码的时候就写好了;缺点是某个Consumer挂掉之后会被迫停掉整个集群;对扩展Consumer也不友好。

2.自动分配,也叫High Level的消费方式。简单来说,就是在Broker集群中,对于不同的Consumer Group来说,都会选取一台Broker当做Coordinator,其作用就是帮助Consumer Group进行Partition的分配,也叫做分片的rebalance。这种方式下,如果有Consumer发生宕机,或者有新的Consumer加入,真个partition和consumer都会重新进行分配来达到一个稳定的状态。

5.重启操作

kafka实现高可用:每一个Broker上都有不同topic分区的不同副本,每一个副本会将其数据存储到该kafka结点上面,对于不同的结点之间,通过副本直接的数据复制,来保证数据的最终一致性和集群的高可用。

重启:leader切换,追赶数据——数据同步完成——leader回切

6.替换、扩容、缩容

如果是替换,本质上来讲就是一个需要追更多数据的重启操作,因为正常重启只需要追一小部分,替换,则是需要复制整个leader的数据,时间更长。扩容也是要从0开始复制一些新的副本。

缩容,缩容结点上面的分片会从零开始复制到集群里其他机器上,时间成本较高。

所以,kafka运维的时间成本不容忽视。

7.负载不均衡

当出现集群中负载不均衡时,解决问题会出现数据复制的问题,就又会导致高负载结点更I/O高,所以问题变成了,为了去解决I/O升高的问题,带来了更高的I/O,所以就需要权衡I/O设计出一个及其复杂的负载均衡策略。

8.其余问题

kafka没有自己的缓存,完全依赖于Page Cache,灵活性较低;

Controller和Coordinator和Broker在同一进程中,大量I/O会造成其性能下降。