Kafka 分区分配及再平衡策略深度解析与消费者事务和数据积压的简单介绍
Kafka:分布式消息系统的核心原理与安装部署-CSDN博客
自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客
Kafka 生产者全面解析:从基础原理到高级实践-CSDN博客
Kafka 生产者优化与数据处理经验-CSDN博客
Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化-CSDN博客
Kafka 消费者全面解析:原理、消费者 API 与Offset 位移-CSDN博客
Kafka 分区分配及再平衡策略深度解析与消费者事务和数据积压的简单介绍-CSDN博客
Kafka 数据倾斜:原因、影响与解决方案-CSDN博客
Kafka 核心要点解析_kafka mirrok-CSDN博客
Kafka 核心问题深度解析:全面理解分布式消息队列的关键要点_kafka队列日志-CSDN博客
目录
一、分区分配策略基础
二、Range 分区分配策略
(一)原理
(二)案例
(三)Range 分区分配再平衡案例
三、RoundRobin 分区分配策略
(一)原理
(二)案例
(三)RoundRobin 分区分配再平衡案例
四、Sticky 分区分配策略
(一)原理
(二)案例
(三)Sticky 分区分配再平衡案例
五、CooperativeSticky 分区分配策略
六、消费者事务
七、数据积压(消费者如何提高吞吐量)
八、总结
在 Kafka 的消费任务处理中,分区的分配以及再平衡是至关重要的环节。合理的分区分配策略能够确保消费者高效地处理消息,而理解再平衡机制则有助于应对消费者组在运行过程中的动态变化。本文将深入探讨 Kafka 中不同的分区分配策略,包括 Range、RoundRobin、Sticky 和 CooperativeSticky,以及它们在各种场景下的再平衡表现,并结合实际案例进行详细分析,并对消费者事务和数据积压进行简单介绍。
一、分区分配策略基础

在一个 Kafka 消费者组中,包含多个消费者,而一个主题则由多个分区组成。关键问题在于确定哪个消费者来消费哪个分区的数据。Kafka 提供了四种主流的分区分配策略,并且可以通过配置参数 partition.assignment.strategy 来修改分区的分配策略,默认策略是 Range + CooperativeSticky。同时,还有一些相关的重要参数:
| 参数名称 | 描述 |
| heartbeat.interval.ms | Kafka 消费者和 coordinator 之间的心跳时间,默认 3s。 该条目的值必须小于session.timeout.ms,也不应该高于 session.timeout.ms 的 1/3。 |
| session.timeout.ms | Kafka 消费者和 coordinator 之间连接超时时间,默认 45s。超 过该值,该消费者被移除,消费者组执行再平衡。 |
| max.poll.interval.ms | 消费者处理消息的最大时长,默认是 5 分钟。超过该值,该 消费者被移除,消费者组执行再平衡 |
| partition.assignment.strategy | 消 费 者 分 区 分 配 策 略 , 默 认 策 略 是 Range +CooperativeSticky。Kafka 可以同时使用多个分区分配策略。 可 以 选 择 的 策 略 包 括 : Range 、 RoundRobin 、 Sticky 、CooperativeSticky |
二、Range 分区分配策略
(一)原理
Range 分区分配策略是基于主题的分区数量和消费者数量进行分配。它会按照顺序将连续的分区分配给每个消费者,尽可能平均地分配分区,但可能会导致不同消费者分配到的分区数量不一致。

(二)案例
首先,将主题 first 修改为 7 个分区:
bin/kafka-topics.sh --bootstrap-server bigdata01:9092 --alter --topic first --partitions 7
注意,分区数可增加但不能减少,主题的副本数修改需要制定计划执行,不能直接修改。
由三个消费者 CustomConsumer、CustomConsumer1、CustomConsumer2 组成消费者组,组名都为 “test”,同时启动这 3 个消费者。
启动 CustomProducer 生产者,发送 500 条消息,随机发送到不同的分区(修改发送次数为 500 次)。
package com.bigdata.kafka.producer;import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;import java.util.Properties;public class CustomProducerCallback {public static void main(String[] args) throws InterruptedException {// 1. 创建 kafka 生产者的配置对象Properties properties = new Properties();// 2. 给 kafka 配置对象添加配置信息properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.235.128:9092");// key,value 序列化(必须):key.serializer,value.serializerproperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());// 3. 创建 kafka 生产者对象KafkaProducer<String, String> kafkaProducer = newKafkaProducer<String, String>(properties);// 4. 调用 send 方法,发送消息for (int i = 0; i < 500; i++) {// 添加回调kafkaProducer.send(new ProducerRecord<>("first","bigdata " + i), new Callback() {// 该方法在 Producer 收到 ack 时调用,为异步调用@Overridepublic void onCompletion(RecordMetadata metadata,Exception exception) {if (exception == null) {// 没有异常,输出信息到控制台System.out.println(" 主题: " +metadata.topic() + "->" + "分区:" + metadata.partition());} else {// 出现异常打印exception.printStackTrace();}}});// 延迟一会会看到数据发往不同分区Thread.sleep(20);}// 5. 关闭资源kafkaProducer.close();}
}
说明:Kafka 默认的分区分配策略就是 Range + CooperativeSticky,所以不需要修改策略。
默认是Range,但是在经过一次升级之后,会自动变为CooperativeSticky。这个是官方给出的解释。
默认的分配器是[RangeAssignor, CooperativeStickyAssignor],默认情况下将使用RangeAssignor,但允许通过一次滚动反弹升级到CooperativeStickyAssignor,该滚动反弹会将RangeAssignor从列表中删除。
观察消费情况,发现一个消费者消费了 5,6 分区,一个消费了 0,1,2 分区,一个消费了 3,4 分区。这是按照 Range 策略分配的结果。



此时并没有修改分区策略,原因是默认是Range.
(三)Range 分区分配再平衡案例
停止掉 0 号消费者,快速重新发送消息(45s 以内),此时 1 号消费者消费到 3、4 号分区数据,2 号消费者消费到 5、6 号分区数据,0 号的数据无人消费。
说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。
再次重新发送消息(45s 以后),1 号消费者消费到 0、1、2、3 号分区数据,2 号消费者消费到 4、5、6 号分区数据。
说明:消费者 0 已经被踢出消费者组,所以重新按照 range 方式分配。

三、RoundRobin 分区分配策略
(一)原理
RoundRobin 分区分配策略以轮询的方式将分区分配给消费者,确保每个消费者尽可能均衡地获取分区,不考虑主题的因素,只要是消费者组内的分区都会按照轮询顺序分配。

(二)案例
在 CustomConsumer、CustomConsumer1、CustomConsumer2 三个消费者代码中修改分区分配策略为 RoundRobin(指定 org.apache.kafka.clients.consumer.RoundRobinAssignor),并修改消费者组为 test2。
package com.bigdata.kafka.consumer;import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;import java.time.Duration;
import java.util.ArrayList;
import java.util.Properties;public class CustomConsumerWithFenPei {public static void main(String[] args) {Properties properties = new Properties();// 连接kafkaproperties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop11:9092");// 字段反序列化 key 和 valueproperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class.getName());properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class.getName());// 配置消费者组(组名任意起名) 必须properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test2");// 指定分区的分配方案properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, "org.apache.kafka.clients.consumer.RoundRobinAssignor");KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);// 消费者订阅主题,主题有数据就会拉取数据// 指定消费的主题ArrayList<String> topics = new ArrayList<>();topics.add("first");// 一个消费者可以订阅多个主题kafkaConsumer.subscribe(topics);while(true){//1 秒中向kafka拉取一批数据ConsumerRecords<String, String> records = kafkaConsumer.poll(Duration.ofSeconds(1));for (ConsumerRecord<String,String> record :records) {// 打印一条数据System.out.println(record);// 可以打印记录中的很多内容,比如 key value offset topic 等信息System.out.println(record.value());}}}
}修改一下消费者组为test2

重启 3 个消费者,重复发送消息步骤并观察分区结果。



(三)RoundRobin 分区分配再平衡案例
停止掉 0 号消费者,快速重新发送消息(45s 以内),1 号消费者消费到 2、5 号分区数据,2 号消费者消费到 4、1 号分区数据,0 号消费者以前对应的数据无人消费。
说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。
再次重新发送消息(45s 以后),1 号消费者消费到 0、2、4、6 号分区数据,2 号消费者消费到 1、3、5 号分区数据。
说明:消费者 0 已经被踢出消费者组,所以重新按照 RoundRobin 方式分配。
四、Sticky 分区分配策略
(一)原理
粘性分区定义:可以理解为分配的结果带有“粘性的”。即在执行一次新的分配之前, 考虑上一次分配的结果,尽量少的调整分配的变动,可以节省大量的开销。 粘性分区是 Kafka 从 0.11.x 版本开始引入这种分配策略,首先会尽量均衡的放置分区 到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分区不变化。
(二)案例
1)需求
设置主题为 first,7 个分区;准备 3 个消费者,采用粘性分区策略,并进行消费,观察
消费分配情况。然后再停止其中一个消费者,再次观察消费分配情况。
2)步骤
(1)修改分区分配策略为粘性。
注意:3 个消费者都应该注释掉,之后重启 3 个消费者,如果出现报错,全部停止等
会再重启,或者修改为全新的消费者组。
// 修改分区分配策略
ArrayList<String> startegys = new ArrayList<>();
startegys.add("org.apache.kafka.clients.consumer.StickyAssignor");
properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, startegys);
(3)使用同样的生产者发送 500 条消息。
可以看到会尽量保持分区的个数近似划分分区。
(三)Sticky 分区分配再平衡案例
停止掉 0 号消费者,快速重新发送消息(45s 以内),1 号消费者消费到 2、5、3 号分区数据,2 号消费者消费到 4、6 号分区数据,0 号消费者的任务无人顶替。
说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需
要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。
再次重新发送消息(45s 以后),1 号消费者消费到 2、3、5 号分区数据,2 号消费者消费到 0、1、4、6 号分区数据。
说明:消费者 0 已经被踢出消费者组,所以重新按照粘性方式分配。
五、CooperativeSticky 分区分配策略
CooperativeSticky 是新添加的策略。在消费过程中,会根据消费的偏移量情况进行重新再平衡,也就是粘性分区,并且在运行过程中还会根据消费的实际情况重新分配消费者,直到平衡为止。其好处是实现负载均衡,但多次平衡会浪费性能,它采用动态平衡,在消费过程中实施再平衡,而不是等到某个消费者退出再平衡。
六、消费者事务
若要实现 Kafka 消费端的精准一次性消费,需要将消费过程和提交 offset 过程做原子绑定。此时可将 Kafka 的 offset 保存到支持事务的自定义介质(如 MySQL),这部分知识将在后续项目中深入涉及,事务具有 ACID 四大特征,例如转账场景(张三 --> 李四)就需要事务的保障来确保数据的准确性和完整性。

七、数据积压(消费者如何提高吞吐量)

当面临数据积压问题时,消费者可以采取多种方式提高吞吐量,例如增加消费者数量、优化消费者代码处理逻辑、调整相关参数(如 max.poll.interval.ms 等)以适应更高的处理负载等。后续将深入探讨数据积压场景下的优化策略。

八、总结
通过对 Kafka 分区分配以及再平衡策略的深入理解和实践,可以更好地构建和优化 Kafka 消费任务处理流程,确保系统的高效稳定运行。在实际应用中,需要根据具体的业务需求和场景特点选择合适的分区分配策略,并合理处理再平衡过程中的各种情况。
消费者事务方面,为实现精准一次性消费,需将消费与提交 offset 原子绑定,可将 offset 存于支持事务的自定义介质如 MySQL 中。在数据积压场景下,消费者可通过增加数量、优化代码处理逻辑、调整参数等方式提高吞吐量,后续会深入探讨相关优化策略。这些知识对于深入理解和优化 Kafka 消费者的性能、可靠性和数据处理准确性具有极为重要的意义,有助于在实际应用中更好地构建和管理基于 Kafka 的系统架构
相关文章:
Kafka 分区分配及再平衡策略深度解析与消费者事务和数据积压的简单介绍
Kafka:分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析:从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析:…...
useEffect、useCallback、useMemo和memo的区别
前言 在构建现代 React 应用时,性能优化是一个关键考虑因素。随着组件的复杂性增加,合理管理状态和副作用变得尤为重要。React 提供了多个工具来帮助开发者优化组件性能,其中最常用的包括 useEffect、useCallback、useMemo 和 React.memo。这…...
layui树形组件点击树节点后高亮的解决方案
效果显示: 代码 //节点高亮var nodes document.getElementsByClassName("layui-tree-txt");for (var i 0; i < nodes.length; i) {if (nodes[i].innerHTML obj.data.title){nodes[i].style.color "#006BF9";nodes[i].style.fontWeight …...
大语言模型(LLM)安全:十大风险、影响和防御措施
一、什么是大语言模型(LLM)安全? 大语言模型(LLM)安全侧重于保护大型语言模型免受各种威胁,这些威胁可能会损害其功能、完整性和所处理的数据。这涉及实施措施来保护模型本身、它使用的数据以及支持它的基…...
02 —— Webpack 修改入口和出口
概念 | webpack 中文文档 | webpack中文文档 | webpack中文网 修改入口 webpack.config.js (放在项目根目录下) module.exports {//entry设置入口起点的文件路径entry: ./path/to/my/entry/file.js, }; 修改出口 webpack.config.js const path r…...
Go语言进阶依赖管理
1. Go语言进阶 1.1 Goroutine package mainimport ("fmt""time" )func hello(i int) {println("hello goroutine : " fmt.Sprint(i)) }func main() {for i : 0; i < 5; i {go func(j int) { hello(j) }(i) // 启动一个新的 goroutine&…...
集成了高性能ARM Cortex-M0+处理器的一款SimpleLink 2.4 GHz无线模块-RF-BM-2340B1
蓝牙模组 - RF-BM-2340B1是基于美国TI的CC2340R5为核心设计的一款SimpleLink 2.4 GHz 无线模块。支持Bluetooth 5.3 Low Energy、Zigbee 、IEEE 802.15.4g、TI 15.4-Stack (2.4 GHz)及私有协议。集成了高性能ARM Cortex-M0处理器,具有512 KB Flash、32 KB超低泄漏SR…...
ffmpeg本地编译不容易发现的问题 — Error:xxxxx not found!
这里区分电脑CPU架构 本次编译是在Mac笔记本,M1芯片上进行! 前面大致流程:分为两种(1.仅适用,直接下载编译好的本地安装即可;2.使用并查看源码,自己修改编译运行)。这里介绍的是第…...
mybatis——Mapper代理方式
一、原始DAO开发问题 Dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作 量。 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不利于开发维护。 调用SqlSession方…...
FreeRTOS——消息队列
目录 一、概念及其作用 1.1概念 1.2特点 1.3工作原理 二、相关API 2.1创建队列 2.2任务中写队列 2.3任务中读队列 2.4中断中写队列 2.5中断中读队列 三、实现原理 3.1消息队列控制块 3.2消息队列的创建 3.3消息的发送 3.3.1任务中发送 3.3.2中断中发送 3.4消息的…...
【题解】—— LeetCode一周小结46
🌟欢迎来到 我的博客 —— 探索技术的无限可能! 🌟博客的简介(文章目录) 【题解】—— 每日一道题目栏 上接:【题解】—— LeetCode一周小结45 11.切棍子的最小成本 题目链接:1547. 切棍子的最…...
Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)
目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导 一、前言 在充满活力与激情的校园生活中,校运会不仅是…...
python里的数据结构
列表(List) 定义和特点: 列表是一种有序的可变序列,可包含不同类型的元素,如整数、字符串、列表等。可以通过索引访问和修改元素,索引从 0 开始。代码示例: my_list [1, 2, apple, [4, 5]] pr…...
[Unity Demo]从零开始制作空洞骑士Hollow Knight第二十一集:制作游戏的金钱系统吉欧Geo和初步制作HUD Canvas的额外内容
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、制作游戏的金钱系统吉欧Geo以及HUD Camera 1.制作金钱系统吉欧2.制作吉欧的脚本Geo Counter逻辑处理3.制作HUD Canvas的吉欧的UI4.在敌人的HealthManager.c…...
底层逻辑之:极大似然方法(Maximum Likelihood Estimation, MLE)
简介: 极大似然方法(Maximum Likelihood Estimation, MLE)是一种用于估计统计模型参数的方法。其核心思想是基于观测数据来寻找最可能产生这些数据的模型参数。 早在1821年,德国数学家高斯(C. F. Gauss)就…...
笔记:Centos Nginx Jdk Mysql OpenOffce KkFile Minio安装部署
远程工具 ToDesk Nginx 解压 tar zxvf nginx-1.20.2.tar.gz进入Nginx 文件夹 cd nginx-1.20.2报错解决 ./configure: error: C compiler cc is not found yum -y install gcc gcc-c autoconf automake make./configure: error: the HTTP rewrite module requires the PC…...
【MARL】深入理解多智能体近端策略优化(MAPPO)算法与调参
📢本篇文章是博主强化学习(RL)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅…...
深入探索Go语言中的sync.Mutex与sync.RWMutex:原理、应用与实践
深入探索Go语言中的sync.Mutex与sync.RWMutex:原理、应用与实践 在并发编程的世界里,Go语言以其独特的并发模型和简洁的语法赢得了广泛的关注。在Go语言的并发控制工具箱中,sync.Mutex和sync.RWMutex是两个至关重要的工具,它们帮助开发者保护共享资源,避免竞态条件,确保…...
15.postgresql--jsonb 数组进行打平,过滤
用jsonb_array_elements函数先展开数组,再用jsonb_each函数遍历元素中的键值对 例如: SELECT * FROM data_table, LATERAL jsonb_array_elements(json_column) WITH ORDINALITY as elem(element, idx) JOIN LATERAL jsonb_each(elem.element) as kv(ke…...
linux下i2c开发与框架源码分析
目录 1 概述 2 I2c子系统框架 3 I2C的使用流程 3.1 在驱动里使用 3.2 在应用层使用 3.3 I2ctool的使用 4 为硬件i2c注册一个适配器 5 i2c子系统源码流程分析 5.1 i2c device与driver绑定过程 5.1.1 Driver的注册与处理 5.1.2 Client device的生成 5.2 I2c的发送与接…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
