kafka优点:
1.解耦:原本单一的处理过程一拆为二,相当于你可以独立扩展和修改(遵循同样的接口约束)
2.可恢复性:即使消息进程挂了,仍可在系统恢复后被处理
3.缓冲:可以理解为生产消息的速度和消费速度不一致(应该是生产速度大于消费),这种控制和优化数据的流经系统的速度是可控的
4.削峰:缓冲不就可以削峰
5.灵活:分布式的话动态增减机器
6.异步通信
模式:
1.点对点模式:一对一
2.发布/订阅:一对多
kafka架构:
kafka Cluster:集群
broker:服务器,相当于进程
leader follower:主+从
topic + partition:分类(逻辑分区+区分消费类型)+分区(物理分区+提高消费能力=提高并发)
l:leader f:follower
producer1 cluster broker1 topicA+part1(l) topicA+part2(f) topicA+part3(f) topicB+part1(f)
producer1 cluster broker2 topicA+part2(l) topicA+part1(f) topicA+part3(f) topicB+part1(f)
producer1 cluster broker3 topicA+part3(l) topicA+part1(f)topicA+part2(f) topicB+part1(f)
producer2 cluster broker4 topicB+part1(l) topicA+part1(f)topicA+part2(f) topicA+part3(f)
follower放在同一台无意义(follower主要是备份,放同一台就都宕了),且机器数=leader+follower=统称副本
partition存了各自的offset(偏移量),并不设置全局offset,每个partition都是自己维护offset的,只能保证区内有序,0.9版本之前offset存在于zk,0.9版本之后存储在本地
partition机制:用.log文件追加数据到文件末尾,每个partition对应一个文件夹,文件名为topic名称+分区序号,为防止.log数据过大采用分片和索引机制,将每个partition分为多个segment,一个segment对应两个文件,分别是.index文件和.log文件,.index文件以第一个message序号的偏移量命名,二分查找找到message序号,message序号对应offset,根据offset+固定size去.log文件找到数据
配置信息:kafka\config\server.properties(先启动zk后kafka)
broker.id=0 唯一整数
delete.topic.enable=true topic是否可删 打开可删
log.retention.hours=168 数据保留时间
zookeeper.connect=... zk集群地址
分区原则:
1.指明partition
2.没有partition但有key,取key的hash值与partition个数进行取模运算,取余作为partition
3.没有partition没有key,随机生成一个整数,以递增的整数与partition个数进行取模运算(轮询round-robin算法)
副本数据同步策略:
1.半数以上完成同步,就发ack
2.全部完成发送ack
达到相同的效果,第一种方式需要的机器比第二种方式多n台(为了容忍n台节点故障,第一种方式需要2n+1,第二种需要n+1台),第二种方式比第一种延迟高
3.isr:in-sync replica set(同步副本集)
leader维护了一个动态的ISR,和leader保持同步的follower集合。
根据同步的时间(replica.lag.time.max.ms=10000ms)和同步条数的误差(replica.lag.max.messages)去维护ISR,高版本去掉了同步条数的误差,由于producer是batch入队,如果batch数量大于同步条数的误差,导致ISR删除所有的follower的记录,但是在最大同步时间内follower又同步完后又写入ISR,频繁占用资源,而zk有存在这部分数据,那么需要频繁地与zk交互。
数据丢失:(acks参数配置)
acks:0 (at most once)不等待leader落盘返回ack,broker故障就会丢失数据
acks:1 等待leader罗盘后返回ack,follower同步未完成,leader故障就会丢失数据
acks:-1(at least once)等待partition的leader和ISR中的follower全部落盘后返回ack,如果follower也落盘了,但是leader返回ack之前leader挂了,根据已经落盘的ISR选举leader,producer重新发送数据到新leader会造成数据重复。(极限情况下ISR只有leader活着,然后活着还落盘了,落盘后leader还挂掉了,那就会丢失数据,考虑补偿机制)
幂等性:(at least once + 幂等= exactly once)
如果天然幂等不需要考虑,否则producer的参数中的enable.idompotence=true,则自动开启ack=-1
开启后broker通过
但是生产者重启会重置pid,重置后就无法保证原来partion维护同一个pid
数据一致性:
(leader故障后,数据同步不一致,重新选举leader后,导致消费数据的不一致)
HW:High Watermark(类似木桶效应的短板,以HW为界限内的数据暴露给消费者,保证消费数据的一致性)
LEO:Log End Offset
follower故障,将follower踢出ISR,follower恢复后,根据HW截掉HW以后的数据,从leader中同步数据直至follower的LEO大于等于leader的HW后加入ISR
leader故障,将其余follower的HW后的数据截掉,然后从leader中同步
消费者方式:
push模式:推送,很难适应消费速率不同的消费者,因为发送速率是由broker决定的
pull模式:拉取,如果kafka没有数据,消费者会空转,陷入空轮询。解决方法:如果当前没有数据消费,consumer会等待一段时间之后再返回,设置这个参数为timeout。(采取)
分区分配策略:
前提:一个consumer group会有多个consumer,一个topic有多个partition
分配策略:一个RoundRobinAssignor(轮询),一个是RangeAssignor(范围分区)
roundrobin是把consumer group订阅的所有topic的TopicAndPartition的hash值(消费组订阅的所有topic与其相对应的partition取hash值)去统一去轮询 ,这种策略需要一个consumer group对应一个topic,如果对应多个topic,就不是完全的轮询分配,当分配的时候没订阅的将分不到partition的。
range是按照单个topic的partition序号排列去分配的(分区数除以消费者数),将该topic的paritition排列分配给消费组内所有订阅这个topic的消费者。其他topic的分配同上。
总结:Kafka提供了消费者客户端参数 partition.assignment.strategy 用来设置消费者与订阅主题之间的分区分配策略。默认情况下,此参数的值为:org.apache.kafka.clients.consumer.RangeAssignor,即采用RangeAssignor分配策略。一个consumer group对应同一个topic的时候可以考虑RoundRobinAssignor,同一组的消费者消费数最大差也就1
重新分配:
消费组的消费者数量发生变化和分区数量发生变化会重平衡(rebalance)
offset的维护:
consumer group+topic+partition去维护offset
kafka高效读写数据:
1.顺序写磁盘
2.零复制技术,我的理解是跳过用户态也就是用户空间,直接由操作系统命令完成复制
kafka事务:(主要是生产者事务)
通过transaction coordinator组件引入全局transactionId,producer通过和transaction coordinator交互,得到transactionId对应的任务状态。
重启后pid改变了,但是transactionId对应的状态还在,进行中的任务得到恢复,因为绑定了transactionId和pid。(不确定)
kafkaProducer发送消息流程:
在main线程里 producer send(ProducerRecord)通过interceptors(拦截器序列化前是string)通过serializer通过partitioner(分区器序列化后是byte数组)到recordAccumulator
springboot配置信息:
spring:
kafka:
#============== kafka ===================
# 指定kafka server的地址,集群配多个,中间,逗号隔开
bootstrap-servers: XXX.XXX.XXX.XXX
#=============== consumer =======================
consumer:
# 指定默认消费者group id --> 由于在kafka中,同一组中的consumer不会读取到同一个消息,依靠groud.id设置组名
group-id: lts-consumer-group
# earliest和largest才有效,如果earliest重新0开始读取,如果是largest从logfile的offset读取。一般情况下都是设置earliest
auto-offset-reset: earliest
# enable.auto.commit:true --> 设置自动提交offset
enable-auto-commit: true
# 如果'enable.auto.commit'为true,则消费者偏移自动提交给Kafka的频率(以毫秒为单位),默认值为5000。
auto-commit-interval: 5000
# 指定消息key和消息体的编解码方式
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
#=============== provider =======================
producer:
# 写入失败时,重试次数。当leader节点失效,一个repli节点会替代成为leader节点,此时可能出现写入失败,
# 当retris为0时,produce不会重复。retirs重发,此时repli节点完全成为leader节点,不会产生消息丢失。
retries: 0
# 每次批量发送消息的数量,produce积累到一定数据,一次发送(producer send(ProducerRecord))
batch-size: 16384
# produce积累数据一次发送,缓存大小达到buffer.memory就发送数据(recordAccumulator)
buffer-memory: 33554432
# procedure要求leader在考虑完成请求之前收到的确认数,用于控制发送记录在服务端的持久化,其值可以为如下:
# acks = 0 如果设置为零,则生产者将不会等待来自服务器的任何确认,该记录将立即添加到套接字缓冲区并视为已发送。在这种情况下,无法保证服务器已收到记录,并且重试配置将不会生效(因为客户端通常不会知道任何故障),为每条记录返回的偏移量始终设置为-1。
# acks = 1 这意味着leader会将记录写入其本地日志,但无需等待所有副本服务器的完全确认即可做出回应,在这种情况下,如果leader在确认记录后立即失败,但在将数据复制到所有的副本服务器之前,则记录将会丢失。
# acks = all 这意味着leader将等待完整的同步副本集以确认记录,这保证了只要至少一个同步副本服务器仍然存活,记录就不会丢失,这是最强有力的保证,这相当于acks = -1的设置。
# 可以设置的值为:all, -1, 0, 1
acks: 1
# 指定消息key和消息体的编解码方式
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer