kafka的工作原理分析(一)

一、kafka中的topic与partition分区


首先需要了解kafka中基本的组成部分。在 kafka 中, topic 是一个存储消息的逻辑概念,可以认为是一个消息集合。每条消息发送到 kafka 集群的消息都有一个topic。物理上来说,不同的 topic 的消息是分开存储的,每个 topic 可以有多个生产者向它发送消息,也可以有多个消费者去消费其中的消息;partition分区是topic的进一步拆分,每个topic可以拆分为多个partition分区,类似于数据库中表的水平拆分,每条消息都会分到某一个分区当中,分区内部会给消息分配一个offset来表示消息的顺序所在。消息结构图大致如下。
这里写图片描述
多个生产者可以向topic发送消息,消息可以按照一定规则均匀分布在各个partition里面,由于各个partition物理上也是隔离存储的,这点就类似于数据库对于表做水平拆分来提高性能。

二、kafka的消息分发策略


了解了kafka中的基本概念之后,看一下kafka生产者对于消息分发的策略是怎么样的。
首先我们知道一条消息肯定会分发到某一个指定的分区上面,那么只需要发送消息到分区所在的broker,就可以找到对应分区。所以需要先了解

kafka集群中分区如何分配到broker上?

一个topic可以建立多个分区,当然在单机环境下分区都在一个broker上面,但是在集群环境下,分区是按照什么规则分布到集群中各台broker上面?使用的是最简单的取模算法。
例如:topic test有 p0,p1,p2,p3四个分区,有三台broker b1,b2,b3。那么分区分配到broker上面的策略就是
b1:p0,p3
b2:p1
b3:p2
简单来说就是topic排序后所在序号对broker 的size取模,结果就是所在broker。

kafka消息如何确定发送到哪个分区?

kafka的消息内容包含了key-value键值对,key的作用之一就是确定消息的分区所在。默认情况下, kafka 采用的是 hash 取模的分区算法。如果Key 为 null,则会随机分配一个分区。这个随机是在这个参数”metadata.max.age.ms”的时间范围内随机选择一个。对于这个时间段内,如果 key 为 null,则只会发送到唯一的分区。简单来说就是:hash(key) % partitions.size。

自定义消息发送分区策略

最新版的kafka支持自定义消息发送到topic的哪个分区,方式如下先实现自定义分区接口,定义一个策略。

/**
 * 自定义消息发送到哪个分区的策略
 */
public class OwnPartitionStrategy implements Partitioner{
    private Random random = new Random();
    @Override
    public int partition(String topic, Object key, byte[] bytes, Object o1, byte[] bytes1, Cluster cluster) {
        List<PartitionInfo> partitionInfos =  cluster.partitionsForTopic(topic);
        int toPar;
        if (Objects.isNull(key)){
            toPar =  random.nextInt(partitionInfos.size());
        }else {
            toPar = key.hashCode() % partitionInfos.size();
        }
        System.out.println(key + "即将发往分区:" + toPar);
        return toPar;
    }

    @Override
    public void close() {

    }

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

    }
}

然后配置producer使用该策略

        properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,
                "com.garine.learn.kafka.OwnPartitionStrategy");

三、kafka消息消费策略


固定分区消费

首先需要知道的是,消费者消费时,是可以指定消费哪一个分区的,这种方式用的比较死板,代码如下改变。

//消费指定分区的时候,不需要再订阅
//kafkaConsumer.subscribe(Collections.singleto
nList(topic));
//消费指定的分区
TopicPartition topicPartition=new TopicPartition(topic,0);
kafkaConsumer.assign(Arrays.asList(topicPartition));

这样就指定了这个消费者消费0号分区。

reblance分区消费

在实际生产过程中,每个 topic 都会有多个 partitions,多个 partitions 的好处在于,一方面能够对 broker 上的数据进行分片有效减少了消息的容量从而提升 io 性能;另外一方面,为了提高消费端的消费能力,一般会通过多个consumer 去消费同一个 topic ,也就是消费端的负载均衡机制,一般来说时一个comsumer group的所有消费者来协调消费topic里面的所有分区,一个分区只能被一个消费者消费,那么消费者被分到哪个分区的过程就是reblance。
有两种reblance策略来指定消费者消费分区。

Range strategy(范围分区)

Range 策略是对每个主题而言的,首先对同一个主题里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。 假设我们有 10 个分区, 3 个消费者,排完序的分区将会是 0, 1, 2, 3, 4, 5, 6, 7, 8, 9;消费者线程排完序将会是C1-0, C2-0, C3-0。然后将 partitions 的个数除于消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区。
以上例子:
c0:0,1,2,3
c1:4,5,6
c2:7,8,9
如果是有11个分区,那么结果就是
c0:0,1,2,3
c1:4,5,6,7
c2:8,9,10

这样的分配策略弊端就是前面的消费者需要消费更多的分区消息,负载更高,不平衡。

RoundRobin strategy(轮询分区)

轮询分区策略是把所有 partition 和所有 consumer 线程都列出来,然后按照hashcode 进行排序。最后通过轮询算法分配 partition 给消费线程。如果所有 consumer 实例的订阅是相同的,那么 partition 会均匀分布。类似于分区取模分配到不同broker,这里先把分区按照hashcode排序,假如排序结果为,2,3,6,9,8,7,4,1,5,消费者group内部消费者c0,c1,c2,那么分区结果为。
c1:2,9,4
c2:3,8,1
c3:6,7,5
按照顺序轮询下来分配。

reblance触发

1.当消费者group内部有新的消费者加入时。
2.当有消费者离开消费者group时
3.topic新增分区时
以上三种情况触发reblance策略,重新制定消费者消费分区。

Reblance的执行过程?

reblance的执行者实际上是comsumer group的leader,reblance策略由他来执行并且让把执行结果通过broker集群中的coordinator来广播到comsumer group。首先,来了解broker 集群的coordinator。

broker 集群的coordinator

coordinator 执行对于 consumergroup 的管理, 当 consumer group 的第一个 consumer 启动的时候,它会去和 kafka server 确定谁是它们组的 coordinator。之后该 group 内的所有成员都会和该 coordinator 进行协调通信。消费者向 kafka 集 群 中 的 任 意 一 个 broker 发 送 一 个GroupCoordinatorRequest 请求,服务端会返回一个负载最 小 的 broker 节 点 的 id , 并 将 该broker 设 置 为coordinator

reblance执行过程

选定coordinator之后,就需要确定谁是消费者group的leader了,整个过程分为JoinGroup和Synchronizing Group State两个阶段。

JoinGroup

整个过程目的就是选出一个消费者group 的leader,这个leader是用来实行reblance策略以及与coordinator交互广播reblance结果的一个消费者。
在这一步中,所有的成员都会向 coordinator 发送 joinGroup 的请求。一旦所有成员都发送了 joinGroup 请求,那么 coordinator 会选择一个 consumer 担任 leader 角色,并把组成员信息和订阅信息等发送给leader。
1.消费者向coordinator发送自己的protocol_metadata,member_id和group_id等信息
2.coordinator从请求的消费者成员中选择一个consumer leader
3.coordinator向所有消费者返回member_metadata,generation_id,leader_id等信息,向选中的leader附加发送members信息。leader获取到所有消费者的信息之后,就可以进行reblance策略执行,分配分区给消费者。

protocol_metadata:消费者对应的订阅信息
member_id:消费者在组内的唯一id
group_id:消费者组id
member_metadata:消费者的订阅信息
generation_id:每次选一个新的leader都递增的一个id,年代信息
leader_id:被选中的消费者的member_id
members:所有的消费者的订阅信息

Synchronizing Group State

这个阶段所有消费者向coordinator发送SyncGroupRequest请求,其中leader会在SyncGroupRequest中发送分区方案到coordinator。coordinator返回一个SyncGroupResponse响应,这个SyncGroupResponse包含消费者leader执行reblance的分区方案,这个分区方案是leader在SyncGroupRequest的时候发送到coordinator 的。这样就完成的分区方案的同步

总的来说,coordinator作为一个协调者,协调整个reblance过程,同步分区方案。

猜你喜欢

转载自blog.csdn.net/qq_20597727/article/details/81639838