Kafka

安装配置

官网下载地址:http://kafka.apache.org/downloads
下载完成后,上传压缩包并解压

Kafka的安装依赖于zookeeper环境,如果需要配置KafkaHA需要先将几台结点zookeeper上安装并并搭建zookeeper集群

修改配置文件server.properties文件

#broker 的全局唯一编号,不能重复
broker.id=0
#是否开启删除topic功能
delete.topic.enable=true
#处理网络请求的线程数量(默认)
num.network.threads=3
#用来处理磁盘 IO 的现成数量(默认)
num.io.threads=8
#发送套接字的缓冲区大小(默认)
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小(默认)
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小(默认)
socket.request.max.bytes=104857600
#kafka 运行日志存放的路径
log.dirs=/opt/module/kafka/logs
#topic 在当前 broker 上的分区个数(默认)
num.partitions=1
#用来恢复和清理 data 下数据的线程数量(默认)
num.recovery.threads.per.data.dir=1
#segment 文件保留的最长时间,超时将被删除(默认)
log.retention.hours=168
#配置连接 Zookeeper 集群地址
zookeeper.connect=num04:2181,num05:2181,num06:2181

如果要使用standalone模式,zookeeper集群地址位置只配置一个地址即可,并且配置到这一步就可以启动Kafka了

接下来将Kafka目录分发到其他节点上,每个结点对照修改server.properties配置文件中的broker.id即可

到这里就可以启动Kafka集群了(这里的-daemon是后台启动的意思)
${KAFKA_HOME}/bin/kafka-server-start.sh -daemon ${KAFKA_HOME}/config/server.properties
启动完成后,所有结点可以看到Kafka进程
在这里插入图片描述
关闭集群命令
${KAFKA_HOME}/bin/kafka-server-stop.sh

集群每次启动需要输入长串的启动命令,且需要三台同时启动,所以可以写个启动脚本,一键启动或关闭Kafka集群(根据实际情况,修改其中的路径)

#!/bin/bash
for host in num01 num02 num03
do
        if [ $1 = "start" ]
        then
                echo "----------starting kafka on ${host}----------"
                ssh root@${host} /opt/kafka/bin/kafka-server-start.sh -daemon /opt/kafka/config/server.properties
                echo "---------kafka on ${host} has started--------"
        elif [ $1 = "stop" ]
        then
                echo "----------stopping kafka on ${host}----------"
                ssh root@${host} /opt/kafka/bin/kafka-server-stop.sh
                echo "---------kafka on ${host} has stoped--------"
        else
                echo "can not recoganize parameter $1"
        fi
done
if [ $1 = "start" ]
then
        echo "-----------------------------------"
        echo "------all kafka node started-------"
        echo "-----------------------------------"
elif [ $1 = "stop" ]
then
        echo "-----------------------------------"
        echo "-------all kafka node stoped-------"
        echo "-----------------------------------"
fi

按照实际需要,参数输入start或者stop

操作指令

操作指令
查看当前主题kafka-topics.sh --zookeeper num01:2181 --list
创建主题kafka-topics.sh --zookeeper num01:2181 --create --topic fourth --partitions 2 --replication-factor 2
删除主题kafka-topics.sh --zookeeper num01:2181 --delete --topic fourth
查看主题详情kafka-topics.sh --zookeeper num01:2181 --describe --topic fourth
修改分区数量kafka-topics.sh --zookeeper num01:2181 -alter --topic fourth --partitions 6
查看主题消息数量kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list num01:9092 --topic fourth --time -1 --offsets 1
生产消息kafka-console-producer.sh --broker-list num01:9092 --topic fourth
消费消息kafka-console-consumer.sh --bootstrap-server num01:9092 --topic fourth

注意事项

1、创建主题时,声明的副本数量不能超过当前Kafka集群的结点数量,否则会抛出异常
2、副本数量不宜设置过多,2-3个即可
3、每个主题实际产生的文件夹数量为partitions分区数量与replication-factor副本数量的乘积
4、Kafka的实际数据保存在配置文件中log.dirs指明的路径下,以<offset>.log结尾的文件保存,同时会有一个同名的<offset>.index结尾的文件,为<offset>.log文件的索引文件;
5、<offset>.log文件默认最大容量1G,当存储数量超出1G后,会新生产一组<offset>.log<offset>.index,新生成的文件名为当前的offset

运行机制

Kafka架构

Kafka是一个消息中间件,实际上是一个传输消息的队列结构,Producer发送的每条数据将被加入到队列中,消费者从队列头部依次取走数据
使用Kafka的好处
1、 解耦
允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。

2、 可恢复性
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

3、 缓冲
有助于控制和优化数据流经过系统的速度, 解决生产消息和消费消息的处理速度不一致的情况。

4、 灵活性 & 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。

5、异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
在这里插入图片描述

名词含义
Producer消息生产者,向Kafka发送消息
Consumer消息消费者,从Kafka获取消息
BrokerKafka集群中的单个结点
topic主题,生产者发布消息时向指定主题中发布,消费者从订阅的主题中获取消息

生产者

Producer向Kafka集群发送消息,要确保的时消息不会丢失,但如果消息发送完成了,集群内部未将消息持久化,就会造成数据丢失,所以,Producer要等待集群的消息回复(acknowledgement),这就是Kafka的ACK应答机制

生产者消息发送流程

首先明确,在每个主题的每个分区中,都会有一个Leader,其余副本均为Follower。
当Producer向该分区发送消息时,首先会发送给Leader,当Leader同步完成后,才会将消息同步给Follower,在所有Follower同步完成后,一条消息发送完成
在这里插入图片描述
如果Leader发生故障,Kafka会在ISR列表中选出新的Leader

ISR

在Leader向Follower同步消息的过程中,如果一个Follower因为网络或者性能原因,迟迟不能完成同步,就会影响这个集群接收消息的效率。因此Kafka维护了一个broker列表(in-sync replications,同步结点列表)来应对这种问题

当某个broker迟迟不与Leader同步时,它就会被认为性能有问题,踢出ISR,而在ISR列表中的broker(Leader在ISR列表中)完成同步后,就认为当前消息接收并同步完成了,这样就消除了个别低性能broker对整体集群的影响

当被踢出ISR的broker修复故障恢复性能后,不断从Leader拉取数据,当其消息与Leader同步完成后,就可以回到ISR中了

在0.10版本之前,有两个参数用来维护ISR队列:
replica.lag.time.max.ms:上限时间,当follower与leader未同步的时间超出此时间后,将被移出ISR
replica.lag.max.messages:上限条数,当follower中同步消息条数与leader中条数相差超出该数量是,将被移出ISR

这两个参数都是为了维护ISR列表,使其中的follower结点都是同步效率较高的broker,但在0.10版本后,replica.lag.max.messages设置被移除了,原因为:

实际Producer向集群发送消息时,很有可能一次性发送的消息条数超出replica.lag.max.messages的限制条数,这样就会造成全部broker移出ISR列表。但是,实际很多broker并非性能较差,因此在同步上Leader后,这些broker又会重新回到ISR中

这样就会造成producer每次向集群发送大量数据时,ISR中全部结点就会被移出,又重新返回的过程。这个过程十分浪费资源。

而另一方面,如果想要避免这种情况,replica.lag.max.messages就要设置大一些,但这样该参数也就失去了意义,无法将真正低性能的broker移出ISR。

因此,在0.10版本后,replica.lag.max.messages参数被移除,仅由replica.lag.time.max.ms同步间隔时间维护ISR列表

ACK应答

ACK应答机制的含义为,Leader何时向Producer返回同步完成的消息,有三个可选选项

应答级别含义
0Producer不等待Leader返回同步完成的消息,默认发送完成就结束发送
1Producer等待Leader返回的同步完成消息,Leader在自己消息同步完成后就返回ACK
-1Producer等待Leader返回的同步完成消息,Leader在ISR中全部Follower同步完成后才返回ACK

数据安全性和执行效率分析

当ACK级别设为0时,消息从Producer发送到Kafka集群,只要在Follower同步完成前,Leader故障,就必然会造成消息的丢失(因为Leader故障后,会从ISR中选出一个Follower作为新的Leader,其他Follower会从新Leader上拉取数据。而原Leader就算恢复后,也只能作为Follower了)

当ACK级别设为1时,在Leader同步数据完成后,就会给Producer发回ACK,但是如果在Follower同步完成前,Leader发生故障,也会造成数据的丢失

当ACK级别设为-1,只有ISR中全部Broker同步了数据,才会返回ACK,这样虽然效率最低,但是确保了数据不会丢失

从执行效率上看,很明显0是最快的,-1执行最慢,1中等
而从消息安全性上看,-1最安全,0丢失数据可能型最大,1中等

注意事项

1、当ACK级别为-1时,虽然确保了数据不会丢失,但是仍有可能会造成数据重复:当Leader向Follower同步数据接近完成时,Leader发生故障,而从ISR中选出的新Leader此时已完成了数据同步,其余Follower从新Leader处拉去的数据必然也是完整的。但是由于未返回ACK,那么Producer就会再向Leader发送一遍数据,这就造成了数据的重复

2、实际情况下,broker故障概率很低,所以上述假设条件很少发生。因此,ACK级别并非不可以设为0,对于一些对传输速度要求较高,但是单条消息重要性不高的场景,例如视频、音频等内容,ACK级别为0反而是更好的选择

LEO和HW

在这里插入图片描述
LEO(Log End Offset),为每个副本中最后一个offset,必然是Leader上的offset
HW(High Watermark),水位线,为分区内所有ISR副本中,最低的LEO,也是消费者可见的最大offset

该机制主要用于故障恢复

当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同步数据

Exactly Once

当ACK为0时,对应的情景为At Most Once,即数据被处理一次,可能会丢失数据,但不会出现数据重复情况

当ACK为-1时,对应的情景为At Leat Once,即数据至少被处理一次,可能会出现数据重复情况,但不会出现数据丢失情况

以上两种情况都不是实际需要的,我们实际的需求是,数据确定只会处理一次,不会丢失,也不会重复,这就是Exactly Once

Kafka在0.10版本引入了幂等性特性,用于实现Exactly Once。幂等性,即无论Producer向Server发送了多少次重复数据,Server端都只会持久化一条。幂等性结合At Least Once语义,就构成了Kafka的Exactly Once语义

将Producer的参数enable.idempotence设为true即可开启幂等性。

Kafka的幂等性实现其实就是将原来下游需要做的去重放在了数据上游。开启幂等性的 Producer 在初始化的时候会被分配一个 PID,发往同一 Partition 的消息会附带 Sequence Number。而Broker 端会对<PID, Partition, SeqNumber>做缓存,当具有相同主键的消息提交时, Broker 只会持久化一条。

但是 PID 重启就会变化,同时不同的 Partition 也具有不同主键,所以幂等性无法保证跨分区跨会话的 Exactly Once

消费者

消费方式

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

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

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

分区分配策略

一个consumer group 中有多个consumer,一个topic有多个partiton,同时一个partition在一个cunsumer group中只能发给一个consumer,所以涉及分配问题

Kafka有两种分配策略,RoundRobin和Range。

RoundRobin:topic中的分区按顺序依次分发给consumer group中的consumer,每个consumer分发到一个后,再从头开始,知道partition分发完成

需要保证消费者组中所有消费者的订阅主题是一致的

Range:使用分区数除以consumer group中的consumer数量,整除则平均分配,不能整除则放到前面的consumer中。是Kafka默认采用的分配策略

问题在于,当订阅topic数量越来越多时,前面的consumer承担压力会远大于后面的consumer。

当消费者组中的消费者数量发生改变时,就会触发重新分配机制,当消费者组中的消费者数量大于分区数时,就会有消费者分配不到topic分区

offset维护

由于consumer在消费过程中可能宕机故障,consumer恢复后,需要从故障前的位置继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费
在这里插入图片描述

offset以消费者组、topic和分区为单元存储,所以,若消费者组中成员发生变化,分区重新分配时,已经消费过的数据不会重新再发送给新分配的成员

高效读写数据

顺序写磁盘

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

零复制

在这里插入图片描述
省去了操作系统层内存读取到用户操作层,再将数据重新写入到系统层的操作

事务

Kafka从0.11版本开始支持事务,事务可以保证Kafka在Exactly Once的基础上,生产和消费可以跨分区和会话,要么全部成功,要么全部失败。

Producer事务

为了实现跨分区跨会话的事务,需要引入一个全局唯一的 Transaction ID,并将 Producer获得的PID 和Transaction ID 绑定。这样当Producer 重启后就可以通过正在进行的 TransactionID 获得原来的 PID。

为了管理 Transaction, Kafka 引入了一个新的组件 Transaction Coordinator。 Producer 就是通过和 Transaction Coordinator 交互获得 Transaction ID 对应的任务状态。TransactionCoordinator 还负责将事务所有写入 Kafka 的一个内部 Topic,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。

Consumer事务

上述事务机制主要是从 Producer 方面考虑,对于 Consumer 而言,事务的保证就会相对较弱,尤其时无法保证 Commit 的信息被精确消费。这是由于 Consumer 可以通过 offset 访问任意信息,而且不同的 Segment File 生命周期不同,同一事务的消息可能会出现重启后被删除的情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值