Kafka—工作流程、如何保证消息可靠性_kafka消息可靠性(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
img

正文

    //Producer的压缩算法使用的是GZIP
    //为什么要压缩?
    properties.put("compression.type","gzip");

    //指定发送消息的key和value的序列化类型
    properties.put("key.serializer", "org.apache.kafka.common,serialization.StringSerializer");
    properties.put("value.serializer", "org.apache.kafka.common,serialization.StringSerializer");
    //补充:为什么要序列化/反序列化?

    //实例化一个生产者对象,指定发送的主题、key、value、分区号等
    KafkaProducer<Object, Object> producer = new KafkaProducer<>(properties);

    //发送100条消息
    for (int i = 0; i < 100; i++) {
        //调用send方法,向kafka发送数据,并返回一个Future对象,通过该对象来获取结果
        Future<RecordMetadata> result = producer.send(new ProducerRecord<>("my-topic", Integer.toString(i),
                Integer.toString(i)));
        RecordMetadata recordMetadata = result.get();
    }

    //关闭生产者对象
    producer.close();
}

}


#### 第一步、生产者配置参数


指定生产消息要达到的kafka服务器地址,压缩方式、序列化方式



![](https://img-blog.csdnimg.cn/img_convert/e735b0d40a6785472a006fef4e13950b.png)


##### **①、为什么要进行压缩?**


Producer生产的每个消息都经过**GZIP压缩**,在传输的过程中能够节省网络传输带宽和Broker磁盘占用



##### **②、为什么要进行序列化/反序列化?**


数据在网络传输过程中都是以**字节流的形式传输**的,在生产者发送消息的时候需要将消息先进行序列化



#### 第二步、拦截器


生产者在发送消息前会对请求的消息进行拦截,起到过滤和处理的作用。


我们可以自定义拦截器,拦截器中定义自己需要的逻辑,满足个性化配置。比方说对消息进行加密解密、消息格式转换、消息路由等等



#### 第三步、序列化器


数据在网络传输过程中都是以字节流的形式传输的,在生产者发送消息的时候需要将消息先进行序列化



#### 第四步、分区器


* 如果ProducerRecord对象提供了分区号,使用提供的分区号
* 如果没有提供分区号,提供了key,则使用key序列化后的值的hash值对分区数量取模
* 如果没有提供分区号、key,采用轮询方式分配分区号(默认)



![](https://img-blog.csdnimg.cn/img_convert/ac6819e5bb293c892089c1ef0f920f9e.png)



#### 第五步、send()发送消息


通过上面的操作生产者已经知道该往哪个主题、哪个分区发送这条消息了。



#### 第六步、获取发送消息响应


①、如果消息发送成功:broker收到消息之后会**返回一个Future类型RecordMetadata对象**,可以通过该对象来获取发送的结果,对象中记录了此条消息发送到的topic、partition、offset。


②、消息发送失败:错误消息。在收到错误消息之后会有尝试机制,尝试重新发送消息



![](https://img-blog.csdnimg.cn/img_convert/bf7ac6ecc4a15c25d65d3075eef78c9a.png)



![](https://img-blog.csdnimg.cn/img_convert/4d829cb831587417d4e36a75c7ff5c2d.png)


但直接使用send(msg)会出现问题,调用之后会立即返回,如果因为网络等外界因素影响导致消息没有发送到broker,**出现生产者程序丢失数据问题**,只能通过处理返回的Future对象处理才能感知到。


对应的解决方案是我们可以使用send(msg,callbakc)的方式发哦是那个消息并设置回调函数



![](https://img-blog.csdnimg.cn/img_convert/1b10e47f23a5bce146fc5abe9da699d3.png)


在发送消息后,会立即调用回调函数来处理发送结果,回调函数中定义了处理逻辑




---


### 二、broker收发消息流程


#### 1. 分区机制(主题-分区-消息)


前文中提到生产者发送到broker的消息都是基于topic进行分类的(逻辑上),而topic中的消息是以partition为单位存储的(物理上),每条消息都有自己的offset



##### ①、 分区中的数据存储在哪儿?


每个partition都有一个commit log文件



##### ②、 为什么要分区(好处)存储?


如果commitlog文件很大的话可能导致一台**服务器无法承担所有的数据量**,机器无法存储,分区之后可以把不同的分区放在不同的机器上,相当于是分布式存储


1. 每个消费者并行消费
2. 提高可用性,增加若干副本



#### 2. 消息存储


**每一个partition都对应了一个commit log文件**,日志文件中存储了消息等信息,新到达的消息以**追加的方式写入**分区的末尾,然后以先入先出的顺序读取。



##### ①、 分区中的消息会一直存储吗?


如果不停的一致向日志文件中写入消息,日志文件大小也是有上限的,所以kafka会定期的清理磁盘,有两种方式:


* **时间**:kafka默认保留最近一周的消息(根据配置文中的日志保留时间设置的:log.retention.hours)
* **大小**:kakfa在配置文件中配置单个消息的大小为1MB,如果生产者发送的消息超过1MB,不会接收消息



##### ②、follower副本数据什么时候同步更新的?


1. **数据传输阶段**:Leader副本将消息发送给Follower副本。这个过程中,Leader副本会将消息按照一定的批次大小发送给Follower副本,Follower副本会接收并写入本地日志。一旦Follower副本成功写入消息到本地日志,就会向Leader副本发送确认消息。
2. **确认阶段**:Leader副本在收到来自所有Follower副本的确认消息后,就会认为消息已经成功复制到所有的副本中。然后向生产者发送成功响应,表示消息已被成功接收和复制。


注意的是,Follower副本的数据同步是异步进行的,即Follower副本不需要等待数据同步完成才返回成功响应。这样可以提高消息的处理速度和吞吐量。但也意味着,在数据同步过程中,Follower副本可能会滞后于Leader副本一段时间,这个时间间隔称为追赶(lag)。Kafka提供了配置参数来控制同步和追赶的速度,以平衡数据的一致性和性能的需求。




---


### 三、消费者消费消息流程



![](https://img-blog.csdnimg.cn/img_convert/af5ad230764909a79135b316b63d936e.png)


1. 配置消费者客户端参数
2. 创建消费者实例并指定订阅的主题
3. 拉取消息并消费
4. 提交消费offset


#### 参考代码:



package com.example;

import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;

public class Consumer {
public static void main(String[] args) {
Properties properties = new Properties();
//要连接的kafka服务器
properties.put(“bootstrap.servers”, “localhost:9092”);
//标识当前消费者所属的小组
properties.put(“group.id”, “test”);

    //---------位移提交(自动提交)----------
    //为true,自动定期地向服务器提交偏移量(offset)
    properties.put("enable.auto.commit", "true");
    //自动提交offset的间隔,默认是5000ms(5s)
    properties.put("auto.commit.interval.ms", "1000");
    //每隔固定实践消费者就会把poll获取到的最大偏移量进行自动提交
    //出现的问题:如果刚提交了offset,还没到5s,2s的时候就发生了均衡,导致分区会重新划分,此时offset是不准确的


    //key和value反序列化
    properties.put("key.serializer", "org.apache.kafka.common,serialization.StringSerializer");
    properties.put("value.serializer", "org.apache.kafka.common,serialization.StringSerializer");

    KafkaConsumer<Object, Object> consumer = new KafkaConsumer<>(properties);
    //指定consumer消费的主题(订阅多个)
    consumer.subscribe(Arrays.asList("my-topic", "bar"));

    //轮询向服务器定时请求数据
    while (true) {
        //拉取数据
        ConsumerRecords<Object, Object> records = consumer.poll(100);
        for (ConsumerRecord<Object, Object> record : records) {
            //同步提交:提交当前轮询的最大offset
            consumer.commitSync();
            //如果失败还会进行重试
            //优点:提交成功准确率上升;缺点:降低程序吞吐量

            System.out.printf("offset=%d,key=%s,value=%s%n", record.offset(), record.key(), record.value());

            //异步提交并定义回调
            //优点:提高程序吞吐量(不需要等待请求响应,程序可以继续往下执行)
            //缺点:当提交失败的时候不会自动重试;
            consumer.commitAsync(new OffsetCommitCallback() {
                @Override
                public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets,
                                       Exception exception) {
                    if (exception != null) {
                        System.out.println("错误处理");
                        offsets.forEach((x, y) -> System.out.printf(
                                "topic = %s,partition = %d, offset = %s \n", x.topic(), x.partition(), y.offset()));
                    }
                }
            });
        }
    }
}

}



#### 第一步、配置消费者客户端参数


配置要消费消息的kafka服务器、消费者所在的消费组、offset是自动提交还是手动提交



![](https://img-blog.csdnimg.cn/img_convert/d21f0f00d3532dcbed9553b6f3f0f94b.png)



enable.auto.commit和auto.commit.interval.ms参数为是否自动提交参数



> 
> * enable.auto.commit=true:自动定期地向服务器提交偏移量(offset)
> * auto.commit.interval.ms:动提交offset的间隔,默认是5000ms(5s)
> 
> 
> 


**逻辑**:每隔固定实践消费者就会把poll获取到的最大偏移量进行自动提交


**出现的问题**:如果刚提交了offset,还没到5s,2s的时候就发生了均衡,导致分区会重新划分,此时offset是不准确的,所以我们也可以配置手动提交的方式,具体的手动提交方式在下面第四步会讲到



#### 第二步、创建消费者实例并指定订阅的主题


调用subscribe()方法可以订阅多个主题



![](https://img-blog.csdnimg.cn/img_convert/a0cf6255a37d467282f92fd0559c67f0.png)



#### 第三步、拉取消息并消费


通过poll()方法设置定时拉取消息的时间间隔,消费者会循环的从kafka服务器拉取消息



![](https://img-blog.csdnimg.cn/img_convert/627b2dd2a090c62493ac7be0e327ea23.png)


#### 第四步、提交消费offset


前文中提到我们可以通过收到的方式提交offset,而**手动提交又分为了两种,同步提交和异步提交**。下面我直接上代码观看更直观



![](https://img-blog.csdnimg.cn/img_convert/4336ce1acc049578879b80cdde0ab01a.png)


①、**同步提交**:如果失败还会进行重试,保证了提交成功准确率上升,但缺点是降低程序吞吐量,会发生阻塞



consumer.commitSync();



②、**异步提交并回调**:提高程序吞吐量(不需要等待请求响应,程序可以继续往下执行),不会阻塞,但缺点是当提交失败的时候不会自动重试;



consumer.commitAsync(new OffsetCommitCallback() {
@Override
public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets,Exception exception) {
if (exception != null) {
System.out.println(“错误处理”);
offsets.forEach((x, y) -> System.out.printf(
“topic = %s,partition = %d, offset = %s \n”, x.topic(), x.partition(), y.offset()));
}
}
});





**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)**
![img](https://img-blog.csdnimg.cn/img_convert/654f7df39c05967b35516db27d1519a2.png)

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

"topic = %s,partition = %d, offset = %s \n", x.topic(), x.partition(), y.offset()));
            }
        }
});

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
[外链图片转存中…(img-1KxVvSpR-1713141742431)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值