Kafka
consumer-group
消费者组,可以将多个消费者组成一个消费者组,同一个组内的消费者消费的数据是不同的,不同组的消费者消费的数据是重复的,而且分区和消费者组是绑定的。
比如一个topic有2个分区,一个消费者组有2个消费者,那么一个消费者消费一个分区
比如一个topic有1个分区,一个消费者组有2个消费者,那么一个消费者消费一个分区,另外那个消费者啥也不干
比如一个topic有3个分区,一个消费者组有2个消费者,那么其中一个消费者消费一个分区,另外一个消费者消费两个分区
结论,一个消费者组内的消费者消费的数据是瓜分消费,而且是对分区进行瓜分,而不是对分区内的数据进行瓜分,一个分区内的数据一定只能由一个消费者消费
partition是不能被切分的,只能有一个消费者去处理,partition是能被消费的最小粒度
broker
一个kafka集群由多个broker组成,每个broker是一台服务器
Topic-partition
每个Topic都有多个分区,分区数据相互独立,数据到kafka之后,会根据一定策略,将数据分发到不同的分区(轮询、权重、哈希取余等等),因此一个topic中的数据是分区有序全局无序的,如果想有序那就设置一个分区
每个分区都可设置多个分区副本,副本之间内容一样
其中一个副本为leader副本,负责通信、写入写出,其余副本为follower负责数据存储和数据同步,如果leader副本故障,follower副本会重新推选一个leader
同一个分区的多个副本,正常来说会分布在不同的broker上
kafka-broker-topic-partition
每个Topic的partition分区的存储方式,这里分区的副本个数一定要小于kafka集群的broker个数
KafkaProducer producer = new KafkaProducer(props);
package com.ws.kafka
import java.util.Properties
import org.apache.kafka.clients.producer.{KafkaProducer, ProducerRecord}
import org.apache.kafka.common.serialization.StringSerializer
object KProducer {
def main(args: Array[String]): Unit = {
val prop = new Properties()
// 指定kafka的server地址
prop.setProperty("bootstrap.servers","dream1:9092,dream2:9092,dream3:9092")
// 设置反序列化组件 一下两种方式都可以
prop.setProperty("key.serializer",classOf[StringSerializer].getName)
prop.setProperty("value.serializer","org.apache.kafka.common.serialization.StringSerializer")
// 主题名称
val topic = "wordcount"
// 创建生产者
val producer: KafkaProducer[String, String] = new KafkaProducer[String, String](prop)
for (i <- 1 to 10){
// 指定编号的数据
val record1 = new ProducerRecord[String,String](topic,1,System.currentTimeMillis(),"key"+i,"value"+i)
// 轮询分区,根据分区编号
val par = i % 3
val record2 = new ProducerRecord[String,String](topic,par,System.currentTimeMillis(),"key"+i,"value"+i)
// 根据key进行分区 key.hashcode % partationnum
val record3 = new ProducerRecord[String,String](topic,"key","value"+i)
// 什么都不指定,默认策略(轮询)
val record4 = new ProducerRecord[String,String](topic,"value"+i)
// 发送消息
producer.send(record4)
}
producer.close()
}
}
KafkaConsumer consumer = new KafkaConsumer(props);
package com.ws.kafka
import java.util
import java.util.Properties
import org.apache.kafka.clients.consumer.{ConsumerRecords, KafkaConsumer}
import org.apache.kafka.common.serialization.StringDeserializer
object KConsumer {
def main(args: Array[String]): Unit = {
val prop = new Properties()
// 指定kafka的server地址
prop.setProperty("bootstrap.servers","dream1:9092,dream2:9092,dream3:9092")
// 设置反序列化组件 一下两种方式都可以
prop.setProperty("key.deserializer",classOf[StringDeserializer].getName)
prop.setProperty("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer")
// 指定该消费者所在的消费者组
prop.setProperty("group.id","1")
// 指定消费者从哪开始消费[latest,earliest]
// earliest 从头开始消费,--from beginnig
// latestb 从该消费者启动之后开始消费
prop.setProperty("auto.offset.reset","earliest")
// 是否自动提交偏移量,默认是true,就是每消费一条数据后,就向kafka报告当前消费的偏移量,来控制消费果的数据不再重复消费
// 我们一般设置为false,自己手动更新偏移量
prop.setProperty("enable.auto.commit","true")
//-------------------以上是消费者的配置代码-------------------------------
// 创建一个消费者
val kafkaConsumer = new KafkaConsumer[String, String](prop)
// 指定要订阅的topic
val topic: util.List[String] = util.Arrays.asList("wordcount")
kafkaConsumer.subscribe(topic)
// 监听数据产生,并拉取数据
import scala.collection.JavaConversions._
while (true) {
// 超过2000就放弃
val msg: ConsumerRecords[String, String] = kafkaConsumer.poll(2000)
msg.foreach(ms=>println(ms))
}
}
}
Acks机制
原理,生产者收到ack消息后才会写入下一条数据
ack=0
kafka有没有保存好,直接返回ack,生产者发送下一条数据,这样快,丢失率高
ack=1
生产者等待接收分区的leader副本写入成功后,才返回ack,生产者才写入下一条,这样速度一般,丢失率很小
ack=-1/all
生产者待接收分区的所有副本写入成功后,才返回ack,生产者才写入下一条,这样速度较慢,数据安全,无丢失
前提是 min.insync.replicas >=2 这个参数的意思是 需要同步的副本数量,如果min.insync.replicas=1 那么ack=-1和ack=1的效果是一样的
kafka幂等性重复校验
生产者每生产一条数据,携带一个id值,下一条时,id自增1,kafka写入之成功后记录上次的id值(last_id),如果再来一条携带的id(now_id)值与上次的id(last_id)值相同,直接返回ack,不进行写入操作
写入数据流程
1、producer 汇集一个batch数据,交给kafka集群
2、kafka通过集群分区规则,找的所在分区的leader副本进行数据写入pagecatch
3、leader副本后台会将pagecatch的数据持久化到磁盘上,形成segment
4、followe副本会向leader副本拉取同步数据
读取数据流程
1、consumer根据 Topic,分区,offset找,负责的leader副本
2、首先去pagecatch里查,如果有直接返回
3、如果没有,根据offset找到 segment(segment是根据offset命名的),segment中包括index索引文件,.log数据文件。
4、先找index(索引文件),然后去.log 里查找对应的数据
持久化数据位置,config/server.properties