Kafka(上)

本文详细介绍了Kafka的基本概念,包括broker、zookeeper、producer、consumer、consumergroup等核心组件,以及分区、副本、主题、幂等性、事务和数据一致性控制的相关知识,展示了分区策略的多种选择及其优缺点。
摘要由CSDN通过智能技术生成

kafka简介

Kafka是由Apache软件基金会开发的一个开源流平台,由Scala和Java编写。

官网描述:

Apache Kafka是一个分布式流平台。一个分布式的流平台应该包含3点关键的能力:

  1. 发布和订阅流数据流,类似于消息队列或者是企业消息传递系统
  2. 以容错的持久化方式存储数据流
  3. 处理数据流

kafka重要概念

架构

简单的结构图

在这里插入图片描述

1.broker

一个独立的kafka服务器被称为broker。broker接收来自生产者的消息,为消息设置偏移量,并提交消息到磁盘保存。broker为消费者提供服务,对读取分区的请求作出相应,返回已经提交到磁盘上的消息。

在这里插入图片描述

  • 一个Kafka的集群通常由多个broker组成,这样才能实现负载均衡、以及容错
  • broker是**无状态(Sateless)**的,它们是通过ZooKeeper来维护集群状态
  • 一个Kafka的broker每秒可以处理数十万次读写,每个broker都可以处理TB消息而不影响性能
2.zookeeper
  • ZK用来管理和协调broker,并且存储了Kafka的元数据(例如:有多少topic、partition、consumer)
  • ZK服务主要用于通知生产者和消费者Kafka集群中有新的broker加入、或者Kafka集群中出现故障的broker。

PS:Kafka正在逐步想办法将ZooKeeper剥离,维护两套集群成本较高,社区提出KIP-500就是要替换掉ZooKeeper的依赖。“Kafka on Kafka”——Kafka自己来管理自己的元数据

3.producer(生产者)
  • 生产者负责将数据推送给broker的topic
4.consumer(消费者)
  • 消费者负责从broker的topic中拉取数据,并自己进行处理
5.consumer group(消费者组)

在这里插入图片描述

  • consumer group是kafka提供的可扩展且具有容错性的消费者机制
  • 一个消费者组可以包含多个消费者
  • 一个消费者组有一个唯一的ID(group Id)
  • 组内的消费者一起消费主题的所有分区数据
6.分区(Partitions)

在这里插入图片描述

在Kafka集群中,主题被分为多个分区

7.副本(Replicas)

在这里插入图片描述

  • 副本可以确保某个服务器出现故障时,确保数据依然可用
  • 在Kafka中,一般都会设计副本的个数>1
8.主题(Topic)

在这里插入图片描述

  • 主题是一个逻辑概念,用于生产者发布数据,消费者拉取数据
  • Kafka中的主题必须要有标识符,而且是唯一的,Kafka中可以有任意数量的主题,没有数量上的限制
  • 在主题中的消息是有结构的,一般一个主题包含某一类消息
  • 一旦生产者发送消息到主题中,这些消息就不能被更新(更改)
9. 偏移量(offset)

在这里插入图片描述

  • offset记录着下一条将要发送给Consumer的消息的序号
  • 默认Kafka将offset存储在ZooKeeper中
  • 在一个分区中,消息是有顺序的方式存储着,每个在分区的消费都是有一个递增的id。这个就是偏移量offset
  • 偏移量在分区中才是有意义的。在分区之间,offset是没有任何意义的

kafka生产者幂等性

什么是幂等性

幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

幂等性产生原因
  • 前端未做限制,导致用户重复提交
  • 使用浏览器后退,或者按F5刷新,或者使用历史记录,重复提交表单
  • 网络波动,引起重复请求
  • 超时重试,引起接口重复调用
  • 定时任务设置不合理,导致数据重复处理
  • 使用消息队列时,消息重复消费
1.1.1 Kafka生产者幂等性

在生产者生产消息时,如果出现retry时,有可能会一条消息被发送了多次,如果Kafka不具备幂等性的,就有可能会在partition中保存多条一模一样的消息。

1.1.1 配置幂等性

props.put("enable.idempotence",true);

1.1.2 幂等性原理

为了实现生产者的幂等性,Kafka引入了 Producer ID(PID)和 Sequence Number的概念。

  • PID:每个Producer在初始化时,都会分配一个唯一的PID,这个PID对用户来说,是透明的,对客户使用段是不可见的。
  • Sequence Number:对于每个ProducerID,Producer发送数据的每个Topic和Partition都对应⼀个从0开始单调递增的SequenceNumber值。

在这里插入图片描述

kafka事务

事务

Kafka事务是2017年Kafka 0.11.0.0引入的新特性。类似于数据库的事务。Kafka事务指的是生产者生产消息以及消费者提交offset的操作可以在一个原子操作中,要么都成功,要么都失败。尤其是在生产者、消费者并存时,事务的保障尤其重要。(consumer-transform-producer模式)

1.事务操作API

Producer接口中定义了以下5个事务相关方法:

  1. initTransactions(初始化事务):要使用Kafka事务,必须先进行初始化操作

  2. beginTransaction(开始事务):启动一个Kafka事务

  3. sendOffsetsToTransaction(提交偏移量):批量地将分区对应的offset发送到事务中,方便后续一块提交

  4. commitTransaction(提交事务):提交事务

  5. abortTransaction(放弃事务):取消事务

2.事务相关属性配置
1.生产者
//配置事务的id,开启了事务会默认开启幂等性
props.put("transactional.id"**, **"first-transactional");
2.消费者
// 1.消费者需要设置隔离级别
props.put("isolation.level","read_committed");  
// 2.关闭自动提交
props.put("enable.auto.commit","false");  
3.生产者事务代码示例
public static void main(String[] args) {
        Consumer<String, String> consumer = createConsumer();
        Producer<String, String> producer = createProducer();
        // 初始化事务
        producer.initTransactions();

        while(true) {
            try {
                // 1. 开启事务
                producer.beginTransaction();
                // 2. 定义Map结构,用于保存分区对应的offset
                Map<TopicPartition, OffsetAndMetadata> offsetCommits = new HashMap<>();
                // 2. 拉取消息
                ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(2));
                for (ConsumerRecord<String, String> record : records) {
                    // 3. 保存偏移量
                    offsetCommits.put(new TopicPartition(record.topic(), record.partition()),
                            new OffsetAndMetadata(record.offset() + 1));
                    // 4. 进行转换处理
                    String[] fields = record.value().split(",");
                    fields[1] = fields[1].equalsIgnoreCase("1") ? "男":"女";
                    String message = fields[0] + "," + fields[1] + "," + fields[2];
                    // 5. 生产消息到dwd_user
                    producer.send(new ProducerRecord<>("dwd_user", message));
                }
                // 6. 提交偏移量到事务
                producer.sendOffsetsToTransaction(offsetCommits, "ods_user");
                // 7. 提交事务
                producer.commitTransaction();
            } catch (Exception e) {
                // 8. 放弃事务
                producer.abortTransaction();
            }
        }
    }

//可以自行模拟测试异常情况,发现如果中间出现异常的话,offset是不会被提交的,除非消费、生产消息都成功,才会提交事务。

kafka分区

Kafka的消息通过主题(Topic)进行分类,就好比是数据库的表,或者是文件系统里的文件夹。主题可以被分为若干个分区(Partition),一个分区就是一个提交日志。消息以追加的方式写入分区,然后以先进先出的顺序读取。**注意,由于一个主题一般包含几个分区,因此无法在整个主题范围内保证消息的顺序,但可以保证消息在单个分区内的顺序。**主题是逻辑上的概念,在物理上,一个主题是横跨多个服务器的。

在这里插入图片描述

1.生产者分区策略

生产者写入消息到topic,Kafka将依据不同的策略将数据分配到不同的分区中,支持自定义分区策略

  1. 如果指定了分区,那么分区器就不会再做任何事情,直接发送到该分区;
  2. 如果发送时未指定,则默认使用key的hash值指定一个分区;
  3. 如果发送时未指定消息key,则采用轮询的方式选择一个分区(这就是Kafka默认的分区策略)。
  4. 2.4版本过后Kafka默认的分区策略改为了sticky策略
轮询策略(Round-robin 默认)

轮询策略,即顺序分配。

在这里插入图片描述

  • 轮询策略是 Kafka Java 生产者 API 默认提供的分区策略;
  • 轮询策略的负载均衡表现非常优秀,总能保证消息最大限度地被平均分配到所有分区上,默认情况下它是最合理的分区策略。
随机策略(Randomness)

随意地将消息放置到任意一个分区上,但是可以从图中看出该分区策略会导致某些分区的消息过多。

在这里插入图片描述

如果要实现随机策略版的 partition 方法,很简单,只需要两行代码即可:

List partitions = cluster.partitionsForTopic(topic);
return ThreadLocalRandom.current().nextInt(partitions.size());

先计算出该主题总的分区数,然后随机地返回一个小于它的正整数。本质上看随机策略也是力求将数据均匀地打散到各个分区,但从实际表现来看,它要逊于轮询策略,所以如果追求数据的均匀分布,还是使用轮询策略比较好。事实上,随机策略是老版本生产者使用的分区策略,在新版本中已经改为轮询了。

按消息键保序策略(Key-ordering)

在这里插入图片描述

说明

  1. Kafka允许为每条消息定义消息键,简称为Key
  2. Key可以是一个有明确业务含义的字符串:客户代码、部门编号、业务ID、用来表征消息的元数据等
  3. 一旦消息被定义了Key,可以保证同一个Key的所有消息都进入到相同的分区里,由于每个分区下的消息处理都是顺序的,所以这个策略被称为按消息键保序策略

实现这个策略的 partition 方法同样简单,只需要下面两行代码即可:

List partitions = cluster.partitionsForTopic(topic);
return Math.abs(key.hashCode()) % partitions.size();
自定义分区策略

如想实现自定义分区策略,直接实现Partitioner接口,重写接口中的方法。

//kafka java api`提供了一个接口,用于自定义分区策略:`org.apache.kafka.clients.producer.Partitioner

public interface Partitioner extends Configurable, Closeable {

    /**
     * Compute the partition for the given record.
     *
     * @param topic The topic name
     * @param key The key to partition on (or null if no key)
     * @param keyBytes The serialized key to partition on( or null if no key)
     * @param value The value to partition on or null
     * @param valueBytes The serialized value to partition on or null
     * @param cluster The current cluster metadata
     */
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);

    /**
     * This is called when partitioner is closed.
     */
    public void close();

}

说明

  • partition():计算给定记录的分区

参数说明

参数说明
topic需要传递的主题
key消息中的键值
keyBytes分区中序列化过后的key,byte数组的形式传递
value消息的 value 值
valueBytes分区中序列化后的值数组
cluster当前集群的原数据
  • close() : 继承了 Closeable 接口能够实现 close() 方法,在分区关闭时调用。
  • onNewBatch(): 表示通知分区程序用来创建新的批次
各分区优缺点
  • 轮询策略(默认分区策略)

    优点:可以提供非常优秀的负载均衡能力,可以保证消息被平均分配到所有分区上。
    缺点:无法保证消息的有序性。

  • 随机策略

    优点:消息的分区选择逻辑简单。
    缺点:负载均衡能力一般,也无法保证消息的有序性

  • 按消息键保序策略

    优点:可以保证相同key的消息被发送到相同的分区,因此可以保证相同key的所有消息之间的顺序性。
    缺点:可能会产生数据倾斜 —— 取决于数据中key的分布,以及使用的hash算法。

Kafka Java生产者的默认分区策略:

  • 如果指定了Key,采用按消息键保序策略
  • 如果没有指定Key,采用轮询策略

参考:https://lilinchao.com/archives/1515.html

2.消费者分区策略

轮询策略

RoundRobin是针对所有topic分区。它是采用轮询分区策略,是把所有的partition和所有的consumer列举出来,然后按照hashcode进行排序,最后再通过轮询算法来分配partition给每个消费者。

在这里插入图片描述

当某一个消费者下线后,则重新进行分配。

随机策略

Range策略是kafka默认的消费者分区分配策略,它是针对topic维度的首先对同一个topic里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序,它可以确保每个消费者消费的分区数量是均衡的。

在这里插入图片描述

粘性分配策略

粘性分区定义:可以理解为分配的结果带有“粘性的”。即在执行一次新的分配之前,考虑上一次分配的结果。尽量少的调整分配的变动,可以节省大量的开销

没有发生rebalance时,Striky粘性分配策略和RoundRobin分配策略类似。

在这里插入图片描述

上面如果consumer2崩溃了,此时需要进行rebalance。如果是Range分配和轮询分配都会重新进行分配,例如:

在这里插入图片描述

通过上图,我们发现,consumer0和consumer1原来消费的分区大多发生了改变。接下来我们再来看下粘性分配策略。

在这里插入图片描述

我们发现,Striky粘性分配策略,保留rebalance之前的分配结果。这样,只是将原先consumer2负责的两个分区再均匀分配给consumer0、consumer1。这样可以明显减少系统资源的浪费,例如:之前consumer0、consumer1之前正在消费某几个分区,但由于rebalance发生,导致consumer0、consumer1需要重新消费之前正在处理的分区,导致不必要的系统开销。(例如:某个事务正在进行就必须要取消了)

kafka副本

副本的ACK参数

在kafka集群中,每个Partition都有多个副本,其中一个副本叫做leader,其他的副本叫做follower,对副本关系较大的就是,生产者(producer)配置的acks参数了, acks参数表示当生产者生产消息的时候,写入到副本的要求严格程度。它决定了生产者如何在性能和可靠性之间做取舍。

Properties props = new Properties();
props.put("bootstrap.servers", "node1.itcast.cn:9092");

// 这里的ack参数有:-1、0、1、all
props.put("acks", "all"); 

props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
  • acks = 0:生产者只管写入,不管是否写入成功,可能会数据丢失。性能是最好的
  • acks = 1:生产者会等到leader分区写入成功后,返回成功,接着发送下一条
  • acks = -1/all:确保消息写入到leader分区、还确保消息写入到对应副本都成功后,接着发送下一条,性能是最差的

根据业务情况来选择ack机制,是要求性能最高,一部分数据丢失影响不大,可以选择0/1。如果要求数据一定不能丢失,就得配置为-1/all。

leader和follower

  • Kafka中的leader和follower是相对分区有意义,不是相对broker
  • Kafka在创建topic的时候,会尽量分配分区的leader在不同的broker中,其实就是负载均衡
  • leader职责:读写数据
  • follower职责:同步数据、参与选举(leader crash之后,会通过zookeeper选举一个follower重新成为分区的leader)
  • 注意和ZooKeeper区分
    • ZK的leader负责读、写,follower可以读取
    • Kafka的leader负责读写、follower只负责副本数据的同步(确保每个消费者消费的数据是一致的),Kafka一个topic有多个分区leader,一样可以实现数据操作的负载均衡

AR

AR(Assigned Replicas) 是指一个topic下为每个分区分配的副本集合。在Kafka中,每个分区可以有多个副本,其中一个副本被选举为leader,其他副本为follower。AR是指包括leader副本在内的所有副本的集合

ISR

**ISR(In-Sync Replicas)**是指与leader副本保持同步的follower副本集合。只有处于ISR中的副本才会被认为是同步的,其他副本将被视为不可靠的。当follower副本无法及时跟上leader副本的同步进度时,它将被移出ISR,直到它能够追赶上来。ISR机制确保了数据的一致性和可靠性。

OSR

OSR(Out-of-Sync Replicas) 是指与leader副本不同步的follower副本集合。当follower副本无法及时跟上leader副本的同步进度时,它将被移出ISR,并被标记为OSR。OSR副本将尝试追赶上来,一旦追赶上来并与leader副本保持同步,它将被重新添加到ISR中。

AR = ISR + OSR

HW(High Watermark)

HW(High Watermark)表示Kafka分区中已经被确认的最高偏移量。它代表了消费者可以安全地读取的消息位置。消费者只能消费高于HW的消息,确保消息的可靠性。

LEO(Log End Offset)

LEO(Log End Offset)表示Kafka分区中当前的最高偏移量。它代表了分区中最新的消息位置,包括已经写入但尚未被确认的消息。LEO是一个动态的值,随着消息的写入和确认而变化。

HW和LEO在Kafka中用于管理消息的可靠性和一致性。消费者可以通过比较HW和LEO来确定自己的消费进度,并确保不会丢失任何重要的消息。

生产者数据不丢失

生产者连接leader写入数据时,可以通过ACK机制来确保数据已经成功写入

生产者可以采用同步和异步两种方式发送数据

  • 同步:发送一批数据给kafka后,等待kafka返回结果
  • 异步:发送一批数据给kafka,只是提供一个回调函数。

说明:如果broker迟迟不给ack,而buffer又满了,开发者可以设置是否直接清空buffer中的数据。

消费者数据不丢失

在消费者消费数据的时候,只要每个消费者记录好offset值即可,就能保证数据不丢失。

  • At-least once:一种数据可能会重复消费
  • Exactly-Once:仅被一次消费

数据积压&数据清理

企业中通过监控系统进行监控,如果发现数据积压就及时进行处理。

数据清理

  • Log Deletion(日志删除):如果消息达到一定的条件(时间、日志大小、offset大小),Kafka就会自动将日志设置为待删除(segment端的后缀名会以 .delete结尾),日志管理程序会定期清理这些日志
    • 默认是7天过期
  • Log Compaction(日志合并)
    • 如果在一些key-value数据中,一个key可以对应多个不同版本的value
    • 经过日志合并,就会只保留最新的一个版本
  • 29
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值