防止消息丢失与消息重复——Kafka可靠性分析及优化实践

系列文章目录

上手第一关,手把手教你安装kafka与可视化工具kafka-eagle
Kafka是什么,以及如何使用SpringBoot对接Kafka
架构必备能力——kafka的选型对比及应用场景
Kafka存取原理与实现分析,打破面试难关


在这里插入图片描述
在上一章内容中,我们解析了Kafka在读写层面上的原理,介绍了很多Kafka在读出与写入时的各种设计,初步理解了Kafka大吞吐量的原因,本期我们将带领大家从另一个角度,即从可靠性方面来分析Kafka的机制与原理

📕作者简介:战斧,从事金融IT行业,有着多年一线开发、架构经验;爱好广泛,乐于分享,致力于创作更多高质量内容
📗本文收录于 kafka 专栏,有需要者,可直接订阅专栏实时获取更新
📘高质量专栏 云原生、RabbitMQ、Spring全家桶 等仍在更新,欢迎指导
📙Zookeeper Redis dubbo docker netty等诸多框架,以及架构与分布式专题即将上线,敬请期待

一、可靠性的考量角度

其实我们在《RabbitMQ 能保证消息可靠性吗》 一文中,我们已经阐述了对于MQ类主键,可靠性应该从哪些角度去判断。总结下来其实就是:

  • 消息不会意外丢失
  • 消息不会重复传递

那么本次我们也将从这两方面来看看 Kafka 都做了哪些工作来提升可靠性。

二、分区副本

1. 分区副本的含义

在这里插入图片描述

我们之前了解到:一个Kafka的主题被分为了若干个分区,每个分区都是一个有序的消息队列。如上图,我们就把topicA分成了1\2\3\4 四个分区(实线圆柱),但是我们还看到了更多的“虚线圆柱”,这些其实是 1\2\3\4 分区的副本,在Kafka中,每个分区都有多个副本。副本是指一个分区在其他Broker上的备份副本的作用是提高消息的可靠性和容错性,一旦某个Broker宕机,其他Broker上的副本就可以接替宕机的Broker继续提供服务

我们可以看到不同的Broker之间。互相存储着对方的副本分区,比如Partition1 存在Broker1 上,但Partition1 的副本可以放在Broker2 ,同理 Partition2 的副本也可以放在Broker1 ,如果我们专注于一个分区,那么其情况如下:

在这里插入图片描述

为了区分,我们一般把“实线圆柱”的分区称为 Leader,“虚线圆柱”的分区称为FollowerLeader副本负责读写请求,而Follower副本则只负责复制Leader副本的数据

2. AR 与 ISR机制

上面我们讲了副本分区的Follower,所有副本的合集统称为AR(Assigned Replicas),但是不同副本和Leader到底一致性有多高呢?会不会出现有的副本同步得及时,有的副本因为网络原因同步得很慢呢? 这里又引出了一个重要的概念叫做ISR(In-Sync Replica)机制。

ISR,是一个机制,也代表着一个同步合集,顾名思义,它包含着所有处于同步状态的副本。当一个副本和Leader副本的差距超过一定程度时,这个副本就会被认为是不同步的,不再被加入到ISR中。也因此,Kafka中的 ISR 并不是一直不变的

在这里插入图片描述

那么,既然ISR是动态的,那哪些副本会被包含在ISR中呢?

其实,其主要依据就是 副本需要保证能够及时地接收并复制Leader副本的消息,也就是需要保证与leader副本的消息同步延迟在一定的时间范围内(默认情况下是10秒钟,由参数 replica.lag.time.max.ms 控制)。

在这里插入图片描述

换而言之,因为分区与ISR机制,我们的消息一旦被Kafka 接收后,就会复制多份并很快落盘。这意味着,即使某一台Broker节点宕机乃至硬盘损毁,也不会导致数据丢失。

三、ACKS设置

如果说备份机制是保障消息不会在Kafka服务器丢失,那么消息丢失的另一个重要原因就是消息在发送中丢失。

这种场景下,我们就需要利用消息确认机制了,此时我们也会利用到ISR,比如我们在发送消息时,可以通过设置ACK的值,来决定同步的情况:

  • acks=0,如果设置为零,则生产者根本不会等待来自服务器的任何确认。该记录将立即添加到套接字缓冲区,并被视为已发送。在这种情况下,不能保证服务器已经收到记录,重试配置也不会生效(因为客户端通常不会知道任何故障)。为每条记录返回的偏移量将始终设置为-1。

  • acks=1,这意味着领导者会将记录写入其本地日志,但不会等待所有追随者的完全确认。在这种情况下,如果领导者在确认记录后立即失败,但在追随者复制之前,记录将丢失。

  • acks=all,这意味着leader将等待所有ISR来确认记录。这保证了只要至少有一个副本处于ISR内,记录就不会丢失。这是最有力的保证。这相当于acks=-1的设置。

当我们设置acks=all 或者 -1 的时候, broker 的 min.insync.replicas 参数起作用(一个典型的场景是,topic 有 3个 副本,客户端设置 acks = -1,服务端设置 topic level 的 min.insync.replicas = 2,这样至少有 2 个副本写入后,broker 才会返回;但是如果 topic 只有 1 个副本,而 acks = all,min.insync.replicas = 2,就会报 NOT_ENOUGH_REPLICAS 错误);

在这里插入图片描述

四、重试机制

发消息不可能万无一失,当Kafka在发送或接收消息时发生错误时,可以通过重试来解决这些问题,提高系统的可靠性。这点不用多说,那么在生产端我们可以配置哪些重试的参数呢?

  • retries:重试次数,默认为0,表示不启用重试机制,但建议开启

  • retry.backoff.ms:每次重试的时间间隔,默认为100ms。

  • retry.buffer.records:每个分区的缓冲区中可以存储的最大重试消息数。

在这里插入图片描述

在实际应用中,一些小故障是可以通过重试来解决的,但是重试次数过多也会增加网络通信的负担,甚至会导致消息堵塞。所以建议将其设置为1或2,这样可以在第一次发送失败后进行重试,从而提高消息的可靠性。但是,如果网络状况很差,或者需要处理重要的消息,可以适当增加retries的值。

五、幂等性设计

如果你仔细观察上面的ACKS的设置,相信你会发现,这并不完美:如果你将ACKs设置为-1(all),可以保证Producer到Server之间不会丢失数据,即At Least Once(最少一次),但不能保证数据不重复。而如果你将ACKs级别设置为0(不需要等写log),则可以保证生产者每条消息只会被发送一次,即At Most Once(最多一次),但不能保证不会发生数据丢失。

难道就没有一种既不会丢失,也不会重复的方案吗?其实是有的,这个时候我们可以使用 ‘ack = all’ + 幂等性来解决,而开启幂等性 ,即设置 enable.idempotence = true

请注意,启用幂等性要求
max.in.flight.requests.per.connection小于或等于5(为任何允许的值保留消息顺序),
retries重试次数大于0,
acks必须为“all”。
PS:我发现不少文章把开启幂等性参数写成 enable.idompotence,不知是笔误还是什么原因

开启幂等性的意义在于生产者将确保在流中每条消息正好只会发送一次,那么它是怎么实现的呢?Kafka生产者的幂等性算法主要包括以下三个方面的实现:

  1. 生产者编号: Producer在初始化的时候(只有初始化的时候会随机生成PID)会被分配一个PID(producerid),
  2. 分区编号:不同的分区有自己的paritionid(即分区号),
  3. 序列号:发往同一Partition的消息会附带Sequence Number(即发送数据的编号,代表着向分区发送的第几条消息),这样<PID, PartitionID, SeqNumber>就相当于构成了一个主键。Broker端会对<PID, PartitionID, SeqNumber>做缓存,当具有相同主键的消息提交时,Broker只会持久化一条。

六、消费偏移量

前面说了很多,都是生产者与Kafka的,其实对于消费者,Kakfa也有同步提交偏移量的设计。在 Kafka 的消费者 API 中,同步提交偏移量可以通过设置 enable.auto.commit 参数为 false ,然后在消费者应用中手动控制提交偏移量的时机来实现。

具体来说,可以使用 commitSync() 方法提交偏移量,该方法会一直阻塞直到提交偏移量成功,或抛出异常。示例代码如下:

Properties props = new Properties();
// 设置 Kafka 集群地址等配置信息
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "test-group");
props.put("enable.auto.commit", "false"); // 禁止自动提交偏移量
props.put("auto.offset.reset", "earliest");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("test-topic"));try {while (true) {ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));for (ConsumerRecord<String, String> record : records) {// 处理消息}consumer.commitSync(); // 手动提交偏移量}
} finally {consumer.close();
}

通过这种方式,可以确保消息在被消费者处理后再提交偏移量,从而避免了消息丢失或者重复处理的问题。同时,手动提交偏移量也提高了消费者的可控性,可以根据自身情况自由地设置提交偏移量的时机。

七、可靠性不足分析

尽管我们在上面讲了一些,Kafka为了实现可靠性而做的设计,一般情况下,这种程度的可靠性足以应付了。但在实际应用过程中,Kafka仍然可能会面临以下几个可靠性问题:

  1. 生产者重复发送:尽管开启了幂等性,但不要忘记幂等性设置仅表示生产者对同一个分区的消息的写入是有序的、幂等的,如果producer挂了,重启之后,producer会重新生成producerid,此时幂等性校验就不准了。
  2. 消费者重复消费:如果消费者在提交偏移量前宕机了,将导致Kafka认为该消息没有被消费,在消费者重启后,又会消费该消息,导致重复消费。

这些情况,Kafka自身已经无法解决。我们的解决策略只能契合在我们的业务处理上,目前一个通用的方案是全局性ID+生产/消费两端校验:
在这里插入图片描述

我们可以先根据业务对消息进行去重,然后使用诸如雪花算法等方案为每一条去重后的消息生产全局性的唯一ID,并在发送和消费之前在redis或其他较快的存储件中进行标记,这样当发生重复发送/消费时,就能及时发现了。此时你可以选择放弃本次发送/消费,也可以将该异常情况上报,由人工来进行检查与处理

总结

本次我们对Kafka的可靠性进行了分析和优化实践。一般来说,我们可以通过设置acks、开启幂等性,消费端手动提交偏移量等方式来保证可靠性,也足以应付大部分场景。而且实际应用过程中,还可以配合全局ID等手段完善可靠场景。当然,架构服务于业务需求,所以最终还是需要结合具体的业务需求和场景来选择合适的部署方式和配置参数,在后面我们还会继续进行Kafka的深入解析,如果你对此有兴趣,可以直接订阅本 kafka 专栏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/169449.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop3教程(三十五):(生产调优篇)HDFS小文件优化与MR集群简单压测

文章目录 &#xff08;168&#xff09;HDFS小文件优化方法&#xff08;169&#xff09;MapReduce集群压测参考文献 &#xff08;168&#xff09;HDFS小文件优化方法 小文件的弊端&#xff0c;之前也讲过&#xff0c;一是大量占用NameNode的空间&#xff0c;二是会使得寻址速度…

Redis数据类型——list类型数据的扩展操作

1.list阻塞式数据获取 2.list类型数据业务场景

电脑软件:推荐一款非常强大的pdf阅读编辑软件

目录 一、软件简介 二、功能介绍 1、界面美观&#xff0c;打开速度快 2、可直接编辑pdf 3、非常强大好用的注释功能 4、很好用的页面组织和提取功能 5、PDF转word效果非常棒 6、强大的OCR功能 三、软件特色 四、软件下载 pdf是日常办公非常常见的文档格式&#xff0c;…

基于RM编译码的协作MIMO系统误码率matlab仿真,对比不同RM编译码参数

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2013b 3.部分核心程序 ...................................................................... [V1,N1,K1,I1] f…

05 MIT线性代数-转置,置换,向量空间Transposes, permutations, spaces

1. Permutations P: execute row exchanges becomes PA LU for any invertible A Permutations P identity matrix with reordered rows mn (n-1) ... (3) (2) (1) counts recordings, counts all nxn permuations 对于nxn矩阵存在着n!个置换矩阵 , 2. Transpose: 2.…

前端性能优化 - 虚拟滚动

一 需求背景 需求&#xff1a;在一个表格里面一次性渲染全部数据&#xff0c;不采用分页形式&#xff0c;每行数据都有Echart图插入。 问题&#xff1a;图表渲染卡顿 技术栈&#xff1a;Vue、Element UI 卡顿原因&#xff1a;页面渲染时大量的元素参与到了重排的动作中&#x…

代码随想录 Day26贪心算法01-上

目录 前言:贪心无套路 本质: 两个极端 贪心的小例子 贪心无套路!!! LeetCode T455 分发饼干 题目思路: 1.优先考虑胃口:大饼干喂饱大胃口 2.优先考虑饼干:小饼干先喂饱小胃口 前言:贪心无套路 本质: 局部最优去推导全局最优 两个极端 贪心算法的难度一般要么特别简单,要…

Mac Intellij Idea get/set方法快捷键

Control Retrun(回车键) Command n 参考&#xff1a; Mac Intellij Idea get/set方法快捷键-CSDN博客

2018年亚太杯APMCM数学建模大赛A题老年人平衡能力的实时训练模型求解全过程文档及程序

2018年亚太杯APMCM数学建模大赛 A题 老年人平衡能力的实时训练模型 原题再现 跌倒在老年人中很常见。跌倒可能会导致老年人出现许多并发症&#xff0c;因为他们的康复能力通常较差&#xff0c;因此副作用可能会使人衰弱&#xff0c;从而加速身体衰竭。此外&#xff0c;对跌倒…

ESP32C3 LuatOS TM1650①驱动测试

合宙TM1650驱动资料 TM1650.lua源码 引脚连接 TM1650ESP32C3SCLGPIO5SDAGPIO4 下载TM1650.lua源码&#xff0c;并以文件形式保存在项目文件夹中 驱动测试源码 --注意:因使用了sys.wait()所有api需要在协程中使用 -- 用法实例 PROJECT "ESP32C3_TM1650" VERSION …

数据结构:选择题+编程题(每日一练)

目录 选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;单值二叉树 思路一&#xff1a; 题二&#xff1a;二叉树的最大深度 思路一&#xff1a; 本人实力有限可能对…

KekeBlog项目实战后台模块(二)(已完结)

十一、后台模块-菜单列表 菜单指的是权限菜单&#xff0c;也就是一堆权限字符串 1. 查询菜单 1.1 接口分析 需要展示菜单列表&#xff0c;不需要分页。可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询。菜单要按照父菜单id和orderNum进行排序 请求方式 请求路径…

【QT开发(10)】QT 进程

文章目录 1.1 运行一个新进程1.2 QProcess 还可以对一些信号进行关联2 进程间通信2.1 使用共享内存实现进程通信2.2 演示 代码仓库参考 1.1 运行一个新进程 使用类 QProcess&#xff0c;允许将一个进程堪称一个顺序IO设备。 在Qt中&#xff0c;QProcess类是用于启动外部进程的…

Vue的MVVM实现原理

目录 前言 用法 代码和效果图 效果图 理解 高质量的使用 前言 MVVM是Model-View-ViewModel的缩写&#xff0c;是一种软件架构设计模式。Vue.js实现了这种设计模式&#xff0c;通过双向数据绑定和虚拟DOM技术&#xff0c;使得数据和视图能够快速响应彼此的变化。了解Vue的…

unity中方向的两种表示:欧拉角和四元数

欧拉角&#xff1a;简单来说就是你可以选择 0度~360度 的范围 四元数&#xff1a;在计算机图像学中&#xff0c;四元数用于物体的旋转&#xff0c;是一种复杂&#xff0c;但效率较高的旋转方式 Quaternion结构体代表一个四元数&#xff0c;包含一个标量和一个三维向量&#x…

C# Onnx Yolov8 Detect 路面坑洼检测

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace Onnx…

leetcode 105. 从前序与中序遍历序列构造二叉树

2023.10.21 本题需要根据前序遍历序列和中序遍历序列来构造出一颗二叉树。类似于从中序与后序遍历序列构造二叉树 。使用递归&#xff0c; java代码如下&#xff1a; /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* …

Monocular arbitrary moving object discovery and segmentation 论文阅读

基本信息 题目&#xff1a;Monocular Arbitrary Moving Object Discovery and Segmentation 作者&#xff1a; 来源&#xff1a;BMVC 时间&#xff1a;2021 代码地址&#xff1a;https://github.com/michalneoral/Raptor Abstract 我们提出了一种发现和分割场景中独立移动的…

VSCode 自动格式化

1.打开应用商店&#xff0c;搜索 prettier code formatter &#xff0c;选择第一个&#xff0c;点击安装。 2.安装完成后&#xff0c;点击文件&#xff0c;选择首选项&#xff0c;选择设置。 3.在搜索框内输入 save &#xff0c;勾选在保存时格式化文件。 4.随便打开一个文件&a…

nginx配置负载均衡--实战项目(适用于轮询、加权轮询、ip_hash)

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…