文章目录
常用命令
从开始消费消息:
./kafka-console-consumer.sh --bootstrap-server {ip}:{host} --topic ready_topic --from-beginning
消费指定组的指定topic消息:
./kafka-console-consumer.sh --bootstrap-server {ip}:{host} --group aaa --topic bbb_TOPIC
查看某个topic分区、描述信息
注意这里是zookeeper的端口
./kafka-topics.sh --zookeeper {ip}:{2181} --describe --topic wx_rm_performance_topic
查看某个topic有多少条数据量:
./kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list {ip}:{host} --topic aaa_topic --time -1 --offsets 1 | awk -F ':' '{sum += $3} END {print sum}'
查看某个topic占用内存大小
./kafka-log-dirs.sh --describe --bootstrap-server {ip}:{host} --topic-list aaa_topic
查看kafka的topic列表:
./kafka-topics.sh --list --zookeeper {ip}:{host}
查看有哪些消费者组:
./kafka-consumer-groups.sh --bootstrap-server {ip}:{host} --list
查看消费者组的信息:
./kafka-consumer-groups.sh --bootstrap-server {ip}:{host} --describe --group aaa
配置已有topic的消息过期时间
retention.ms=604800000 //过期时间为:一周
./kafka-configs.sh --zookeeper localhost:2181 --alter --entity-type topics --entity-name aaa_topic --add-config retention.ms=604800000
如果想取消 Kafka topic 的过期时间限制,可以使用以下命令将 “retention.ms” 属性设置为 “-1”:
./kafka-configs.sh --zookeeper localhost:2181 --alter --entity-type topics --entity-name aaa_topic --add-config retention.ms=-1
查看 Kafka 主题的清除策略:
./kafka-configs.sh --zookeeper localhost:2181 --entity-type topics --entity-name <topic_name> --describe
配置kafka日志过期时间
在kafka的安装目录的config目录下,找到 server.properties
vim server.properties
//168小时 = 一周
log.retention.hours=168
一、概述
1、消息队列的好处
1)解耦
允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
2)可恢复性
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
3)缓冲
有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
4)灵活性 & 峰值
处理能力在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。
如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
5)异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
2、消息队列的 两种模式
(1 )点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。消息被消费以后,queue 中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue 支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
(2 )发布/ 订阅模式(一对多,消费者消费数据之后不会清除消息)
消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到 topic 的消息会被所有订阅者消费。
3、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 会成为新的 follower。
二、为什么使用Kafka消息队列
1、使用同步的通信方式
2、使用异步的通信方式
3、消息队列的流派
三、环境搭建
1、安装Kafka
这里下载 2.4.1版本的。
2、上传到linux服务器
cd /usr/local
mkdir kafka
cd kafka/
ll
使用xftp上传完成后解压:
tar -zxvf kafka_2.11-2.4.1.tgz
3、修改配置文件
cd /usr/local/kafka/kafka_2.11-2.4.1/config
vim server.properties
找到下面的配置并修改:
# kafka的id
broker.id=0
# 监听
listeners=PLAINTEXT://0.0.0.0:9092
# 外部代理地址
advertised.listeners=PLAINTEXT://1.117.203.6:9092
# 日志地址
log.dirs=/usr/local/kafka/data/kafka-logs
# zookeeper地址
zookeeper.connect=localhost:2181
保存退出
按Esc + :wq
4、下载zookeeper
官网:https://archive.apache.org/dist/zookeeper/
参考:https://www.cnblogs.com/expiator/p/9853378.html
这里下载3.5.8的zookeeper,浏览器打开下面地址即可:
https://archive.apache.org/dist/zookeeper/zookeeper-3.5.8/apache-zookeeper-3.5.8-bin.tar.gz
cd /usr/local
mkdir zookeeper
上传压缩包
#解压
tar -zxvf apache-zookeeper-3.5.8-bin.tar.gz
修改配置:
cd /usr/local/zookeeper/apache-zookeeper-3.5.8-bin/conf
cp zoo_sample.cfg zoo.cfg
vim zoo.cfg
dataDir=/usr/local/zookeeper/data
dataLogDir=/usr/local/zookeeper/log
启动:
cd /usr/local/zookeeper/apache-zookeeper-3.5.8-bin/bin
./zkServer.sh start
查看:
ps -aux | grep zookeeper
5、修改配置并启动kafka
在bin目录下找到 kafka-server-start.sh
文件:
cd /usr/local/kafka/kafka_2.11-2.4.1/bin
vim kafka-server-start.sh
将 export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G" 修改为
export KAFKA_HEAP_OPTS="-Xmx256M -Xms128M"
启动 kafka
./kafka-server-start.sh -daemon ../config/server.properties &
../bin/kafka-server-start.sh -daemon ../config/server.properties &
查看
ps -ef|grep kafka
或
jps
四、Spring Boot整合Kafka
1、依赖
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
2、编写配置文件
server:
port: 8082
spring:
kafka:
bootstrap-servers: 1.117.203.6:9092
producer: # ⽣产者
retries: 3 # 设置⼤于0的值,则客户端会将发送失败的记录重新发送
batch-size: 16384
buffer-memory: 33554432
acks: 1
# 指定消息key和消息体的编解码⽅式
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
group-id: default-group
enable-auto-commit: false
# auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
max-poll-records: 500
listener:
# 当每⼀条记录被消费者监听器(ListenerConsumer)处理之后提交
# RECORD
# 当每⼀批poll()的数据被消费者监听器(ListenerConsumer)处理之后提交
# BATCH
# 当每⼀批poll()的数据被消费者监听器(ListenerConsumer)处理之后,距离上次提交时间⼤于TIME时提交
# TIME
# 当每⼀批poll()的数据被消费者监听器(ListenerConsumer)处理之后,被处理record数量⼤于等于COUNT时提交
# COUNT
# TIME | COUNT 有⼀个条件满⾜时提交
# COUNT_TIME
# 当每⼀批poll()的数据被消费者监听器(ListenerConsumer)处理之后, ⼿动调⽤Acknowledgment.acknowledge()后提交
# MANUAL
# ⼿动调⽤Acknowledgment.acknowledge()后⽴即提交,⼀般使⽤这种
# MANUAL_IMMEDIATE
ack-mode: MANUAL_IMMEDIATE
3、编写消息生产者
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xdragon
* @Description TODO
* @createTime 2021年10月06日
*/
@RestController
@RequestMapping("/msg")
public class MyKafkaController {
private final static String TOPIC_NAME = "my-replicated-topic";
@Autowired
private KafkaTemplate<String,String> kafkaTemplate;
@RequestMapping("/send")
public String sendMessage(){
kafkaTemplate.send(TOPIC_NAME,0,"key","this is a message!");
return "send success!";
}
}
4、编写消费者
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Component;
/**
* @author xdragon
* @Description TODO
* @createTime 2021年10月06日
*/
@Component
public class MyKafkaConsumer {
@KafkaListener(topics = "my-replicated-topic",groupId = "MyGroup1")
public void listenGroup(ConsumerRecord<String, String> record,
Acknowledgment ack) {
String value = record.value();
System.out.println(value);
System.out.println(record);
//⼿动提交offset
ack.acknowledge();
}
}
消费者中配置消费主题、分区和偏移量
5、测试
发送消息:http://localhost:8082/msg/send
消费者消费:
五、Kafka知识
1、创建topic
通过kafka命令向zk中创建⼀个主题
./kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
查看当前zk中所有的主题
./kafka-topics.sh --list --zookeeper localhost:2181
2、发送消息
把消息发送给broker中的某个topic,打开⼀个kafka发送消息的客户端,然后开始⽤客户端向kafka服务器发送消息
./kafka-console-producer.sh --broker-list localhost:9092 --topic test
3、消费消息
打开⼀个消费消息的客户端,向kafka服务器的某个主题消费消息
- ⽅式⼀:从当前主题中的
最后⼀条消息
的offset(偏移量位置)+1开始消费
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test
- ⽅式⼆:从当前主题中的第⼀条消息开始消费
--from-beginning
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --from-beginning --topic test
4、关于消息的细节
5、单播消息
在⼀个kafka的topic中,启动两个消费者,⼀个⽣产者,问:⽣产者发送消息,这条消息是否同时会被两个消费者消费?
答:如果多个消费者在同⼀个消费组,那么只有⼀个消费者可以收到订阅的topic中的消息。换⾔之,同⼀个消费组中只能有⼀个消费者收到⼀个topic中的消息。
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --consumer-property group.id=testGroup --topic test
6、多播消息
不同的消费组订阅同⼀个topic,那么不同的消费组中只有⼀个消费者能收到消息。实际上也是多个消费组中的多个消费者收到了同⼀个消息。
./kafka-console-consumer.sh --bootstrap-server 172.16.253.38:9092 --consumer-property group.id=testGroup1 --topic test
./kafka-console-consumer.sh --bootstrap-server 172.16.253.38:9092 --consumer-property group.id=testGroup2 --topic test
9、查看消费组的详细信息
./kafka-consumer-groups.sh --bootstrap-server 172.16.253.38:9092 --describe --group testGroup
重点关注以下⼏个信息:
- current-offset: 最后被消费的消息的偏移量
- Log-end-offset: 消息总量(最后⼀条消息的偏移量)
- Lag:积压了多少条消息
六、Kafka中主题和分区的概念
1、主题Topic
主题-topic在kafka中是⼀个逻辑的概念,kafka通过topic将消息进⾏分类
。不同的topic会被订阅该topic的消费者消费。
但是有⼀个问题,如果说这个topic中的消息⾮常⾮常多,多到需要⼏T来存,因为消息是会被保存到log⽇志⽂件中的。为了解决这个⽂件过⼤的问题,kafka提出了Partition分区的概念。
2、分区Partition
1)分区的概念
通过partition将⼀个topic中的消息分区来存储。这样的好处有多个:
- 分区存储,可以解决统⼀存储⽂件过⼤的问题
- 提供了读写的吞吐量:读和写可以同时在多个分区中进⾏
2)创建多分区的主题
./kafka-topics.sh --create --zookeeper 172.16.253.35:2181 --replication-factor 1 --partitions 2 --topic test1
3、kafka中消息⽇志⽂件中保存的内容
- 00000.log: 这个⽂件中保存的就是消息
- _consumer_offsets-49:
kafka内部⾃⼰创建了__consumer_offsets主题包含了50个分区。这个主题⽤来存放消费
者消费某个主题的偏移量。因为每个消费者都会⾃⼰维护着消费的主题的偏移量,也就是说每个消费者会把消费的主题的偏移量⾃主上报给kafka中的默认主题:consumer_offsets。因此kafka为了提升这个主题的并发性,默认设置了50个分区。
提交到哪个分区:通过hash函数:hash(consumerGroupId) % __consumer_offsets主题的分区数
提交到该主题中的内容是:key是consumerGroupId+topic+分区号,value就是当前offset的值
- ⽂件中保存的消息,默认保存7天。七天到后消息会被删除。
七、Kafka集群
1、搭建kafka集群(三个broker)
查看:
2、副本的概念
在创建主题时,除了指明了主题的分区数以外,还指明了副本数,那么副本是⼀个什么概念呢? --replication-factor 3
./kafka-topics.sh --create --zookeeper 172.16.253.35:2181 --replication-factor 3 --partitions 2 --topic my-replicated-topic
副本是为了为主题中的分区创建多个备份,多个副本在kafka集群的多个broker中,会有⼀个副本作为leader,其他是follower。
查看topic情况:
./kafka-topics.sh --describe --zookeeper 172.16.253.35:2181 --topic my-replicated-topic
一个主题两个分区三个副本
(重点~!)此时,broker、主题、分区、副本 这些概念就全部展现了,⼤家需要把这些概念梳理清楚:
集群中有多个broker,创建主题时可以指明主题有多个分区(把消息拆分到不同的分区中存储),可以为分区创建多个副本,不同的副本存放在不同的broker⾥。
3、集群消费
1)向集群发送消息:
./kafka-console-consumer.sh --bootstrap-server172.16.253.38:9092,172.16.253.38:9093,172.16.253.38:9094 --from-beginning --consumer-property group.id=testGroup1 --topic my-replicated-topic
2)从集群中消费消息
./kafka-console-producer.sh --broker-list172.16.253.38:9092,172.16.253.38:9093,172.16.253.38:9094 --topic my-replicated-topic
3)指定消费组来消费消息
./kafka-console-consumer.sh --bootstrap-server 172.16.253.38:9092,172.16.253.38:9093,172.16.253.38:9094 --from-beginning --consumer-property group.id=testGroup1 --topic my-replicated-topic
4)分区分消费组的集群消费中的细节
八、Kafka的java客户端-生产者的实现
1、⽣产者的基本实现
引⼊依赖
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.4.1</version>
</dependency>
具体实现
2、⽣产者的同步发送消息
如果⽣产者发送消息没有收到ack,⽣产者会阻塞,阻塞到3s的时间,如果还没有收到消息,会进⾏重试。重试的次数3次。
RecordMetadata metadata = producer.send(producerRecord).get();
System.out.println("同步⽅式发送消息结果:" + "topic-" + metadata.topic() + "|partition-" + metadata.partition() + "|offset-" + metadata.offset());
3、⽣产者的异步发送消息
异步发送,⽣产者发送完消息后就可以执⾏之后的业务,broker在收到消息后异步调⽤⽣产者提供的callback回调⽅法。
4、⽣产者中的ack的配置
下⾯是关于ack和重试(如果没有收到ack,就开启重试)的配置:
5、关于消息发送的缓冲区
九、Java客户端消费者的实现细节
1、消费者的基本实现
2、关于消费者⾃动提交和⼿动提交offset
1)提交的内容
消费者⽆论是⾃动提交还是⼿动提交,都需要把所属的消费组+消费的某个主题+消费的某个分区及消费的偏移量,这样的信息提交到集群的_consumer_offsets主题⾥⾯。
2)⾃动提交
消费者poll消息下来以后就会⾃动提交offset
// 是否⾃动提交offset,默认就是true
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
// ⾃动提交offset的间隔时间
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
注意:⾃动提交会丢消息。因为消费者在消费前提交offset,有可能提交完后还没消费时消费者挂了。
3)⼿动提交
需要把⾃动提交的配置改成false
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
⼿动提交⼜分成了两种:
-
⼿动同步提交
在消费完消息后调⽤同步提交的⽅法,当集群返回ack前⼀直阻塞,返回ack后表示提交成功,执⾏之后的逻辑
-
⼿动异步提交
在消息消费完后提交,不需要等到集群ack,直接执⾏之后的逻辑,可以设置⼀个回调⽅法,供集群调⽤
3、⻓轮询poll消息
默认情况下,消费者⼀次会poll500条消息。
//⼀次poll最⼤拉取消息的条数,可以根据消费速度的快慢来设置
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500);
代码中设置了⻓轮询的时间是1000毫秒
意味着:
4、消费者的健康状态检查
消费者每隔1s向kafka集群发送⼼跳,集群发现如果有超过10s没有续约的消费者,将被踢出消费组,触发该消费组的rebalance机制,将该分区交给消费组⾥的其他消费者进⾏消费。
5、指定分区和偏移量、时间消费
指定分区消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
从头消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
consumer.seekToBeginning(Arrays.asList(new TopicPartition(TOPIC_NAME,0)));
指定offset消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
consumer.seek(new TopicPartition(TOPIC_NAME, 0), 10);
指定时间消费
根据时间,去所有的partition中确定该时间对应的offset,然后去所有的partition中找到该offset之后的消息开始消费。
6、新消费组的消费offset规则
新消费组中的消费者在启动以后,默认会从当前分区的最后⼀条消息的offset+1开始消费(消费新消息)。可以通过以下的设置,让新的消费者第⼀次从头开始消费。之后开始消费新消息(最后消费的位置的偏移量+1)
十、kafka集群中的controller、rebalance、HW
1、controller
2、rebalance机制
3、HW和LEO
LEO是某个副本最后消息的消息位置(log-end-offset)
HW是已完成同步的位置。消息在写⼊broker时,且每个broker完成这条消息的同步后,hw才会变化。在这之前消费者是消费不到这条消息的。在同步完成之后,HW更新之后,消费者才能消费到这条消息,这样的⽬的是防⽌消息的丢失。
十一、Kafka中的优化问题
1、如何防⽌消息丢失
- ⽣产者:1)使⽤同步发送 2)把ack设成1或者all,并且设置同步的分区数>=2
- 消费者:把⾃动提交改成⼿动提交
2、如何防⽌重复消费
在防⽌消息丢失的⽅案中,如果⽣产者发送完消息后,因为⽹络抖动,没有收到ack,但实际上broker已经收到了。
此时⽣产者会进⾏重试,于是broker就会收到多条相同的消息,⽽造成消费者的重复消费。
怎么解决:
3、如何做到消息的顺序消费
4、如何解决消息积压问题
1)消息积压问题的出现
消息的消费者的消费速度远赶不上⽣产者的⽣产消息的速度,导致kafka中有⼤量的数据没有被消费。随着没有被消费的数据堆积越多,消费者寻址的性能会越来越差,最后导致整个kafka对外提供的服务的性能很差,从⽽造成其他服务也访问速度变慢,造成服务雪崩。
2)消息积压的解决⽅案
5、实现延时队列的效果
1)应⽤场景
订单创建后,超过30分钟没有⽀付,则需要取消订单,这种场景可以通过延时队列来实现
2)具体⽅案
十二、Kafka-eagle监控平台
1、搭建
去kafka-eagle官⽹下载压缩包:http://download.kafka-eagle.org/
修改配置