RedissonClient 分布式队列工具类
注意:轻量级队列可以使用工具类,重量级数据量 请使用 MQ
本文章基于redis使用redisson客户端实现轻量级队列,以及代码、执行结果演示
一、常见队列了解
- 普通队列:先进先出(FIFO),只能在一端添加元素,在另一端移除元素。
- 循环队列:利用数组和取模运算实现队尾连接队首。
- 双端队列:两端都可以添加和移除元素。
- 优先级队列:根据元素的优先级顺序处理元素。
- 阻塞队列:在多线程中使用,队空时取元素会等待,队满时加元素会等待。
- 有限队列:队列长度固定,队满时新元素加入会导致队头元素自动移除。
二、工具类
基于redisson 实现的分布式工具类,copy即用
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueueUtils {private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);/*** 获取客户端实例*/public static RedissonClient getClient() {return CLIENT;}/*** 添加普通队列数据** @param queueName 队列名* @param data 数据*/public static <T> boolean addQueueObject(String queueName, T data) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);return queue.offer(data);}/*** 通用获取一个队列数据 没有数据返回 null(不支持延迟队列)** @param queueName 队列名*/public static <T> T getQueueObject(String queueName) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);return queue.poll();}/*** 通用删除队列数据(不支持延迟队列)*/public static <T> boolean removeQueueObject(String queueName, T data) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);return queue.remove(data);}/*** 通用销毁队列 所有阻塞监听 报错(不支持延迟队列)*/public static <T> boolean destroyQueue(String queueName) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);return queue.delete();}/*** 添加延迟队列数据 默认毫秒** @param queueName 队列名* @param data 数据* @param time 延迟时间*/public static <T> void addDelayedQueueObject(String queueName, T data, long time) {addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS);}/*** 添加延迟队列数据** @param queueName 队列名* @param data 数据* @param time 延迟时间* @param timeUnit 单位*/public static <T> void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);delayedQueue.offer(data, time, timeUnit);}/*** 获取一个延迟队列数据 没有数据返回 null** @param queueName 队列名*/public static <T> T getDelayedQueueObject(String queueName) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);return delayedQueue.poll();}/*** 删除延迟队列数据*/public static <T> boolean removeDelayedQueueObject(String queueName, T data) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);return delayedQueue.remove(data);}/*** 销毁延迟队列 所有阻塞监听 报错*/public static <T> void destroyDelayedQueue(String queueName) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);delayedQueue.destroy();}/*** 添加优先队列数据** @param queueName 队列名* @param data 数据*/public static <T> boolean addPriorityQueueObject(String queueName, T data) {RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);return priorityBlockingQueue.offer(data);}/*** 优先队列获取一个队列数据 没有数据返回 null(不支持延迟队列)** @param queueName 队列名*/public static <T> T getPriorityQueueObject(String queueName) {RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);return queue.poll();}/*** 优先队列删除队列数据(不支持延迟队列)*/public static <T> boolean removePriorityQueueObject(String queueName, T data) {RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);return queue.remove(data);}/*** 优先队列销毁队列 所有阻塞监听 报错(不支持延迟队列)*/public static <T> boolean destroyPriorityQueue(String queueName) {RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);return queue.delete();}/*** 尝试设置 有界队列 容量 用于限制数量** @param queueName 队列名* @param capacity 容量*/public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity) {RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);return boundedBlockingQueue.trySetCapacity(capacity);}/*** 尝试设置 有界队列 容量 用于限制数量** @param queueName 队列名* @param capacity 容量* @param destroy 已存在是否销毁*/public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);if (boundedBlockingQueue.isExists() && destroy) {destroyQueue(queueName);}return boundedBlockingQueue.trySetCapacity(capacity);}/*** 添加有界队列数据** @param queueName 队列名* @param data 数据* @return 添加成功 true 已达到界限 false*/public static <T> boolean addBoundedQueueObject(String queueName, T data) {RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);return boundedBlockingQueue.offer(data);}/*** 有界队列获取一个队列数据 没有数据返回 null(不支持延迟队列)** @param queueName 队列名*/public static <T> T getBoundedQueueObject(String queueName) {RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);return queue.poll();}/*** 有界队列删除队列数据(不支持延迟队列)*/public static <T> boolean removeBoundedQueueObject(String queueName, T data) {RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);return queue.remove(data);}/*** 有界队列销毁队列 所有阻塞监听 报错(不支持延迟队列)*/public static <T> boolean destroyBoundedQueue(String queueName) {RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);return queue.delete();}/*** 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等)*/public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer, boolean isDelayed) {RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);if (isDelayed) {// 订阅延迟队列CLIENT.getDelayedQueue(queue);}queue.subscribeOnElements(consumer);}}
三、普通队列代码测试
3.1 添加进入队列
QueueUtils.addQueueObject 方法添加数据进入队列 test
@RestController
@SaIgnore
public class QueueTestController {@GetMapping("addQueue")public void addQueue() {TestDemo testDemo = new TestDemo();testDemo.setTestKey("testKey");testDemo.setCreateTime(new Date());QueueUtils.addQueueObject("test", testDemo);}
}
redis中查询加入队列数据:
3.2 获取队列
获取上面添加的 test队列 数据
@GetMapping("getQueue")public void getQueue() {TestDemo testDemo = new TestDemo();testDemo.setTestKey("testKey");testDemo.setCreateTime(new Date());Object test = QueueUtils.getQueueObject("test");Console.log("test->{}", test);}
按照先进先出的规则,创建时间最早一条被获取,剩下2条为后添加数据
3.3 删除数据
删除test队列数据
@GetMapping("removeQueue")public R<Void> removeQueue() throws ParseException {TestDemo testDemo = new TestDemo();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");testDemo.setCreateTime(simpleDateFormat.parse("2024-09-04 10:40:53"));testDemo.setTestKey("testKey");boolean test = QueueUtils.removeQueueObject("test", testDemo);return R.ok(test ? "成功":"失败");}
如上代码,删除时间为:2024-09-04 10:40:53 这条数据,剩下一条
3.4 销毁队列
@GetMapping("destoryQueue")public R<Void> destoryQueue() throws ParseException {boolean test = QueueUtils.destroyQueue("test");return R.ok(test ? "成功":"失败");}
如图销毁队列后,刷新,则提示键不存在
3.5 订阅队列
开启订阅队列:
- 一般是在程序启动时候开启,比如使用 @PostConstruct 注解
- 或者实现 ApplicationRunner 接口来实现
@Component public class RuoYiSubcribeInitializer implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {QueueUtils.subscribeBlockingQueue("test",(Consumer<TestDemo>) testDemo ->{Console.log("testDemo->{}", testDemo);},false);} }
@PostConstructpublic void subscribeQueue() {QueueUtils.subscribeBlockingQueue("test",(Consumer<TestDemo>) testDemo ->{Console.log("testDemo->{}", testDemo);},false);}
每次执行添加操作时候,订阅队列都会获取到数据
订阅队列会监听队列数据,知道队列数据为空
2024-09-04 11:37:49 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]开始请求 => URL[GET /addQueue],无参数
testDemo->TestDemo(id=null, deptId=null, userId=null, orderNum=null, testKey=testKey, value=null, version=null, delFlag=null)
2024-09-04 11:37:49 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]结束请求 => URL[GET /addQueue],耗时:[15]毫秒
2024-09-04 11:38:12 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]开始请求 => URL[GET /addQueue],无参数
2024-09-04 11:38:12 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]结束请求 => URL[GET /addQueue],耗时:[13]毫秒
testDemo->TestDemo(id=null, deptId=null, userId=null, orderNum=null, testKey=testKey, value=null, version=null, delFlag=null)
四、有界队列代码测试
4.1设置队列容量
QueueUtils.trySetBoundedQueueCapacity("test", 5); 设置队列容量
@GetMapping("addBoundedQueue")public void addBoundedQueue() {//销毁队列QueueUtils.destroyQueue("test");//设置队列容量QueueUtils.trySetBoundedQueueCapacity("test", 5);}
如下图bps,则是记录容量
4.2 添加队列
@GetMapping("addBounded")public R<Void> addBounded() {//设置队列容量boolean test = QueueUtils.addBoundedQueueObject("test", "vlue111");return R.ok(test ? "成功":"失败");}
- 如果未设置容量,添加失败
- 超出容量,添加也会失败
4.3获取数据
获取队列数据,会同时改变容量大小
getBoundedQueueObject,会正确计算容量的大小。
getQueueObject 获取导数据,容量会为0.后面无法添加
@GetMapping("getBounded")public R<Void> getBounded() {//设置队列容量Object test = QueueUtils.getBoundedQueueObject("test");return R.ok(test.toString());}
底层逻辑,如果取出一个数据,容量则会加 1
{"code": 200,"msg": "vlue111","data": null
}
<Response body is empty>Response code: 200 (OK); Time: 26ms (26 ms); Content length: 40 bytes (40 B)
五、延时队列代码测试
5.1 延时队列数据流转流程
延时队列数据到期后会存入到普通队列,如下图流程:
+-------------------+
| 添加任务到 |
| 延时队列 |---------------------------------------
+-------------------+ | |v v
+-------------------+
| 定时检查到期 |
| 任务 | 获取数据
+-------------------+| v |
+-------------------+
| 延时队列 |---------------------------------------
| -> 普通队列 |
+------------------->
所以拿数据是从延时队列拿数据,还是从普通队列拿数据,考虑下业务场景
5.2 脚本的实现过程:
简单了解地底层:
struct.pack('dLc0', tonumber(ARGV[1]), string.len(ARGV[2]), ARGV[2])
:将过期时间、对象长度和对象本身打包成一个二进制字符串,便于在 Redis 中存储。redis.call('zadd', KEYS[2], ARGV[1], value)
:将打包后的值value
添加到有序集合(延时队列)中,其中ARGV[1]
是过期时间。redis.call('rpush', KEYS[3], value)
:将打包后的值value
添加到列表(待处理队列)中。local v = redis.call('zrange', KEYS[2], 0, 0)
:获取有序集合的第一个元素。if v[1] == value then redis.call('publish', KEYS[4], ARGV[1]) end
:如果添加的新元素是有序集合的第一个元素,则通过 Redis 的发布订阅机制通知其他消费者。Lua 脚本:local value = struct.pack('dLc0', tonumber(ARGV[1]), string.len(ARGV[2]), ARGV[2]); redis.call('zadd', KEYS[2], ARGV[1], value); redis.call('rpush', KEYS[3], value); local v = redis.call('zrange', KEYS[2], 0, 0); if v[1] == value thenredis.call('publish', KEYS[4], ARGV[1]); end;
5.3 测试延时队列:
场景:
- 用于不是立即执行的任务场景:
- 比如用户创建订单但是不付款,时间到后取消订单
如图先订阅队列 test,手动开启:
/*** <简述>订阅延时队列* <详细描述>* @author syf* @date 2024/9/6 14:53*/@GetMapping("subscribeDelayQueue")public R<Void> subscribeDelayQueue() {Console.log("开启监听。。。。。。。。。。。。。。。。。。。。。。。。。。。");QueueUtils.subscribeBlockingQueue("test",(Consumer<TestDemo>) testDemo ->{Console.log("接受到订单->{}", testDemo);Console.log("关闭订单");},false);return R.ok();}
添加延时队列到test:
@GetMapping("addDelayQueue")public void addDelayQueue() throws ParseException {Console.log("创建订单。。。。。。。。。。。。。。。。。。。");TestDemo testDemo = new TestDemo();testDemo.setValue("订单编号");QueueUtils.addDelayedQueueObject("test", testDemo, 10, TimeUnit.SECONDS);Console.log("等待10秒。。。。。。。。。。。。。。。。。。。");}
如图 10秒后,订阅队列监听到订单并关闭
开启监听。。。。。。。。。。。。。。。。。。。。。。。。。。。
2024-09-05 19:47:51 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]结束请求 => URL[GET /subscribeDelayQueue],耗时:[51]毫秒
2024-09-05 19:47:54 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]开始请求 => URL[GET /addDelayQueue],无参数
创建订单。。。。。。。。。。。。。。。。。。。
等待10秒。。。。。。。。。。。。。。。。。。。
2024-09-05 19:47:54 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]结束请求 => URL[GET /addDelayQueue],耗时:[57]毫秒
接受到订单->TestDemo(id=null, deptId=null, userId=null, orderNum=null, testKey=null, value=订单编号, version=null, delFlag=null)
关闭订单
六、优先队列代码测试
场景:
vip 用户按照OrderNum,随机生成等级进行排队
添加vip用户进入队列:
插入数据时候会按照OrderNum 大小找到位置,就像list索引一样
/*** 添加队列数据** @param queueName 队列名*/@GetMapping("/add")public R<Void> add(String queueName) {// 用完了一定要销毁 否则会一直存在boolean b = QueueUtils.destroyPriorityQueue(queueName);log.info("通道: {} , 删除: {}", queueName, b);for (int i = 0; i < 10; i++) {int randomNum = RandomUtil.randomInt(10);PriorityDemo data = new PriorityDemo();data.setName("data-" + i);data.setOrderNum(randomNum);if (QueueUtils.addPriorityQueueObject(queueName, data)) {log.info("通道: {} , 发送数据: {}", queueName, data);} else {log.info("通道: {} , 发送数据: {}, 发送失败", queueName, data);}}return R.ok("操作成功");}
按照等级获取vip用户:
@GetMapping("/get")public R<Void> get(String queueName) {PriorityDemo data;do {data = QueueUtils.getPriorityQueueObject(queueName);log.info("通道: {} , 获取数据: {}", queueName, data);} while (data != null);return R.ok("操作成功");}
如图orderNum从 0 到7依次被打印
2024-09-06 11:06:57 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]结束请求 => URL[GET /demo/queue/priority/get],耗时:[11]毫秒
2024-09-06 11:07:50 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]开始请求 => URL[GET /demo/queue/priority/get],参数类型[param],参数:[{"queueName":["test"]}]
2024-09-06 11:07:50 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-9, orderNum=0)
2024-09-06 11:07:50 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-1, orderNum=2)
2024-09-06 11:07:50 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-2, orderNum=2)
2024-09-06 11:07:50 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-3, orderNum=3)
2024-09-06 11:07:50 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-4, orderNum=3)
2024-09-06 11:07:50 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-8, orderNum=3)
2024-09-06 11:07:51 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-0, orderNum=5)
2024-09-06 11:07:51 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-7, orderNum=6)
2024-09-06 11:07:51 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-5, orderNum=7)
2024-09-06 11:07:51 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: PriorityDemo(name=data-6, orderNum=7)
2024-09-06 11:07:51 [XNIO-1 task-1] INFO c.r.d.c.q.PriorityQueueController- 通道: test , 获取数据: null
2024-09-06 11:07:51 [XNIO-1 task-1] INFO c.r.f.i.PlusWebInvokeTimeInterceptor- [PLUS]结束请求 => URL[GET /demo/queue/priority/get],耗时:[488]毫秒
博主精心整理专栏,CV大法即可用,感谢您小手点一点 手动跪拜:
1- SpringBoot框架常用配置(若依),代码解读:
http://t.csdnimg.cn/jpsSN
2- java常用工具类整理,示例演示:
http://t.csdnimg.cn/gmCfJ
3- CompletableFuture 异步编排与实际代码展示
http://t.csdnimg.cn/ZuC0N
4- XXL-JOB 详细学习,手把手带入门
http://t.csdnimg.cn/lyR7Y
相关文章:

RedissonClient 分布式队列工具类
注意:轻量级队列可以使用工具类,重量级数据量 请使用 MQ 本文章基于redis使用redisson客户端实现轻量级队列,以及代码、执行结果演示 一、常见队列了解 普通队列:先进先出(FIFO),只能在一端添…...

protobuf使用
我下载的是 protobuf-27.4 以下使用vs2022 根据readme,执行如下命令 "C:\Program Files\CMake\bin\cmake.exe" -G "Visual Studio 17 2022" -DCMAKE_INSTALL_PREFIXC:\Users\x\Downloads\install C:\Users\x\Downloads\protobuf-27.4 -D…...

【微处理器系统原理与应用设计第十二讲】通用定时器设计二之PWM波实现呼吸灯的功能
一、基础知识 1、寄存器的配置 (1)GPIOX_AFRL:GPIO复用功能低位寄存器 GPIOX_AFRH:GPIO复用功能高位寄存器 (2)配置PA5 GPIOA->MODER(端口模式寄存器),10为复用功…...
2025秋招NLP算法面试真题(十九)-大模型分布式训练题目
目录: 理论篇 1.1 训练大语言模型存在问题? 1.2 什么是点对点通信? 1.3 什么是集体通信? 1.4 什么是数据并行? 1.5 数据并行如何提升效率? 1.6 什么是流水线并行? 1.7 什么是张量并行 (intra-layer)? 1.8 数据并行 vs 张量并行 vs 流水线并行? 1.9 什么是3D并行? 1.1…...

线程池的应用
1.线程的执行机制 线程分为用户线程 和 内核线程 内核线程就是系统级别的线程,与cpu逻辑处理器数量对应的 用户线程就是使用java代码创建的Thread对象 用户线程必须与内核线程关联(映射),才能执行任务 当用户线程多于内核线程时…...

OPenCV结构分析与形状描述符(5)查找图像中的连通组件的函数connectedComponents()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 connectedComponents 函数计算布尔图像的连通组件标签图像。 该函数接受一个具有4或8连通性的二值图像,并返回 N,即标签…...

HCIA--实验十三:VLAN间通信子接口实验/双单臂路由实验
一、实验内容 1.需求/要求: 将两个单臂路由通过两台交换机连接起来,成为双臂路由,并探讨这么做的原因。实现全网通,让任何一台主机之间都可以通信。 二、实验过程 1.拓扑图: 2.步骤: 1.给PC配置ip地址…...
AIStarter市场指南:项目分享与框架优化【AI绘画、写作、对话、办公、设计】
随着人工智能技术的飞速发展,越来越多的开发者和爱好者希望能够将自己的创意和项目分享给更多人。AIStarter作为一个专注于AI领域的平台,正致力于打造一个开放的应用市场,让创作者能够轻松分享他们的项目,同时也方便其他用户下载和…...

机器学习第8章 集成学习
目录 个体与集成BoostingBagging与随机森林Bagging随机森林 结合策略平均法投票法学习法 个体与集成 定义:集成学习,也叫多分类器系统、基于委员会的学习等,它是一种通过结合多个学习器来构建一个更强大的学习器的技术。如下图所示 在这里&a…...

京东鸿蒙上线前瞻——使用 Taro 打造高性能原生应用
背景 2024 年 1 月,京东正式启动鸿蒙原生应用开发,基于 HarmonyOS NEXT 的全场景、原生智能、原生安全等优势特性,为消费者打造更流畅、更智能、更安全的购物体验。同年 6 月,京东鸿蒙原生应用尝鲜版上架华为应用市场,…...
day2 QT
作业 2> 在登录界面的登录取消按钮进行以下设置: 使用手动连接,将登录框中的取消按钮使用第2种方式的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数 将登录按钮使用qt4版本的连接到自定义的槽函数中,在槽函数中判断…...

JS_函数声明
JS中的方法,多称为函数,函数的声明语法和JAVA中有较大区别 函数说明 函数没有权限控制符不用声明函数的返回值类型,需要返回在函数体中直接return即可,也无需void关键字参数列表中,无需数据类型调用函数时,实参和形参的个数可以不一致声明函数时需要用function关键字函数没有…...
快速排序+归并排序代码回顾
快速排序与归并排序简介: quick_sort为快速排序,merge_sort为归并排序,两者基于分治的思想; 快速排序,简称快排,它以原来数组中的一个值(我们记为x)作为界限,将比它小…...

DBC中一种特殊的特殊的Signal—多路复用Signal
前言: DBC设计中一般设计Signal时其实存在三种类型,如下图所示: **1)步骤1,鼠标单击展开Message,选中底下的Signal **2)步骤2,弹出dialog中选择 map signal **3)得到…...
前端基础面试题·第三篇——JavaScript(其三)
1.字符串 (1) 常用方法 1.charAt(index) 返回指定位置的字符,若没找到,则返回空2.charCodeAt(index) 返回指定位置的unicode字符编码,若没找到,则返回空 3.String.concat(str1,str2) 连接多个字符串,并返回新字符串4.String.fromCharCode(co…...

MacBook真的不能打游戏吗?Mac打游戏会损坏电脑吗?苹果电脑怎么玩游戏
MacBook从来都是高端的代名词,超强的性能搭配顶尖的系统,不光处理大型文件时举重若轻,长期使用也不会有明显卡顿。但很多人在需要MacBook一流的生产力同时,也希望能在空闲时体验游戏的乐趣。在大多人的印象里,Mac电脑对…...

安卓逆向(之)真机root(红米手机)
概览: 1, 手机解锁 2, 下载官方系统包,推荐线刷包,取出镜像文件 3, magisk工具修补 官方系统包 4, adb:命令对手机刷 root 5, 完成 6, 小米手机解锁 点击 小米手机解锁OEM官方教程 记得数据线连接手机电脑 工具下载 点击 下载adb(电脑操作…...

关于转行网络安全的一些建议
在当前就业形势下,不少朋友面临转行的困境。网络安全作为一个热门领域,自然也吸引了许多人的目光。本文将就转行网络安全这一话题,提供一些切实可行的建议。 网络安全行业概况 网络安全涵盖了从基础的脚本编写到高级的漏洞研究等多个层面。该…...
(六十五)第 10 章 内部排序(希尔排序)
示例代码 shellSort.h // // 希尔排序实现头文件#ifndef SHELL_SORT_H #define SHELL_SORT_H#include "errorRecord.h"#define NUM 10 #define MAX_SIZE 20#define EQUAL(a, b) ((a) == (b)) #define LESS_THAN(a, b) ((a) < (b)) #define LESS_OR_EQUAL(a, b) ((…...
802.11 中 scrambler的matlab仿真
802.11a和802.11n中的scrambler仿真不可以直接用matlab中的comm.Scrambler函数。因为这个函数实现的是multiplicative scrambling,而802.11a和802.11n中的scrambler使用的是additive scrambling。additive scrambling使用异或操作进行扰码,multiplicativ…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...