一:kafka集群 基础、概念、安装了解
官网下载:
jdk-17_linux-x64_bin.tar.gz、apache-zookeeper-3.6.3-bin.tar.gz、kafka_2.13-3.1.0.tgz
需知:Apache Kafka 3.0.0 正式发布:已弃用对 Java 8 和 Scala 2.12 的支持,对它们的支持将在 4.0 版本中彻底移除,以让开发者有时间进行调整。
集群安装过程:
1.创建普通用户,设置 [root@localhost ~]# useradd -d /home/apprun apprun sudo vi /etc/sudoers %apprun ALL=NOPASSWD: /apprun/*,/bin/systemctl *,/sbin/service *,/bin/mv /tmp/* /apprun/*,/usr/bin/vi *,/usr/bin/yum install *,/usr/bin/rpm *,/usr/bin/find *,/usr/sbin/lsof *,/usr/sbin/tcpdump *,/usr/bin/chown * /apprun/*,/usr/bin/chmod * /apprun/*,/usr/bin/kill mkdir -p /apprun && chown -R apprun:apprun /apprun 2.上传解压文件: [root@localhost ~]# su - apprun [apprun@localhost ~]$ cd /apprun/ && mkdir soft && cd soft [apprun@kafka01 soft]$ wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz [apprun@kafka01 soft]$ tar -zxf jdk-17_linux-x64_bin.tar.gz [apprun@kafka01 soft]$ mv jdk-17.0.2 /apprun/ [apprun@kafka01 apprun]$ ln -s /apprun/jdk-17.0.2 /apprun/jdk [apprun@kafka01 soft]$ mv apache-zookeeper-3.6.3-bin /apprun/ [apprun@kafka01 soft]$ ln -s /apprun/apache-zookeeper-3.6.3-bin /apprun/zookeeper [apprun@kafka01 soft]$ mv kafka_2.13-3.1.0 /apprun/ [apprun@kafka01 soft]$ ln -s /apprun/kafka_2.13-3.1.0 /apprun/kafka [apprun@kafka01 apprun]$ sudo vim /etc/profile export JAVA_HOME=/apprun/jdk export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar export PATH=$JAVA_HOME/bin:$HOME/bin:$HOME/.local/bin:$PATH export zookeeper_HOME=/apprun/zookeeper export kafka_HOME=/apprun/kafka export PATH=$kafka_HOME/bin:$zookeeper_HOME/bin:$JAVA_HOME/bin:$HOME/bin:$HOME/.local/bin:$PATH [apprun@kafka01 apprun]$ source /etc/profile #hosts文件解析: 192.168.109.137 kafka01 192.168.109.138 kafka02 192.168.109.139 kafka03 #免密认证 #创建目录: mkdir -pv /apprun/data/zookeeper/ mkdir -pv /apprun/data/kafka/logs/ 配置zookeeper配置文件 [apprun@kafka01 conf]$ cp /apprun/zookeeper/conf/zoo_sample.cfg /apprun/zookeeper/conf/zoo.cfg [apprun@kafka01 conf]$ vim /apprun/zookeeper/conf/zoo.cfg #修改数据路径 dataDir=/apprun/data/zookeeper server.0=kafka01:2888:3888 server.1=kafka02:2888:3888 server.2=kafka03:2888:3888 dataDir 指定的目录下,创建 myid 文件 kafka01服务器: [apprun@kafka01 conf]$ echo "0" > /apprun/data/zookeeper/myid kafka02服务器: [apprun@kafka01 conf]$ echo "1" > /apprun/data/zookeeper/myid kafka03服务器: [apprun@kafka01 conf]$ echo "2" > /apprun/data/zookeeper/myid 配置kafka配置文件 [apprun@kafka01 config]$ cp /apprun/kafka/config/server.properties /apprun/kafka/config/server.properties_bak [apprun@kafka01 config]$ vim /apprun/kafka/config/server.properties #修改分别如下 kafka01服务器: broker.id=0 listeners=PLAINTEXT://kafka01:9092 log.dirs=/apprun/data/kafka/logs zookeeper.connect=kafka01:2181,kafka02:2181,kafka03:2181 kafka02服务器: broker.id=1 listeners=PLAINTEXT://kafka02:9092 log.dirs=/apprun/data/kafka/logs zookeeper.connect=kafka01:2181,kafka02:2181,kafka03:2181 kafka03服务器: broker.id=2 listeners=PLAINTEXT://kafka03:9092 log.dirs=/apprun/data/kafka/logs zookeeper.connect=kafka01:2181,kafka02:2181,kafka03:2181 #kafka的jmx开启:(后续kafka-eagle需要连接) [apprun@kafka01 bin]$ vim /apprun/kafka/bin/kafka-run-class.sh 1.首行添加JMX_PORT=6666,绑定端口 2.查找KAFKA_JMX_OPTS,绑定各自主机IP,添加 -Djava.rmi.server.hostname=kafka01 #启动zookeeper [apprun@kafka01 apprun]$ zkServer.sh start [apprun@kafka01 apprun]$ zkServer.sh status #启动kafka [apprun@kafka01 apprun]$ kafka-server-start.sh /apprun/kafka/config/server.properties & ... [2022-03-06 08:54:20,235] INFO [KafkaServer id=0] started (kafka.server.KafkaServer) ... #查看kafka集群注册情况 [apprun@kafka01 ~]$ zkCli.sh [zk: localhost:2181(CONNECTED) 4] ls /brokers/ids [0, 1, 2] [zk: localhost:2181(CONNECTED) 5]
集群中broker、topic、分区、副本的概念:
[apprun@kafka01 apprun]$ kafka-topics.sh --create --bootstrap-server 192.168.109.138:9092 --replication-factor 3 --partitions 2 --topic wang01-topic
查看topic情况:
[apprun@kafka01 apprun]$ kafka-topics.sh --describe --bootstrap-server 192.168.109.138:9092 --topic wang01-topic
下图:broker01的节点ID为0,broker02的节点ID为1,broker03的节点ID为2,
副本:是为了为主题中的分区创建多个备份,多个副本在kafka集群的多个broker中,会有一个副本作为leader,其他是follower;kafka的读写都在leader上,leader负责把数据同步给follower;当leader挂了
经过主从选举,从多个follower中选举产生一个新的leader
Isr:可以同步的broker节点和已同步的broker节点,存放在Isr集合中。只有在Isr集合中的节点才能参与选举leader;如果isr中的节点性能较差,会被踢出isr集合。
Kafka动态调整topic分区partition、修改修改topic分区及副本数:(这里只是基础的分析,不做为线上环境分区副本重分配,线上分三步,还涉及到:1.数据量过大,过期log临时缩小;2.限流)
假如有个主题topic:wang03-topic,原始为1分区,1个副本;这时为了提高这个主题的读写效率和高可用性,我们需要对此主题增加一个分区,每个分区设为3副本
[apprun@kafka01 ~]$ kafka-topics.sh --create --bootstrap-server kafka01:9092 --replication-factor 1 --partitions 1 --topic wang06-topic [apprun@kafka01 ~]$ kafka-topics.sh --describe --bootstrap-server kafka01:9092 --topic wang06-topic 第一步:通过kafka-topics.sh修改分区数,从1改到2 [apprun@kafka01 ~]$ kafka-topics.sh --alter --bootstrap-server kafka01:9092 --topic wang06-topic --partitions 2 [apprun@kafka01 ~]$ kafka-topics.sh --describe --bootstrap-server kafka01:9092 --topic wang06-topic 第二步:修改topic副本数: #重新分配及查看分配进度 vim ~/topic-reassignment.json {"partitions": [ {"topic": "wang06-topic", "partition": 0, "replicas": [0,1,2] }, {"topic": "wang06-topic", "partition": 1, "replicas": [0,1,2] } ], "version":1 } [apprun@kafka01 ~]$ kafka-reassign-partitions.sh --bootstrap-server kafka01:9092 --reassignment-json-file ~/topic-reassignment.json --execute
关于集群消费:
单播:如果多个消费组在同一个消费组,那么只有一个消费者可以收到topic中的消费消息
多播:在不同的消费组订阅同一个topic,那么不同的消费组中只有一个消费者能收到
偏移量:
发消息:
kafka-console-producer.sh --bootstrap-server kafka01:9092 --topic wang01-topic
消费消息:
kafka-console-consumer.sh --bootstrap-server kafka01:9092 --from-beginning --topic wang01-topic
kafka-console-consumer.sh --bootstrap-server kafka01:9092 --consumer-property group.id=wangGroup1 --from-beginning --topic wang01-topic
关于分区消费组消费者:
图中kafka集群有俩个broker,每个broker有多个partition。一个partition只能被一个消费者中的某一个消费者消费,从而保证消息的顺序。kafka只在partition的范围内保证消息的局部顺序性,
不能在一个topic中的多个partition中保证总的消费顺序性。一个消费者可以消费多个partition。
消费组中消费者的数量不能比一个topic中的partition数量多,否则多出来的消费者消费不到消息。
如果消费者挂了,那么会触发rebalance机制,会让其他消费者来消费该分区。
生成者的同步发送和异步发送:
异步发送:可能会丢消息~(生产者发送完消息就可以执行之后的业务,broker在收到消息后异步调用生产者提供的callback回调方法) 同步发送:ack,如果生产者发送消息没有收到ack,生产者会阻塞,阻塞3s的时间,如果还没有到消息,会进行重试,重试的次数3次 ack=0 :生产者发送消息到leader后,就继续发送其他的消息,不需要等待leader的ack--缺点是数据可能丢失 ack=1 :生产者发送消息到leader后,leader会将消息落地到磁盘,然后就向生产者发送ack 缺点是 数据可能丢失(leader落地了,并ack,但是follwer还未同步,数据不全) 数据可能重复(leader落地了,但是ack失败了,生产者再次发送消息 ,所以可能重复) ack=-1或all,配合min.insync.replicas默认为1,推荐大于等于2 :生产者发送消息到leader后,leader会将消息落地到磁盘,follwer会同步leader里的消息然后落地,全部落地完成后leader向生产者发送ack 缺点是 数据可能重复(leader落地了,但是ack失败了,生产者再次发送消息 ,所以可能重复) 任何一种ack都不能保证数据不丢失或者数据重复,ack+幂等机制可以保障不重复,不丢失
ack+幂等机制可以保障不重复,不丢失
发送消息的缓冲区机制:发送的消息会先进入缓冲区(32mb),kafka会跑一个线程,该线程去缓冲区中取16k的数据,发送到kafka,如果10毫秒数据没取满16k,也会发送一次。
消费者的自动提交与手动提交:offset
消费者的监控状态检查:消费者每隔1s向kafka集群发送心跳,集群发现如果有超过10s没有续约的消费者,将被踢出消费组,触发该消费组的rebalance机制,将该分区交给消费组里的其他消费者进行消费。
指定分区消费;消息回溯消费;指定offset消费;从指定时间消费
新消费组的消费offset规则:
新消费组中的消费者在启动后,默认会从当前分区的最后一条消息的offset+1开始消费(消费新消息)。可以通过以下设置,让新的消费者第一次从头开始消费,之后开始消费新消息
Latest:默认的,消费新消息
earliest:第一次从头开始消费,之后开始消费新消息
kafka集群 Controller、Rebalance 和 HW、LEO
1. Controller (集群中谁来充当 Controller??)
kafka集群中的broker在ZK中创建临时序号节点,序号最小的节点(最先创建的节点)将作为集群的 controller ,负责管理 整个集群中的所有分区和副本的状态: 当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本; 当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有的broker更新其元数据信息; 当使用kafka-topics.sh 脚本为某个topic增加分区数量时,同样还是控制器负责让新分区被其他节点感知到。
2. Rebalance 机制
前提是:消费者没有指明分区消费。 当消费组里消费者和分区的关系发生变化,那么就会触发 Rebalance 机制
这个机制会重新调整消费者消费哪个分区。
在触发 Rebalance机制之前,消费者消费哪个分区有三种策略:
range:通过公示来计算某个消费者消费哪个分区。
轮询:大家轮着消费
sticky:在触发了Rebalance后,在消费者消费的原分区不变的基础上进行调整。如果这个策略没有开,那么就要进行全部的重新分配,影响性能,建议开启
3.HW和LEO
HW:俗称高水位,取一个partition对应的ISR中最小的LEO(log-end-offset)作为HW,consumer最多只能消费到HW所在的位置。
另外每个replica都有HW;leader和follower各自负责更新自己的HW状态,对于leader新写入的消息,consumer不能立刻消费,
leader会等待该消息被所有ISR中的replica同步后更新HW,此时消息才能被consumer消费。这样就保证了如果leader所在的broker
失效,该消息仍然可以从新选举的leader中获取。
kafka线上问题优化:
1.如何防止消息丢失
发送方:ack=1 或者 -1/all 可以防止消息丢失,如果要做到99.9999%,ack设成all,把min.insync.replicas配置成分区备份数 消费方:把自动提交改为手动提交。
2.如何防止消息的重复消费
一条消息被消费者消费多次,如果为了消息的不重复消费,而把生产者的重试机制关闭、消息端的手动提交改为被动提交,这样
反而会出现消息丢失,那么可以直接在防止消息丢失的手段上再加上消费消息时的幂等性保证,就能解决消息的重复消费问题。
幂等性如何保证:
mysql插入业务id作为主键,主键是唯一的,所以一次只能插入一条
使用redis或zk的分布式锁(主流方案)
3.如何做到顺序消费
发送方:在发送时将ack不能设置为0,使用同步发送,等到发送成功再发送下一条。确保消息是顺序发送的。
接收方:消息是发送到一个分区中,只能有一个消费组的消费者来接收消息。
因此,kafka的顺序消费会牺牲掉性能。
4.解决消息积压问题
消息积压会导致很多问题,比如磁盘被打满、生产端发消息导致kafka性能过慢,就容易出现服务器雪崩,就需要有相应的手段: 方案1:在一个消费者中启动多个线程,让多个线程同时消费。 ----提升一个消费者的消费能力。 方案2:如果方案1还不够的话,这个时候可以启动多个消费者,多个消费者部署在不同的服务器上。其实多个消费者部署在同一个 服务器上也可以提高消费能力--充分利用服务器的cpu资源。 方案3:让一个消费者去把收到的消息往另外一个topic上发,另一个topic设置多个分区和多个消费者,进行具体的业务消费。
5.延迟队列
延迟队列的应用场景:在订单创建成功后如果超过30分钟没有付款,则需要取消订单,此时可用延时队列来实现
创建多个topic,每个topic表示延时的间隔,如
topic_5s:延时5s执行的队列
topic_51m:...
topic_30m: ...
消息发送者发送消息到相应的topic,并带上消息的发送时间
消费者订阅相应的topic,消费时轮询消费整个topic中的消息:
如果消息的发送时间 和 消费的当前时间 超过预设的值,例:30分钟
如果消息的发送时间 和 消费的当前时间 没有超过预设值,则不消费当前的offset及之后的offset的所有消息都消费
下次继续消费该offset处的消息,判断时间是否已满足预设值。
kafka-eagle 监控平台:
官网:https://www.kafka-eagle.org/ 1.搭建: 1.创建普通用户,设置 [root@kafka-eagle ~]# useradd -d /home/apprun apprun sudo vi /etc/sudoers %apprun ALL=NOPASSWD: /apprun/*,/bin/systemctl *,/sbin/service *,/bin/mv /tmp/* /apprun/*,/usr/bin/vi *,/usr/bin/yum install *,/usr/bin/rpm *,/usr/bin/find *,/usr/sbin/lsof *,/usr/sbin/tcpdump *,/usr/bin/chown * /apprun/*,/usr/bin/chmod * /apprun/*,/usr/bin/kill,/usr/bin/grep * [root@kafka-eagle ~]# mkdir -p /apprun && chown -R apprun:apprun /apprun [root@kafka-eagle ~]# su - apprun [apprun@kafka-eagle ~]$ cd /apprun/ [apprun@kafka-eagle apprun]$ mkdir -p /apprun/soft && cd /apprun/soft/ JDK安装:(jdk-17.0.2) [apprun@kafka-eagle apprun]$ sudo vi /etc/profile export JAVA_HOME=/apprun/jdk export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar export PATH=$JAVA_HOME/bin:$HOME/bin:$HOME/.local/bin:$PATH [apprun@kafka-eagle apprun]$ source /etc/profile 安装mysql: [apprun@kafka-eagle soft]$ wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm [apprun@kafka-eagle soft]$ sudo yum install mysql57-community-release-el7-10.noarch.rpm -y sudo vi /etc/yum.repos.d/mysql-community.repo 修改对应安装版本的gpgcheck=0即可,默认值为1 ... [mysql57-community] name=MySQL 5.7 Community Server baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/ enabled=1 gpgcheck=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql ... [apprun@kafka-eagle soft]$ sudo yum install mysql-community-server -y [apprun@kafka-eagle soft]$ sudo systemctl start mysqld.service [apprun@kafka-eagle soft]$ sudo systemctl status mysqld.service [apprun@kafka-eagle soft]$ sudo grep "password" /var/log/mysqld.log [apprun@kafka-eagle soft]$ mysql -uroot -p mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'wang!321WBC'; 可以通过以下命令修改默认密码的复杂度: set global validate_password_policy=LOW; 修改默认密码长度: set global validate_password_length=6; 命令查看mysql默认密码复杂度: SHOW VARIABLES LIKE 'validate_password%'; ###grant all privileges on *.* to 'root'@'192.168.0.1' identified by 'password' with grant option; GRANT USAGE ON *.* TO 'root'@'localhost' IDENTIFIED BY 'wang!321WBC'; GRANT SELECT,EXECUTE ON *.* TO web_ngames@'%' IDENTIFIED BY 'wang!321WBC'; grant select,EXECUTE on *.* to select_ngames@'%' identified by 'wang!321WBC'; grant all privileges on *.* to ngames@'%' identified by 'wang!321WBC'; flush privileges; 重新登录mysql,然后输入status,查看mysql字符集信息: [apprun@kafka-eagle soft]$ sudo vi /etc/my.cnf 进入文件后,新增五行代码: [client] default-character-set=utf8 [mysqld] character-set-server=utf8 collation-server=utf8_general_ci [apprun@kafka-eagle soft]$ sudo service mysqld restart 安装efak: [apprun@kafka-eagle soft]$ wget https://github.com/smartloli/kafka-eagle-bin/archive/v2.1.0.tar.gz [apprun@kafka-eagle soft]$ tar -zxf v2.1.0.tar.gz [apprun@kafka-eagle soft]$ cd kafka-eagle-bin-2.1.0 [apprun@kafka-eagle kafka-eagle-bin-2.1.0]$ tar -zxf efak-web-2.1.0-bin.tar.gz -C /apprun/ [apprun@kafka-eagle apprun]$ ln -s /apprun/efak-web-2.1.0 /apprun/efak [apprun@kafka-eagle efak]$ sudo vi /etc/profile export KE_HOME=/apprun/efak export PATH=$PATH:$KE_HOME/bin [apprun@kafka-eagle apprun]$ source /etc/profile [apprun@kafka-eagle conf]$ cp /apprun/efak/conf/system-config.properties /apprun/efak/conf/system-config.properties_bak [apprun@kafka-eagle conf]$ vim /apprun/efak/conf/system-config.properties 修改 efak.zk.cluster.alias=cluster1 #有几个集群就加几个,逗号分隔,下面相应的cluster1.zk.list依据集群来配 cluster1.zk.list=192.168.109.137:2181,192.168.109.138:2181,192.168.109.139:2181 #配置mysql的连接~ efak.driver=com.mysql.cj.jdbc.Driver efak.url=jdbc:mysql://192.168.109.140:3306/ke?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull efak.username=ngames efak.password=wang!321WBC 启动 [apprun@kafka-eagle apprun]$ ke.sh start ... Version 2.1.0 -- Copyright 2016-2022 ******************************************************************* * EFAK Service has started success. * Welcome, Now you can visit 'http://192.168.109.140:8048' * Account:admin ,Password:123456 ******************************************************************* * <Usage> ke.sh [start|status|stop|restart|stats] </Usage> * <Usage> https://www.kafka-eagle.org/ </Usage> ******************************************************************* kafka的jmx开启: [apprun@kafka01 bin]$ vim /apprun/kafka/bin/kafka-run-class.sh 1.首行添加JMX_PORT=6666,绑定端口 2.查找KAFKA_JMX_OPTS,绑定IP,添加 -Djava.rmi.server.hostname=kafka01 重启kafka集群生效~
Kafka的灵魂伴侣Logi-KafkaManger 之运维管控–集群运维(数据迁移和集群在线升级):(另外开篇章介绍)