Kafka 随笔

Kafka

一、MQ

Message Queue 简称 MQ ;消息队列,也称为消息中间件。

是基础数据结构中“先进先出”的一种数据结构。

一般用来解决:应用解耦、异步消息、流量削峰等问题,实现高性能、高可用、可伸缩和最终一致性架构。

MQ的技术维度

  • API 发送和接收
  • MQ 的高可用性
  • MQ 的集群和容错配置
  • MQ 的持久化
  • MQ 的延时发送 / 定时发送
  • 签收机制

MQ 消费消息的两种模式

点对点模式

一对一,消费者主动拉取数据,消息收到后消息清除

消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。

消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费,弊端明显。

这种模式中,消费的消息是直接从队列中进行消息的消费。

在这里插入图片描述

发布 / 订阅模式

一对多,消费者消费数据之后不会清除消息

消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。

这种模式中,消费的消息是直接从队列中的 “Topic” 主题中进行消息的消费。

在这里插入图片描述

二、Kafka

kafka是一个分布式的基于发布 / 订阅模式的消息队列,主要应用于大数据实时处理领域。

2.1 Kafka基础架构

2.2 kafka中的角色组成

(1)Producer :消息生产者,就是向kafka broker发消息的客户端;
(2)Consumer :消息消费者,向kafka broker取消息的客户端;
(3)Consumer Group (CG):消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
(4)Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。
(5)Topic :可以理解为一个队列,生产者和消费者面向的都是一个topic;
(6)Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列;
(7)Replica:副本,为保证集群中的某个节点发生故障时,该节点上的partition数据不丢失,且kafka仍然能够继续工作,kafka提供了副本机制,一个topic的每个分区都有若干个副本,一个leader和若干个follower。
(8)leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是leader。
(9)follower:每个分区多个副本中的“从”,实时从leader中同步数据,保持和leader数据的同步。leader发生故障时,某个follower会成为新的leader。

2.3 kafka架构的最基本的认识

一、
Kafka Cluster由多个Broker组成,每个broker都有自己唯一的ID。
在一个Broker内部可以有多个topic;
在一个topic中可以有多个分区;
一个分区可以有多个副本,副本分为leader 和 follow两个角色【1个leader + n个follower】。
二、
在Kafka中的消费者端:是以消费者组为单位对数据进行消费的。
从消费者角度出发:一个消费者组中的一个消费者可以消费一个topic主题中的多个分区;
从Topic角度出发:但是一个topic主题中的一个分区不可以被一个消费者组中的多个消费者消费【消费者组不可重复消费】。
三、
zookeeper的作用:
1、
①:0.9版本之前,offset偏移量维护在zookeeper中。
②:0.9版本之后,offset偏移量维护在kafka本地中。
Kafka中offset偏移量的维护就是维护3个部分:
把offset中的数据维护在topic中。
G:group       组
T:topic       主题
P:partition   分区
2、
kafka集群的工作是基于zookeeper的。
例如:controller的确定、topic的创建、分区的配置、副本的配置、leader的选举。

三、Kafka 快速入门

注意 ! 注意 ! !注意 ! !!

Kafka Cluster 的工作是基于zookeeper的,所以,我们在启动kafka之前,一定要先启动zookeeper !!!

3.1 Kafka 的集群部署

端口号:

kafka : 9092

zookeeper : 2181

hadoop01 hadoop02 hadoop03
zookeeper zookeeper zookeeper
kafka kafka kafka
  1. jar 包的下载

    http://kafka.apache.org/downloads
    
  2. 上传kafka到Linux系统并解压。

  3. 在kafka中,所有的数据(日志、topic等等 都是以log文件进行看待的。)

    我们在kafka根目录下创建一个目录(datas),用来存储topic主题。

    也可以把topic主题存储在kafka根目录下的logs目录中

  4. kafka的核心配置文件(config 下的server.properties文件中)

vim /opt/module/kafka/kafka_2.11-2.4.1/config/erver.properties

主要的配置信息有:

############################### 主要的配置项()##############################
#broker的全局唯一编号,不能重复,因为我们有3台机器,所以规定broker的序号是:1,2,3 
broker.id=1
#删除topic功能使能,kafka2.4版本默认为true,以前的老版本默认是false
delete.topic.enable=true
#kafka中topic主题存放的目录(注意不是kafka运行日志,运行入职存储在bin/kafka-run-class.sh中)
log.dirs=/opt/module/kafka/kafka_2.11-2.4.1/datas
#配置连接Zookeeper集群地址
zookeeper.connect=hadoop01:2181,hadoop02:2181,hadoop03:2181/kafka
########################## 非主要配置项(根据需要配置)#########################
#处理网络请求的线程数量
num.network.threads=3
#用来处理磁盘IO的现成数量
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
  1. 配置环境变量

    #KAFKA_HOME
    export KAFKA_HOME=/opt/module/kafka/kafka_2.11-2.4.1
    export PATH=$PATH:$KAFKA_HOME/bin
    
  2. 向其他两台机器分发kafka和环境变量

    注意 ! 注意 ! !注意 ! !!

    分发后,一定要修改配置文件中的broker.id,broker.id 不能重复 !!!

    hadoop02 : broker.id = 2;

    hadoop03 : broker.id = 3;

  3. 启动集群,首先启动zookeeper集群

  4. kafka群启脚本

    #! /bin/bash
    if [ $# -lt 1 ]
    then 
        echo "Input Args Is Can Not Empty !!!"
        exit
    fi
    
    # 循环遍历每一台机器,操作kafka
    for host in hadoop01 hadoop02 hadoop03
    do 
        case $1 in 
        "start")
            echo "================start $host Kafka============"
            ssh $host '/opt/module/kafka/kafka_2.11-2.4.1/bin/kafka-server-start.sh -daemon /opt/module/kafka/kafka_2.11-2.4.1/config/server.properties'
        ;;
        "stop")
            echo "================stop $host Kafka============"
            ssh $host '/opt/module/kafka/kafka_2.11-2.4.1/bin/kafka-server-stop.sh'
        ;;
        *)
            echo "Input Args Is Error !!!"
            exit
        ;;
        esac
    done
    
  5. 检验kafka是否正常启动的标准

    # 启动zookeeper的客户端工具
    [heather@hadoop01 ~]$ zkCli.sh
    # 查看zookeeper根目录下所有数据
    [zk: localhost:2181(CONNECTED) 5] ls /
    [hadoop-ha, hbase, kafka, zookeeper]
    # 查看broker中id状态,是否是全部的id
    [zk: localhost:2181(CONNECTED) 7] ls /kafka/brokers/ids
    [1, 2, 3]
    

3.2 命令行操作

3.2.1 Topic 主题

Topic 主题是逻辑单位,真正在存储数据的时候是以分区为单位进行的,分区是物理单位。

  1. 查看所有的topic 主题

    kafka-topics.sh --list --bootstrap-server hadoop01:9092
    
  2. 创建topic 主题(同时给主题设置为2个分区,3个副本)

    kafka-topics.sh --create --bootstrap-server hadoop01:9092 --topic users --partitions 2 --replication-factor 3
    
  3. 查看某个 topic 主题的详情

    kafka-topics.sh --describe --bootstrap-server hadoop01:9092 --topic users
    

image-20210413092933577

  1. 修改 某个topic 主题的分区个数(只能增加分区数,不能减少分区数)

    kafka-topics.sh --alter --bootstrap-server hadoop01:9092 --topic users --partitions 3
    

    image-20210413093730395

  2. 删除 topic 主题

kafka-topics.sh --delete --bootstrap-server hadoop01:9092 --topic users

2. 生产者 & 消费者

生产者向kafka中生产消息、消费者从kafka中消费消息,都是面向leader的。

  1. 开启生产者

    kafka-console-producer.sh --broker-list hadoop01:9092 --topic users
    
  2. 开启消费者

    第一种方式:

    此处会涉及到offset的重置问题,默认情况下,新启动的消费者是无法消费kafka中的已经存在的数据的,只能消费此消费者启动后kafka新存入的数据消息。

    kafka-console-consumer.sh --bootstrap-server hadoop01:9092 --topic users
    

    第二种方式:

    在启动消费者时,可以指定从kafka中消费所有已经存在的数据,包含启动之前的数据消息,

    在命令行中加上 --from-beginning

    kafka-console-consumer.sh --bootstrap-server hadoop01:9092 --topic users --from-beginning
    

    第三种方式:

    在启动消费者时,可以指定消费者组,这种情况下就不会出现消息重复消费的问题。实际开发中,一般情况下,Topic 主题有几个分区,消费者组中就应该启动几个消费者

    此时,我们需要提前查看消费者组的配置信息

    cd /opt/module/kafka/kafka_2.11-2.4.1/config

    vim consumer.properties

    可以修改消费者组的名字,也可以不修改。

    image-20210413103248166

    • 原始的启动消费者方法
    kafka-console-consumer.sh --bootstrap-server hadoop01:9092 --topic users --consumer.config /opt/module/kafka/kafka_2.11-2.4.1/config/consumer.properties 
    
    • 简洁的启动消费者方法
    kafka-console-consumer.sh --bootstrap-server hadoop01:9092 --topic users --group g1
    

四、Kafka 架构

4.1 kafka的工作流程

在这里插入图片描述


kafka维护偏移量offset在topic主题中

4.2. Kafka 的文件存储机制

Kafka中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。
topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个或多个【1个log文件可以存储1G 的数据,当时间超过1G ,会产生新的log文件】log文件,该log文件中存储的就是producer生产的数据。Producer生产的数据会被不断追加到该log文件末端,且每条数据都有自己的offset。
消费者组中的每个消费者,都会实时记录自己消费到了哪个offset,以便出错恢复时,从上次的位置继续消费。

由于生产者生产的消息会不断追加到log文件末尾,为防止log文件过大导致数据定位效率低下,Kafka采取了分片索引机制,将每个partition分为多个segment[i]。每个segment对应两个文件——“.index”文件和“.log”文件。这些文件位于一个文件夹下,该文件夹的命名规则为:topic名称+分区序号。例如,first这个topic有三个分区,则其对应的文件夹为first-0,first-1,first-2。

index和log文件以当前segment的第一条消息的offset命名。下图为index文件和log文件的结构示意图。

4.3 Kafka生产者

4.3.1 分区策略

  1. 分区的原因

    • 方便在集群中扩展,每个Partition可以通过调整以适应它所在的机器,而一个topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了;

    • 可以提高并发,因为可以以Partition为单位读写了。

  2. 分区的原则

    JAVA 开发中的常用API
    ProduceRecord(@NotNull String topic, Integer partition, String key, String value)
    ProduceRecord(@NotNull String topic, String key, String value)
    ProduceRecord(@NotNull String topic, String value)
    • 直接指定分区号:消息发送到指定的分区partition中。
    • 指定了一个key : 按照key的hashcodde 值 % 分区数得到具体的分区号。【取余操作】
    • 没有指定分区号,没有指定key:
      • 在Kafka低版本中,使用轮巡,均匀发送发送到每个分区中。
      • 2.4版本中,使用黏性分区。(首先会随机选择一个分区,然后黏住该分区,知道该分区对应的batch满或者超时,再次随机选择一个新的分区,黏住使用,以此类推。)

4.3.2 数据可靠性保证ACK

1. 同步和异步发送消息

Produce生产者消息的发送分为同步和异步两种方式。

  • 同步:为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后,都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack,就会进行下一轮的发送,否则重新发送数据。
  • 异步:producer向topic发送消息后,不会等待ack,会直接把新的消息发送到别的topic中。然后会在一定的超时时间内等待各个topic的ack,如果超时未收到某个topic的ack,则会向此topic中重新再次发送一次消息。
2. ack的发送时机策略

通过上述的表达已经明确,为了保障数据的可靠性,produce生产者端只有收到了topic主题Leader端发送的ack回复才可以。那么Leader端何时发送ack呢???

首先有一点要保证:确保follower与leader同步完成以后,leader才能发送ack,这样才能保证leader挂掉以后,能在现有的follower中选举出新的leader。在kafka Cluster集群模式中,follower有多个,那么需要多少个follower同步完成之后发送ack呢???现有两种方案如下:

  • 半数以上的follower同步完成,即可发送ack。

  • 全部的follower同步完成,才可以发送ack。

方案 优点 缺点
半数以上完成同步,就发送ack 延迟低 选举新的leader时,容忍n台节点的故障,需要2n+1个副本
全部完成同步,才发送ack 选举新的leader时,容忍n台节点的故障,需要n+1个副本 延迟高

Kafka选择了第二种方案,原因如下:

  • 同样为了容忍n台节点的故障,第一种方案需要2n+1个副本,而第二种方案只需要n+1个副本,而Kafka的每个分区都有大量的数据,第一种方案会造成大量数据的冗余,并且需要部署更多的节点带来了更大的开销。
  • 虽然第二种方案的网络延迟会比较高,但网络延迟对Kafka的影响较小。
3. ISR机制 - 动态集合

采用第二种方案之后,设想以下情景:leader收到数据,所有follower都开始同步数据,但有一个follower,因为某种故障,迟迟不能与leader进行同步,那leader就要一直等下去,直到它完成同步,才能发送ack。这个问题怎么解决呢?

Leader维护了一个动态的in-sync replica set (ISR),意为和leader保持同步的follower集合。当ISR中的follower完成数据的同步之后,leader就会给producer发送ack。如果follower长时间未向leader同步数据,则该follower将被踢出ISR,该时间阈值由replica.lag.time.max.ms参数设定【默认为1000ms】。Leader发生故障之后,就会从ISR中选举新的leader。

4. ack 应答机制

对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等ISR中的follower全部接收成功。

所以Kafka为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择以下的配置。acks

  • 0:producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据
  • 1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据
  • -1(all):producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack。但是如果在follower同步完成后,broker发送ack之前,leader发生故障,在ISR动态集合中会重新选举出新的leader,因为produce没有接收到ack,则会重新发送一次数据,但是此时各个节点中已经有了消息,所以会造成数据重复
5. 故障处理 - LEO/HW

解决消费者漏消费重复消费的问题

LEO : 每个副本的最后一个offset ;

HW : 所有副本中最小的 LEO 。

对于消费者而言,能够消费的数据都是 HW 之前的数据 ! ! !

leader 和 follower的故障处理方案:
  • follower故障

    follower发生故障后会被临时踢出ISR,待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步。等该follower的LEO大于等于该Partition的HW,即follower追上leader之后,就可以重新加入ISR了。
    
  • leader故障

    leader发生故障之后,会从ISR中选出一个新的leader,之后,为保证多个副本之间的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
    

在这里插入图片描述

注意:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。

4.3.3 Exactly Once语义

1. 何为幂等性

幂等性 : 所谓的幂等性,就是对接口的多次调用所产生的的结果和调用一次是一致的。

生产者Producer在进行重试时不论向Server发送多少次重复数据,Server端都只会持久化一条。

幂等性结合At Least Once语义,就构成了Kafka的Exactly Once语义。即:

At Least Once + 幂等性 = Exactly Once

2. 幂等性条件
  • 只能保证produce在单个会话内数据不丢失不重复,如果produce出现意外挂掉再重启是无法保证的

    (幂等性情况下,是无法获取之前的状态信息的,因为是无法做到跨会话级别的不丢不重)

  • 幂等性不能跨多个topic-produce,只能保证单个partition内的幂等性,当涉及到多个topic-produce时,这个中间的状态并没有同步。

3. 幂等性的使用

修改produce的配置:

enable.idempotence = true

4.4 Kafka消费者

4.4.1 消费方式

consumer采用pull(拉)模式从broker中读取数据。

**push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。**它的目标是尽可能以最快速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而pull模式则可以根据consumer的消费能力以适当的速率消费消息。

pull(拉)模式不足之处是,如果kafka没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka的消费者在消费数据时会传入一个时长参数timeout,如果当前没有数据可供消费,consumer会等待一段时间之后再返回,这段时长即为timeout。

4.4.2 分区分配策略

最好的情况是:有多少个消费者,就创建多少个分区,这也是最常见的情况,一个消费者对应一个分区。
如果,消费者数量和分区数量不是一一对应的情况,我们采取下面的分区分配策略。

一个consumer group中有多个consumer,一个 topic有多个partition,所以必然会涉及到partition的分配问题,即确定那个partition由哪个consumer来消费。

Kafka有三种分配策略,一是roundrobin(轮巡),一是range(范围分配),最后是Sticky(黏性【已作废不用】)

当前Kafka中默认的分区分配策略是range范围分配

1. roundrobin(轮巡)

在这里插入图片描述

roundrobin(轮巡)再分配策略

如果在系统运行时,其中一个消费者(例如Consumer-0)突然挂掉,则Kafka会重置其余消费者消费的消息,Kafka会把这topic主题中的7个分区进行回收再次重新分配,仍然是采用roundrobin轮巡分配策略。
————>分区从0号开始逐个分配一遍。
2. range(范围分配)

在这里插入图片描述

范围分区再分配策略

如果在系统运行时,其中一个消费者(例如Consumer-0)突然挂掉,则Kafka会重置其余消费者消费的消息,Kafka会把这topic主题中的7个分区进行回收再次重新分配,仍然是采用range范围分配策略。
————>如果分区数是消费者数据的整倍数,则平均分,如果不是整倍数,则第一个消费者多消费一个分区。
3. Sticky (黏性)

sticky分区策略初始分区和round robin一样是轮询机制。但与round Robin机制不一样的是,在有新的消费者加入到本消费者组时,sticky重新分配的分区个数较少,比较节省性能。

4.4.3 offset 的维护

那么__consumer_offsets中到底使用什么方式进行维护呢 ?

其实,就是维护了**G【group】、T【topic】、P【partition】**三个数据。

由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。

Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中,从0.9版本开始,consumer默认将offset保存在Kafka一个内置的topic中,该topic为**__consumer_offsets**。

扩展:

在kafka内部有两个内部的话题:

— 、 _consumer_offsets

— 、 _transaction_states

我们可以开启一个消费者去消费**_consumer_offsets **话题中的offset,就可以直观看到offset的维护。

  1. 修改配置文件consumer.properties

    # 不排除内部的topic,在文件的最后一行加上下面这行配置信息。	
    exclude.internal.topics=false
    
  2. 创建一个topic(testOffset)

    kafka-topics.sh --create --topic testOffset --bootstrap-server hadoop01:9092 --partitions 2 --replication-factor 2
    
  3. 启动生产者和消费者,分别向testOffset中生产数据和消费数据

     # 生产者
     kafka-console-producer.sh --topic testOffset --broker-list hadoop01:9092
     # 消费者
     kafka-console-consumer.sh --consumer.config /opt/module/kafka/kafka_2.11-2.4.1/config/consumer.properties --topic testOffset --bootstrap-server hadoop01:9092
    
  4. 再次启动一个消费者【黄雀】,这个消费者就是用来消费_consumer_offsets话题中的offset

    kafka-console-consumer.sh --topic _consumer_offsets --bootstrap-server hadoop01:9092  "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" 
    --consumer.config /opt/module/kafka/kafka_2.11-2.4.1/config/consumer.properties 
    --from-beginning
    
  5. 生产者向testOffset中生产消息,观察黄雀消费者的变化

4.5 Kafka的高效读写

1. 顺序写磁盘

Kafka的producer生产数据,要写入到log文件中,写的过程是一直追加到文件末端,为顺序写。官网有数据表明,同样的磁盘,顺序写能到到600M/s,而随机写只有100k/s。这与磁盘的机械机构有关,顺序写之所以快,是因为其

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值