深入了解kafka(二)

1.kafka中topic的概念

在kafka中,topic只是存储消息的一个逻辑的概念,他并没有实际的文件存在磁盘上,可以认为是某一类型的消息的集合。所有发送到kafka上的消息都一个类型,这个类型就是他的topic。在物理上来说,不同的topic的消息是分开存储的。同时,一个topic可以有多个producer和多个consumer。
在这里插入图片描述

2.kafka中partition的概念

每个topic下面可以有多个分区,每一个topic至少会有一个partition,相同的topic下面,不同的partition他的消息内容是不一样的。每一个消息在被分配到partition都会有一个offset(偏移量),表示当前消息的位置,所有的消息都是追加到partition后面的。每个分区partition的offset都是从0开始的,每个partition的消息都是由顺序的,不同的partition的消息的顺序是不保证有序的。
在这里插入图片描述
每一消息发送broker之后,broker会根据partition的规则分配到不同的partition里面。

3.kafka中topic和partition的存储

(1)我们创建一个有三个分区的topic先,创建语句如下:
./kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic quanTest
在这里插入图片描述
(2)partition是以文件的形式存储在文件系统里面的。

4.消息的分发到partition的原理

1.首先消息是我们kafka最基本的数据单元,每一个消息都是由key–>value组成的,在发送消息时,我们可以指定key,我们的生产者producer会根据key和partition的规则来确认这一个消息应该分发到哪一个partition里面。

2.我们消息分发到partition的默认的规则:当我们的key不为空的时候,我们的分区数为key的哈希值取模运算值。当我们的key为空的时候,他会随机发送,但是这个随机数是在这个参数”metadata.max.age.ms”的时间范围内随机选择一个,这个metadata.max.age.ms的值每十分钟会刷新一次。

3.当然,我们也可以自己定义消息分发到partition的规则
实验代码:
public class MyPartition implements Partitioner {

/**
 *自定义分配分区
 * @param s
 * @param o
 * @param bytes
 * @param o1
 * @param bytes1
 * @param cluster
 * @return
 */
public int partition(String s, Object o, byte[] bytes, Object o1, byte[] bytes1, Cluster cluster) {
    if (o == null) {
        return 0;
    } else {
        return 1;
    }
}

public void close() {

}

public void configure(Map<String, ?> map) {

}
}

配置设置那里指定使用自己定义的分区策略:(有趣的事情:就是指定过分发策略后,就算取消的指定策略,还是按照原来的策略来分发数据)
public class KafkaDemoProducer extends Thread {

/**
 * 生产者
 */
private KafkaProducer<Integer, String> producer;

private String topic;
/**
 * 构造方法
 */
public KafkaDemoProducer(String topic, Boolean isAysnc) {
    //配置kafka信息
    Properties properties = new Properties();
    //kafka集群,多台机器用逗号隔开
    properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
    //配置clienId
    properties.setProperty(ProducerConfig.CLIENT_ID_CONFIG, "kafkaDemoProducer");
    //设置ack为-1,0:不用等待确认,1:等地leader确认,-1:全部确认(最安全)
    properties.setProperty(ProducerConfig.ACKS_CONFIG, "-1");
    //key序列化类配置(根据你的泛型而定)
    properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
            "org.apache.kafka.common.serialization.IntegerSerializer");
    //value序列化类配置(根据你的泛型而定)
    properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
            "org.apache.kafka.common.serialization.StringSerializer");
    //可以自定义指定数据分配到分区的celue
    properties.put("partitioner.class", "com.kafka.MyPartition");
    //根据配置文件,创建kafka
    producer = new KafkaProducer<Integer, String>(properties);
    this.topic = topic;
    this.isAysnc = isAysnc;
}
}

结果:
在这里插入图片描述

4.消费端也可以指定消费某一个分区
public class KafkaDemoConsumer extends Thread {

private KafkaConsumer<Integer, String> kafkaConsumer;

public KafkaDemoConsumer(String topic) {
    Properties properties = new Properties();
    //kafka服务器集群
    properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
            "127.0.0.1:9092");
    //配置kafka的分组ID
    properties.put(ConsumerConfig.GROUP_ID_CONFIG, "KafkaConsumerDemo");
    //配置是否自动提交
    properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
    //配置自动提交时间
    properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
    //配置key的序列化类型
    properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
            "org.apache.kafka.common.serialization.IntegerDeserializer");
    //配置value的序列化类型
    properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
            "org.apache.kafka.common.serialization.StringDeserializer");
    //配置新加入的消费组从哪里开始消费
    properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");

    kafkaConsumer = new KafkaConsumer(properties);

    //消费指定的分区
    TopicPartition topicPartition = new TopicPartition(topic, 0);
    kafkaConsumer.assign(Arrays.asList(topicPartition));
}
}

实验结果:
在这里插入图片描述

5.消息的消费原理

1.简要说明:在真实的生产环境中,我们都是有多个partition的,使用多个partition的好处在于能有效的对broker上面的数据进行分片,减少io性能问题。同时为了提高消费能力,我们会有多个consumer对数据进行消费。在多个consumer,多个partition的消费策略有时什么?我们有分组group的概念,我们知道相同topic下的同一个partition只能被一个consumer消费。组内的所有consumer订阅这个topic下的所有的消息。
在这里插入图片描述
实验:创建一个三个分区的topic,使用三个consumer,会发现三个consumer会消费不同的partition
实验结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.对于consumer消费端而言他的分区分配策略是:
(1)默认是范围分区:Range:他会先对所有的分区按照序号进行排序,所有的consumer按字母进行排序,然后将partitions的个数除于消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区。弊端是:当有多个topic是这样的情况的时候,会出现某一个consumer的压力会很大。
在这里插入图片描述
(2)第二种就是轮询分区策略(RoundRobin strategy)
轮询分区策略就是先把所有的partition和所有的consumer都列出来,然后进行hashCode,根据hash值排序,然后根据轮询算法,把partition分配给consumer消费。如果所有consumer实例的订阅是相同的,那么partition会均匀分布。
在这里插入图片描述
使用轮询分区策略必须满足两个条件
(1)每个主题的消费者实例具有相同数量的流
(2) 每个消费者订阅的主题必须是相同的

3.partition与partition的数量关系:
(1)消费者数量多于partition的数量的时候,会有消费者消费不到数据的情况
(2)消费者数量少于partition的数量的时候,会有消费者消费多个partition
(3)一般partition是consumer的整数倍

6.kafka consumer的rebalance机制

简要的说明:kafka consumer的rebalance机制规定了同一个group下的consumer如何达成一致来消费订阅各个分区的消息,具体的策略是范围策略,或者轮询策略。
1.什么时候会触发kafka consumer的rebalance
(1) 同一个consumer group内新增了消费者
(2)消费者离开当前所属的consumer group,比如主动停机或者宕机
(3)topic新增或者减少了分区

2.如何管理rebalance,以及整个过程是怎么样的?
(1)首先我们会确定的一个coordinate角色,当启动第一个consumer的时候我们就会确定为coordinate,之后所有的consumer都会与这个coordinate保持通信。而我们的coordinate就是对consumer group进行管理。

(2)如何确定coordinate:
消费者向kafka集群中的任意一个broker发送一个GroupCoordinatorRequest请求,服务端会返回一个负载最小的broker节点的id,并将该broker设置为coordinator

(3)接着,我们会进行第一阶段joinGroup(选举leader)的过程:所有的消费者都会向consumer发送joinGroup的请求,当所有的consumer都发送了请求之后,我们的coordinate就会在选举出一个consumer来作为leader,而且会把订阅消息,组成员信息反馈回去。

在这里插入图片描述
简要的文字说明:所有的consumer都来了的时候,coordinate会选出一个leader

(4)接着再继续,我们会进入第二阶段Synchronizing Group State(同步leader的分区分配方案),这个过程简单来说就是leader把分区分配方案发送给coordinate,然后,coordinate再把这个分区发送给各个consumer。
在这里插入图片描述
简要说明:第二阶段就是把leader的分区分配方案,发送到coordinate,然后通过coordinate同步到组内的所有的consumer

7.offset

(1)什么是offset:每一个消息进入partition的时候,他都会有一个偏移量,就是offset,他代表这个消息在这个partition上面的位置,他只保证在同一个partition上面是有顺序的。
在这里插入图片描述

(2)我们是如何确保同一个分组consumer不会去重复消费同一个消息?
我们通过一个叫consumer_offset_*的topic来保存已经消费的各个组的信息,commit之后,他就会保存在对应的consumer_offset分区里面,consumer_offset默认有50个分区,具体是落在哪一个分区使用过:Math.abs(“groupid”.hashCode())%50来计算得到的。
在这里插入图片描述

消息的存储

(1)我们的kafka的消息都是日志消息(log),他不是直接存放在磁盘上面的,而是存放在对应的topic_partitionNum目录下面

(2)在多个broker和多个partition,他是这样分配的:
在这里插入图片描述
总结:他就是partition按照broker顺序一个一个轮着来分配下去

(3)我们写入的方式是顺序写入

消息的性能问题

(1)我们的kafka是零拷贝的。他大概的过程是磁盘上的数据,读到内存里面,内存里面的数据再通过socket发送到消费者
传统的过程:
在这里插入图片描述
传统过程设计到了多次的数据复制。
通过“零拷贝”技术,可以去掉这些没必要的数据复制操作,同时也会减少上下文切换次数
在这里插入图片描述
只需要一次拷贝就行,允许操作系统将数据直接从页缓存发送到网络上。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值