Kafka安装基于Zookeeper
broker.id=0 # 集群中必须唯一
port=9092 #端口号
host.name=localhost #Kafka部署机器的IP
log.dirs=/usr/local/kafka/data/kafka-logs #日志存放路径可修改可不修改
zookeeper.connect=localhost:2181 #zookeeper地址和端口
./kafka-server-start.sh -daemon ../config/server.properties #建议使用这种方式,不需要启动多个窗口
创建主题Topic
通过Kafka命令向zk中创建一个主题:
bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic demo
查看当前zk中所有主题:
bin/kafka-topics.sh --list --zookeeper localhost:2181
发送消息
bin/kafka-console-producer.sh --broker-list 192.168.xxx.xxx:9092 --topic demo
消费消息
bin/kafka-console-consumer.sh --bootstrap-server 192.168.xxx.xxx:9092 --topic demo --from-beginning
- -from-beginning:加上表示从头开始消费,不加表示从最后一条消息后准备开始消费。
生产者将消息发送给broker,broker会将消息保存在本地日志文件中:
/usr/local/kafka/data/kafka-logs/主题-分区/00000000.log
单播消息
如果多个消费者在同一个消费组,那么只有一个消费者可以收到订阅的topic中的消息
./kafka-console-consumer.sh --bootstrap-server 192.168.xxx.xxx:9092 --topic demo --from-beginning --consumer-property group.id=testgroup1
多播消息
不同的消费组订阅同一个topic,每个消费组里都会有一个消费者能收到消息
./kafka-console-consumer.sh --bootstrap-server 192.168.xxx.xxx:9092 --topic demo --from-beginning --consumer-property group.id=testgroup1
./kafka-console-consumer.sh --bootstrap-server 192.168.xxx.xxx:9092 --topic demo --from-beginning --consumer-property group.id=testgroup2
另外,查看消费组详情命令:
./kafka-consumer-groups.sh --bootstrap-server 192.168.xxx.xxx:9092 --describe --group testgroup1
主题Topic
kafka通过topic将消息进行分类,不同的topic会被订阅该topic的消费者消费
但是有个问题,如果说这个topic中的消息非常多,多到需要几个T来存,因为消息是保存在log日志文件中的,为了解决这个问题,kafka给出分区解决
分区Partition
一个Topic里有多个分区
创建多分区主题:
./kafka-topics.sh --create --zookeeper localhost:2181 --topic test1 --replication-factor 1 --partitions 2
好处:
- 可以分区存储大文件
- 提高吞吐量,可以并行进行读写
Kafka默认有一个偏移量consumer_offsets主题,且默认有50个分区。这个主题用来存储消费者的偏移量相关信息。
搭建3个broker:
# 分别配置0、1、2
broker.id=0 # 集群中必须唯一
# 分别配置9092、9093、9094
port=9092 #端口号
host.name=localhost #Kafka部署机器的IP
# 分别配置kafka-logs、kafka-logs-1、kafka-logs-2
log.dirs=/usr/local/kafka/data/kafka-logs #日志存放路径可修改可不修改
# 集群节点连接同一个zookeeper
zookeeper.connect=localhost:2181 #zookeeper地址和端口
./kafka-server-start.sh -daemon ../config/server.properties #建议使用这种方式,不需要启动多个窗口
在集群中创建1个主题,2个分区,3个副本:
bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 2 --topic demo
查看集群中主题的详情信息:
bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic demo
执行命令可以看出分区0的Leader是broker2,分区1的Leader是broker0:
Isr:可以同步和已经同步的节点会存入Isr集合。但如果Isr中的节点性能较差,会被踢出Isr集合。
Leader
kafka的写和读操作都发生在leader上,leader负责把数据同步给follower,当leader挂了,经过主从选举,从follower中选举产生一个新的leader
Follower
接受leader同步的数据
为了保证消费消息的循序性,一个Partition只能被一个消费组里的某一个消费者消费,但一个消费者可以同时消费多个Partition。
集群发送消息
bin/kafka-console-producer.sh --broker-list 192.168.xxx.xxx:9092,192.168.xxx.xxx:9093,192.168.xxx.xxx:9094 --topic demo
集群消费消息
bin/kafka-console-consumer.sh --bootstrap-server 192.168.xxx.xxx:9092,192.168.xxx.xxx:9093,192.168.xxx.xxx:9094 --topic demo --from-beginning --consumer-property group.id=testgroup1
生产者向kafka发送消息有同步和异步两种方式,此阶段异步容易丢消息且性能提升不明显,故常用同步方式
同步方式需要等待kafka发送成功的ack,关于kafka返回ack的时机有三个配置:
自动提交:消费者把消息poll下来以后自动直接提交offset
手动提交:在消费消息后在手动提交offset
引入依赖
<dependency>
<groupId>org.springframework.kafkagroupId>
<artifactId>spring-kafkaartifactId>
dependency>
编写springboot配置文件
spring:
application:
name: hello-kafka
kafka:
listener:
#设置是否批量消费,默认 single(单条),batch(批量)
type: single
# 集群地址
bootstrap-servers: 127.0.0.1:9092,127.0.0.1:9093,127.0.0.1:9094
# 生产者配置
producer:
# 重试次数
retries: 3
# 应答级别
# acks=0 把消息发送到kafka就认为发送成功
# acks=1 把消息发送到kafka leader分区,并且写入磁盘就认为发送成功
# acks=all 把消息发送到kafka leader分区,并且leader分区的副本follower对消息进行了同步就任务发送成功
acks: all
# 批量处理的最大大小 单位 byte
batch-size: 4096
# 发送延时,当生产端积累的消息达到batch-size或接收到消息linger.ms后,生产者就会将消息提交给kafka
buffer-memory: 33554432
# 客户端ID
client-id: hello-kafka
# Key 序列化类
key-serializer: org.apache.kafka.common.serialization.StringSerializer
# Value 序列化类
value-serializer: org.apache.kafka.common.serialization.StringSerializer
# 消息压缩:none、lz4、gzip、snappy,默认为 none。
compression-type: gzip
properties:
partitioner:
#指定自定义分区器
class: top.zysite.hello.kafka.partitioner.MyPartitioner
linger:
# 发送延时,当生产端积累的消息达到batch-size或接收到消息linger.ms后,生产者就会将消息提交给kafka
ms: 1000
max:
block:
# KafkaProducer.send() 和 partitionsFor() 方法的最长阻塞时间 单位 ms
ms: 6000
# 消费者配置
consumer:
# 默认消费者组
group-id: testGroup
# 自动提交 offset 默认 true
enable-auto-commit: false
# 自动提交的频率 单位 ms
auto-commit-interval: 1000
# 批量消费最大数量
max-poll-records: 100
# Key 反序列化类
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
# Value 反序列化类
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
# 当kafka中没有初始offset或offset超出范围时将自动重置offset
# earliest:重置为分区中最小的offset
# latest:重置为分区中最新的offset(消费分区中新产生的数据)
# none:只要有一个分区不存在已提交的offset,就抛出异常
auto-offset-reset: latest
properties:
interceptor:
classes: top.zysite.hello.kafka.interceptor.MyConsumerInterceptor
session:
timeout:
# session超时,超过这个时间consumer没有发送心跳,就会触发rebalance操作
ms: 120000
request:
timeout:
# 请求超时
ms: 120000
编写生产者代码
@Service
public class KafkaProducerService {
private final static String TOPIC_NAME="my-topic";
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendMessage(){
kafkaTemplate.send(TOPIC_NAME,0,"key","this is a message!");
}
}
编写消费者代码
@Service
public class KafkaConsumerService {
@KafkaListener(topics = "my-topic",groupId = "MyGroup1")
public void listenMessage(ConsumerRecord<String,String> record, Acknowledgment ack){
System.out.println(record.value());
//手动提交offset
ack.acknowledge();
}
}
参考:https://blog.csdn.net/qq_39340792/article/details/117534578
参考视频:https://www.bilibili.com/video/BV1Xy4y1G7zA?p=23&spm_id_from=pageDriver&vd_source=d902bddfa2b1d73669d22889b25198a2
负责几件事:
HW和LEO
LEO是某个副本最后消息的消息位置(log-end-offset)
HW是已完成同步的位置。消息在写入broker时,且每个broker完成这条消息的同步后,hw才会变化。在这之前消费者是消费不到这条消息的。在同步完成之后,HW更新之后,消费者才能消费到这条消息,这样的目的是防止消息的丢失。
如何防止消息丢失
生产者:1) 使用同步发送 2) 把ack设成1或者all,并且设置同步的分区数>=2
消费者:把自动提交改成手动提交
如何防止重复消费
在防止消息丢失的方案中,如果生产者发送完消息后,因为网络抖动,没有收到ack,但实际上broker已经收到了。此时生产者会进行重试,于是broker就会收到多条相同的消息,而造成消费者的重复消费。
怎么解决:
– 生产者关闭重试:会造成丢失消息(不建议)
– 消费者解决非幂等性消费问题:
所谓幂等性:多次访问的结果是一样的。对于restful的请求(get(幂等), post(非幂等), put(幂等), delete(幂等))
解决方案:
在数据库中创建联合索引,防止相同的主键创建出多条记录
使用分布式锁,以业务id为锁,保证只有一条记录能够创建成功
如何做到消息的顺序消费
– 生产者:保证消息按顺序消费,且消息不丢失 – 使用同步的发送,ack设置成非0的值
– 消费者:主题只能设置一个分区,消费组中只能有一个消费者。
kafka的顺序消费使用场景不多,因为牺牲掉了性能,但是比如RocketMQ在这一块有专门的功能已设计好。
如何解决消息积压问题
消息积压问题的出现:
消息的消费者的消费速度,远赶不上生产者的生产消息的速度,导致kafka中有大量的数据没有被消费。随着没有被消费的数据堆积越多,消费者寻址的性能会越来越差,最后导致整个kafka对外提供的服务的性能很差,从而造成其他服务也访问速度变慢,造成服务雪崩。
消息积压的解决方案:
– 在这个消费者中,使用多线程,充分利用机器的性能进行消息消费。
– 通过业务的架构设计,提升业务层面消费的性能。
– 创建多个消费组,多个消费者,部署到其他机器上,一起消费,提高消费者的消费速度。
– 创建一个消费者,该消费者在kafka另建一个主题,配上多个分区,多个分区再配上多个消费者。该消费者将poll下来的消息,不进行消费,直接转发到新建的主题上。此时,新的主题的多个分区的多个消费者就开始一起消费了(不常用)。
Kafka本身不带延迟机制,故只能以消费者逻辑实现的方式到达延时队列的效果。
可以通过可视化界面查看Kafka运行情况。安装略。