Redisson延迟队列实战:分布式系统中的“时间管理者“
目录
引言:延迟队列的魅力与应用
什么是Redisson延迟队列?
技术原理与工作机制
应用场景
环境准备:搭建基础
Maven依赖配置
Redisson客户端配置
延迟队列实现:核心代码
工作原理深度解析
数据模型与存储结构
元素流转过程
核心方法剖析
分布式特性
实际应用示例
订单超时自动取消场景
定时任务调度场景
性能考量与最佳实践
性能优化
注意事项
高可用考虑
与其他实现方式的对比
总结与展望
参考文献
导读:在分布式系统中,如何优雅地处理时间相关的任务?订单30分钟未支付自动取消、定时推送消息、延迟执行任务——这些场景都需要一个可靠的延迟处理机制。本文深入剖析了基于Redis的Redisson延迟队列实现,从技术原理到实战应用全面展开。
文章不仅讲解了Redisson延迟队列的核心工作原理与数据模型,还通过丰富的代码示例展示了如何在实际项目中优雅地实现订单超时自动取消和定时任务调度等功能。你是否好奇延迟队列在Redis中是如何巧妙利用zset数据结构来实现时间管理的?或者想了解与其他延迟队列实现相比的优缺点?
引言:延迟队列的魅力与应用
在分布式系统架构中,延迟队列作为一种特殊的消息队列,扮演着"时间管理者"的角色。它允许消息在指定的时间后才被消费,解决了诸如订单超时取消、定时任务调度、消息延迟推送等业务场景的需求。Redisson作为一个功能强大的Redis客户端框架,提供了分布式延迟队列(RDelayedQueue)的实现,这一实现巧妙地利用了Redis的数据结构特性,为我们搭建高可用的分布式延迟队列提供了便捷之路。
什么是Redisson延迟队列?
Redisson分布式延迟队列(RDelayedQueue)是基于Redis的有序集合(zset)实现的延时消息处理机制。它允许开发者以指定的延迟时长将元素放入目标队列中,当延迟时间到达时,消息会被自动转移到可消费的目标队列,等待消费者处理。
技术原理与工作机制
Redisson延迟队列本质上是在Redis的zset基础上构建的一个分布式时间轮实现。当我们向延迟队列添加数据时,Redisson会将数据与其到期时间作为score值存储到zset中,并启动一个后台线程监控这些数据。当某条数据的延迟时间到达时,后台线程会将其从zset中取出,并转移到目标阻塞队列(RBlockingDeque)中,供消费者消费。
应用场景
- 订单超时自动取消:电商平台中未支付订单在一定时间后自动取消
- 定时消息推送:如营销活动的定时短信发送
- 任务调度系统:定时执行的任务调度
- 支付结果异步查询:支付后一定时间查询支付结果
- 会话超时管理:管理分布式系统中的会话超时
环境准备:搭建基础
Maven依赖配置
首先,在项目的pom.xml
文件中添加Redisson依赖:
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.20.0</version> <!-- 建议使用最新稳定版本 -->
</dependency>
版本选择建议:建议选择3.17.0以上版本,这些版本修复了之前版本中的一些问题,并提供了更好的性能和更多的功能。你可以在Maven Central查看最新版本。
Redisson客户端配置
创建Redis连接配置类
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.io.IOException;@Configuration
public class RedissonConfig {@Bean(destroyMethod = "shutdown")public RedissonClient redisson() throws IOException {// 创建配置Config config = new Config();// 单节点模式config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0).setPassword(null) // 如果有密码,请设置.setConnectionMinimumIdleSize(5) // 最小空闲连接数.setConnectionPoolSize(64); // 连接池大小// 创建客户端RedissonClient redisson = Redisson.create(config);return redisson;// 集群模式配置示例/*config.useClusterServers().addNodeAddress("redis://192.168.1.1:6379").addNodeAddress("redis://192.168.1.2:6379").setPassword("password");*/}
}
这个配置类创建了一个RedissonClient实例,它将作为与Redis交互的主要入口点。我们通过@Bean
注解将其注册到Spring容器中,方便在其他组件中注入使用。destroyMethod="shutdown"
确保在应用关闭时,RedissonClient能够正确释放资源。
延迟队列实现:核心代码
创建延迟队列服务组件
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;@Component
public class RedissonOrderDelayQueue {private static final String ORDER_DELAY_QUEUE_KEY = "order:delay:queue";private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");@Autowiredprivate RedissonClient redisson;/*** 添加订单到延迟队列* @param orderId 订单ID* @param delay 延迟时间* @param timeUnit 时间单位*/public void addTaskToDelayQueue(String orderId, long delay, TimeUnit timeUnit) {// 获取阻塞队列RBlockingDeque<String> blockingDeque = redisson.getBlockingDeque(ORDER_DELAY_QUEUE_KEY);// 获取延迟队列RDelayedQueue<String> delayedQueue = redisson.getDelayedQueue(blockingDeque);// 记录添加时间System.out.println(LocalDateTime.now().format(DATE_FORMATTER) + " - 添加订单[" + orderId + "]到延迟队列,延迟:" + delay + " " + timeUnit);// 将订单ID放入延迟队列,指定延迟时间delayedQueue.offer(orderId, delay, timeUnit);}/*** 演示添加多个不同延迟时间的消息* @param orderId 订单ID*/public void addMultipleDelayTasks(String orderId) {// 添加3秒后执行的任务addTaskToDelayQueue(orderId + "-3s", 3, TimeUnit.SECONDS);// 添加6秒后执行的任务addTaskToDelayQueue(orderId + "-6s", 6, TimeUnit.SECONDS);// 添加9秒后执行的任务addTaskToDelayQueue(orderId + "-9s", 9, TimeUnit.SECONDS);}/*** 从延迟队列中获取到期的订单(阻塞方式)* @return 到期的订单ID* @throws InterruptedException 如果阻塞被中断*/public String getOrderFromDelayQueue() throws InterruptedException {// 获取阻塞队列RBlockingDeque<String> blockingDeque = redisson.getBlockingDeque(ORDER_DELAY_QUEUE_KEY);// 阻塞等待直到队列中有元素String orderId = blockingDeque.take();System.out.println(LocalDateTime.now().format(DATE_FORMATTER) + " - 从延迟队列中获取到订单:" + orderId);return orderId;}/*** 从延迟队列中获取到期的订单(非阻塞方式,带超时)* @param timeout 超时时间* @param timeUnit 时间单位* @return 到期的订单ID,如果超时返回null* @throws InterruptedException 如果阻塞被中断*/public String getOrderFromDelayQueue(long timeout, TimeUnit timeUnit) throws InterruptedException {RBlockingDeque<String> blockingDeque = redisson.getBlockingDeque(ORDER_DELAY_QUEUE_KEY);// 带超时的阻塞等待String orderId = blockingDeque.poll(timeout, timeUnit);if (orderId != null) {System.out.println(LocalDateTime.now().format(DATE_FORMATTER) + " - 从延迟队列中获取到订单:" + orderId);}return orderId;}/*** 释放延迟队列资源* 建议在应用关闭时调用,以释放资源*/public void destroy() {RBlockingDeque<String> blockingDeque = redisson.getBlockingDeque(ORDER_DELAY_QUEUE_KEY);RDelayedQueue<String> delayedQueue = redisson.getDelayedQueue(blockingDeque);// 销毁延迟队列,释放资源delayedQueue.destroy();}
}
这个服务组件封装了Redisson延迟队列的核心操作,包括添加延迟任务和获取到期任务。值得注意的是,我们提供了阻塞和非阻塞两种方式来获取到期的任务,以适应不同的应用场景。
工作原理深度解析
数据模型与存储结构
Redisson延迟队列在Redis中使用了两个关键的数据结构:
- Zset(有序集合):用于存储延迟消息和它们的执行时间。消息作为member,预计执行时间戳作为score。
- List(列表):作为目标队列,当消息的延迟时间到达时,消息会从Zset转移到这个List中。
元素流转过程
当我们调用offer
方法将元素添加到延迟队列时,实际发生了以下步骤:
- 元素与当前时间+延迟时间的时间戳一起,被存储到Redis的Zset中
- Redisson的后台线程定期扫描Zset,检查是否有到期的元素
- 当元素到期时,它会被从Zset中移除,并添加到目标的RBlockingDeque(List结构)中
- 当消费者调用
take
或poll
方法时,会从RBlockingDeque中获取元素
核心方法剖析
offer方法:添加延迟消息
offer
方法是添加延迟消息的核心,它将元素和延迟时间作为参数:
delayedQueue.offer(orderId, delay, TimeUnit.SECONDS);
这个方法在底层执行了Redis的ZADD
命令,将消息和执行时间(当前时间+延迟时间)添加到Zset中。
take方法:阻塞获取消息
take
方法从目标队列中获取元素,如果队列为空,则阻塞等待:
String orderId = blockingDeque.take();
这个操作在底层使用了Redis的BLPOP
命令,它会阻塞等待,直到队列中有元素可用。
分布式特性
Redisson的延迟队列具有天然的分布式特性:
- 数据共享:所有节点共享同一个Redis实例,因此所有节点都能访问相同的延迟队列
- 任务分发:多个消费者可以同时从队列中获取消息,Redis保证每个消息只会被一个消费者获取
- 高可用性:可以利用Redis的主从复制和集群模式,实现延迟队列的高可用
实际应用示例
订单超时自动取消场景
@Service
public class OrderService {@Autowiredprivate RedissonOrderDelayQueue delayQueue;@Autowiredprivate OrderRepository orderRepository;/*** 创建订单并设置超时自动取消*/public String createOrder(OrderDTO orderDTO) {// 1. 保存订单到数据库Order order = convertToEntity(orderDTO);order.setStatus(OrderStatus.WAITING_PAYMENT);order = orderRepository.save(order);// 2. 添加订单到延迟队列,设置30分钟后检查支付状态delayQueue.addTaskToDelayQueue(order.getId(), 30, TimeUnit.MINUTES);return order.getId();}/*** 处理超时订单,在单独的线程中运行*/@Asyncpublic void processTimeoutOrders() {while (true) {try {// 从延迟队列中获取超时的订单IDString orderId = delayQueue.getOrderFromDelayQueue();// 检查订单状态Order order = orderRepository.findById(orderId).orElse(null);if (order != null && OrderStatus.WAITING_PAYMENT.equals(order.getStatus())) {// 如果订单仍处于等待支付状态,则取消订单order.setStatus(OrderStatus.CANCELLED);order.setCancelReason("超时未支付自动取消");orderRepository.save(order);// 可能还需要释放库存、发送通知等操作logger.info("订单 {} 超时未支付,已自动取消", orderId);}} catch (InterruptedException e) {Thread.currentThread().interrupt();logger.error("处理超时订单被中断", e);break;} catch (Exception e) {logger.error("处理超时订单异常", e);// 异常处理,可能需要重试机制}}}
}
定时任务调度场景
@Service
public class ScheduleService {@Autowiredprivate RedissonOrderDelayQueue delayQueue;/*** 调度一次性任务*/public void scheduleOneTimeTask(String taskId, long delay, TimeUnit timeUnit) {delayQueue.addTaskToDelayQueue(taskId, delay, timeUnit);}/*** 调度周期性任务*/public void scheduleRecurringTask(String taskId, long period, TimeUnit timeUnit) {// 添加首次执行的任务scheduleOneTimeTask(taskId, period, timeUnit);// 启动任务处理线程processScheduledTasks();}@Asyncpublic void processScheduledTasks() {while (true) {try {// 获取要执行的任务IDString taskId = delayQueue.getOrderFromDelayQueue();// 解析任务ID,提取任务信息TaskInfo taskInfo = parseTaskId(taskId);// 执行任务executeTask(taskInfo);// 如果是周期性任务,重新调度if (taskInfo.isRecurring()) {scheduleOneTimeTask(taskId, taskInfo.getPeriod(), taskInfo.getTimeUnit());}} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}}// 其他辅助方法...
}
性能考量与最佳实践
性能优化
- 合理设置连接池:配置适当的连接池大小,避免连接资源不足或浪费
- 批量操作:尽可能使用批量添加和处理,减少网络往返次数
- 避免过多元素:控制延迟队列中的元素数量,过多元素可能导致性能下降
- 避免超长延迟:对于超长延迟(如天级别)的任务,考虑分层处理,先进入小时级队列,再进入分钟级队列
注意事项
- 处理重复消费:设计消息处理逻辑时要考虑幂等性,确保重复处理不会导致问题
- 容错处理:消费者要有良好的异常处理机制,确保一个消息处理失败不会影响整个队列
- 监控队列状态:监控延迟队列的长度和处理速度,及时发现异常情况
- 合理设置超时:使用非阻塞方式获取消息时,设置合理的超时时间
高可用考虑
- Redis集群:使用Redis集群或哨兵模式,确保Redis的高可用
- 消费者集群:部署多个消费者实例,确保消费者的高可用
- 消息持久化:配置Redis的持久化策略,防止数据丢失
与其他实现方式的对比
优势
- 简单易用:Redisson API设计简洁,使用方便
- 轻量级:仅依赖Redis,不需要额外的组件
- 性能高:基于Redis的高性能实现
- 分布式原生支持:天然支持分布式环境
局限性
- 依赖Redis:完全依赖Redis的可用性
- 消息保障:没有RabbitMQ等专业消息队列的消息保障机制
- 监控能力:监控和管理能力相对较弱
直通车:订单超时自动关闭:电商系统的技术难题与解决方案-CSDN博客
总结与展望
Redisson提供的延迟队列是一个简单而强大的解决方案,适用于需要在分布式环境中实现延迟处理的场景。通过结合Redis的高性能和Redisson的优雅API,开发者可以轻松构建出高可用的延迟处理系统。
在选择延迟队列实现方案时,需要根据具体需求权衡各方案的优缺点。对于对延迟精度要求不是特别高,但需要简单实现和较高性能的场景,Redisson延迟队列是一个理想的选择。
随着微服务架构的普及和分布式系统的复杂性增加,延迟队列作为一种重要的异步处理机制,将在更多场景中发挥作用。未来,我们可能会看到更多专注于延迟队列的解决方案出现,提供更精细的控制和更高的可靠性。
参考文献
- Redis官方文档:有序集合(Sorted Set)
- Redisson官方文档:延迟队列
- 分布式系统中的延迟队列实现方案对比
- 延迟队列在电商系统中的应用实践
相关文章:
Redisson延迟队列实战:分布式系统中的“时间管理者“
目录 引言:延迟队列的魅力与应用 什么是Redisson延迟队列? 技术原理与工作机制 应用场景 环境准备:搭建基础 Maven依赖配置 Redisson客户端配置 延迟队列实现:核心代码 工作原理深度解析 数据模型与存储结构 元素流转过…...
国产化适配 - YashanDB、达梦数据库与MySQL 的兼容性及技术选型对比分析
根据知识库信息,以下是 YashanDB、达梦数据库与MySQL 的兼容性及技术选型对比分析: 1. YashanDB 与 MySQL 兼容性 协议与语法兼容 : YashanDB 100%兼容 MySQL 5.7协议 的常用命令(如 SELECT、INSERT),但…...
从0开始——在PlatformIO下开展STM32单片机的HAL库函数编程指南
目录 前言 编写时钟初始化 实现Systicks_Handler,完成HAL库的时基更新 编写驱动测试 前言 笔者最开始的尝试是在2025年的寒假,准备向PlatformIO迁移HAL库,注意,截止到目前,PlatformIO对HAL库的支持已经非常完善了。…...
Python小练习系列 Vol.9:杨辉三角生成(数组构建 + 数学组合)
🧠 Python小练习系列 Vol.9:杨辉三角生成(数组构建 数学组合) 🔺 本期我们带来一道简洁却优雅的经典练习 —— 生成杨辉三角,是训练数组操作与组合思想的绝佳题目! 🧩 一、题目描述…...
Webview详解(下)
第三阶段:性能优化 加载速度优化 缓存策略 缓存策略可以显著减少网络请求,提升页面加载速度。常用的缓存策略包括 HTTP 缓存和本地资源预加载。 1. HTTP 缓存 HTTP 缓存利用 HTTP 协议中的缓存机制(如 Cache-Control、ETag 等࿰…...
scss基础用法
SCSS(Sassy CSS)是Sass的增强版本,作为CSS的预处理器,它提供了多种功能来提高代码的可维护性和效率。以下是SCSS的基础用法: 变量(Variables) 用于存储常用的值,如颜色、字体大小等。…...
知能行每日综测
题目1 自己的做法 答案 题目2 自己的 答案 题目3 注意:这道做错了,你们可以看看我哪里错了 题目4 我的 答案 题目5 没思路,不会做 已更改 题目6 答案 第七题 我的 不会 现在补综测最后一个...
c++ vs和g++下的string结构
话不多说进入正题.注:下述结构是在32位平台下进行验证,32位平台下指针占4个字节. vs下string的结构 string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义 string中字符串的存储空间:(联合体的…...
海量数据处理
1.海量数据处理问题 给两个文件,分别有100亿个query,只有1G内存,如何找到两个文件交集? 解决方案一: 可以先用布隆过滤器,一个文件的query放进布隆过滤器,另一个文件依次查找,在的…...
洛谷题单1-P5706 【深基2.例8】再分肥宅水-python-流程图重构
题目描述 现在有 t t t 毫升肥宅快乐水,要均分给 n n n 名同学。每名同学需要 2 2 2 个杯子。现在想知道每名同学可以获得多少毫升饮料(严格精确到小数点后 3 3 3 位),以及一共需要多少个杯子。 输入格式 输入一个实数 t …...
【HarmonyOS 5】初学者如何高效的学习鸿蒙?
【HarmonyOS 5】初学者如何高效的学习鸿蒙? 一、前言 在全球科技格局风云变幻的当下,谷歌安卓系统的管控逐步收紧,加之国际形势愈发复杂,打造中国人自主的操作系统,已成为时代发展的必然要求,这不仅是突破…...
Java NIO之FileChannel 详解
关键点说明 文件打开选项: StandardOpenOption.CREATE - 文件不存在时创建 StandardOpenOption.READ/WRITE - 读写权限 StandardOpenOption.APPEND - 追加模式 StandardOpenOption.TRUNCATE_EXISTING - 清空已存在文件 缓冲区操作: ByteBuffer.wrap…...
数据可视化(matplotlib)-------图表样式美化
目录 一、图表样式概述 (一)、默认图表样式 (二)、图表样式修改 1、局部修改 2、全局修改 二、使用颜色 (一)、使用基础颜色 1、单词缩写或单词表示的颜色 2、十六进制/HTML模式表示的颜色 3、RGB…...
Go 语言中,关于客户端初始化的最佳实践
在 Go 语言中,关于客户端初始化的最佳实践确实需要注意以下几点: 全局单例模式是推荐做法,尤其对于需要保持长连接或需要复用资源的客户端(如数据库、Redis、HTTP 客户端等)并发安全是必须保证的,需要确保…...
MyBatis的第一天笔记
1. MyBatis 概述 1.1 什么是框架 框架是对通用代码的封装,提前写好了一堆接口和类,可以直接引入使用框架一般以jar包形式存在Java常用框架:SSM三大框架(Spring SpringMVC MyBatis)、SpringBoot、SpringCloud等 1.…...
区块链赋能,为木材货场 “智” 造未来
区块链赋能,为木材货场 “智” 造未来 在当今数字化浪潮席卷的时代,软件开发公司不断探索创新,为各行业带来高效、智能的解决方案。今天,让我们聚焦于一家软件开发公司的杰出成果 —— 区块链木材货场服务平台,深入了…...
IvorySQL:兼容Oracle数据库的开源PostgreSQL
今天给大家介绍一款基于 PostgreSQL 开发、兼容 Oracle 数据库的国产开源关系型数据库管理系统:IvorySQL。 IvorySQL 由商瀚高软件提供支持,主要的功能特性包括: 完全兼容 PostgreSQL:IvorySQL 基于 PostgreSQL 内核开发…...
Python 序列构成的数组(切片)
切片 在 Python 里,像列表(list)、元组(tuple)和字符串(str)这类 序列类型都支持切片操作,但是实际上切片操作比人们所想象的要强大 很多。 这一节主要讨论的是这些高级切片形式的…...
Pre-flash和Main flash
在相机拍照过程中,Pre-flash(预闪光) 和 Main flash(主闪光) 是常见的两种闪光灯使用模式,通常用于提高低光环境下的拍摄质量,尤其在自动曝光(AE)和自动对焦(…...
【区块链安全 | 第十篇】智能合约概述
部分内容与前文互补。 文章目录 一个简单的智能合约子货币(Subcurrency)示例区块链基础交易区块预编译合约 一个简单的智能合约 我们从一个基础示例开始,该示例用于设置变量的值,并允许其他合约访问它。 // SPDX-License-Identi…...
判断质数及其优化方法
判断质数(素数)及其优化方法 质数是指 大于1的自然数,且 只有1和它本身两个正约数。以下是几种判断方法及其优化策略。 目录 基础方法(试除法)优化1:仅检查到√n优化2:跳过偶数优化3ÿ…...
【源码阅读/Vue Flask前后端】简历数据查询功能
目录 一、Flask后端部分modelServiceroute 二、Vue前端部分index.js main.vue功能界面templatescriptstyle 一般就是三个层面,model层面用来建立数据库的字段,service用来对model进行操作,写一些数据库操作的代码,route就是具体的…...
R语言对偏态换数据进行转换(对数、平方根、立方根)
我们进行研究的时候经常会遇见偏态数据,数据转换是统计分析和数据预处理中的一项基本技术。使用 R 时,了解如何正确转换数据有助于满足统计假设、标准化分布并提高分析的准确性。在 R 中实现和可视化最常见的数据转换:对数、平方根和立方根转…...
链表(C++)
这是本人第二次学习链表,第一次学习链表是在大一上的C语言课上,首次接触,感到有些难;第二次是在大一下学习数据结构时(就是这次),使用C再次理解链表。同时,这也是开启数据结构学习写…...
算法-前缀和与差分
一、前缀和(Prefix Sum) 1. 核心思想 前缀和是一种预处理数组的方法,通过预先计算并存储数组的前缀和,使得后续的区间和查询可以在**O(1)**时间内完成。 2. 定义 给定数组 nums,前缀和数组 prefixSum 的每个元素 p…...
网关接口超时?用Java实现接口快速返回,后台继续执行的方法
网关接口超时?用Java实现接口快速返回,后台继续执行的方法 在开发过程中,我们经常会遇到网关接口由于超时限制而导致请求失败的情况。然而,有些接口本身就需要较长时间来执行任务,这时我们不能简单地增加超时时间&…...
HTTP---基础知识
天天开心!!! 文章目录 一、HTTP基本概念1. 什么是HTTP,又有什么用?2. 一次HTTP请求的过程3.HTTP的协议头4.POST和GET的区别5. HTTP状态码6.HTTP的优缺点 二、HTTP的版本演进1.各个版本的应用场景2、注意要点 三、HTTP与…...
python基础学习三(元组及字符串的使用)
文章目录 元组什么是元组元组的创建方式为什么要将元组设计成不可变序列元组的遍历集合集合的相关操作集合操作集合的数学操作集合生成式列表,字典,元组,集合总结 字符串字符串的驻留机制判断字符串的操作方法字符串的比较操作字符串的切片操…...
c#winform,倒鸭子字幕效果,typemonkey字幕效果,抖音瀑布流字幕效果
不废话 直接上效果图 C# winform 开发抖音的瀑布流字幕。 也是typemonkey插件字幕效果 或者咱再网上常说的倒鸭子字幕效果 主要功能 1,软件可以自定义添加字幕内容 2,软件可以添加字幕显示的时间区间 3,可以自定义字幕颜色,可以随…...
1、C51单片机(STC8G2K64S4)串口实验
一、串口1接线图 1、下面是单片机外接电路图,P30,P31分别用于RXD和TXD功能引脚 2、我们来查看单片机手册 串口1需要设置的寄存器 串口1的功能脚配置选择位,看电路图选择的是P3.0,P3.1。 3、串口1:SCON控制寄存器 设置为0x50:0101 0000。&a…...
ue材质学习感想总结笔记
2025 - 3 - 27 1.1 加法 对TexCoord上的每一个像素加上一个值,如果加上0.1,0.1, 那么左上角原来0,0的位置变成了0.1,0.1 右上角就变成了1.1,1.1,那么原来0,0的位置就去到了左上角左上边,所以图像往左上偏移。 总而言…...
MFC TRACE 宏的使用说明
书籍:《Visual C 2017从入门到精通》的2.7 字符串 环境:visual studio 2022 内容:几个字符串类型->(将单字节char*转换为宽字节wchar_t *)(将宽字节wchar_t* 转换为单字节char *) 问题&am…...
latex笔记
1、基本结构 \documentclass[a4paper, 12pt]{article} %文档类型 \begin{document}\title{My First Document}\author{My Name}\date{\today}\maketitleA sentence of text. \end{document}2、带有章、节、小节的结构 \documentclass[a4paper, 12pt]{article}\begin{document…...
Unity编辑器功能及拓展(3) —[Attribute]特性
在 Unity 中,[Attribute]格式的特性是用于扩展编辑器功能、控制序列化行为和调整 Inspector 显示,进行编辑器拓展的核心工具。 一.基础编辑器拓展 1.基础序列化控制 1.[SerializeField] 强制显示私有变量到Inspector 2.[HideInInspector] 隐藏该字段在Inspect…...
Rust基础语法
以下是 Rust 语言基础语法的核心要点,结合与 JavaScript 的对比,帮助前端开发者快速掌握核心概念: 一、变量与常量 1. 变量声明 Rust:变量默认不可变,需用 mut 显式声明可变性。let x 5; // 不可变变量 le…...
<tauri><rust><GUI>基于rust和tauri,实现一个大寰电爪PGHL(串口设备)定制化控制程序
前言 本文是基于rust和tauri,由于tauri是前、后端结合的GUI框架,既可以直接生成包含前端代码的文件,也可以在已有的前端项目上集成tauri框架,将前端页面化为桌面GUI。 环境配置 系统:windows 10平台:visual studio code语言:rust、javascript库:tauri2.0概述 本文是…...
Sentinel 相关知识点
Sentinel 实现原理? Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来帮助开发者保障微服务的稳定性。以下是 Sentinel 的实现原理: 核心概念 资源&…...
DFS飞机降落
问题描述 NN 架飞机准备降落到某个只有一条跑道的机场。其中第 ii 架飞机在 TiTi 时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 DiDi 个单位时间,即它最早可以于 TiTi 时刻开始降落,最晚可以于 TiDiTiDi 时刻开始降落。降落…...
SpringCould微服务架构之Docker(5)
Docker的基本操作: 镜像相关命令: 1.镜像名称一般分两部分组成:[repository]:[tag]。 2. 在没有指定tag时,默认是latest,代表着最新版本的镜像。 镜像命令的案例: 镜像操作常用的命令: dock…...
音乐webpack(通杀webpack-1)
本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许可…...
数据结构与算法——顺序表之手撕OJ题
文章目录 一、前言二、拿捏OJ题2.1移除元素2.2删除有序数组中的重复项2.3合并两个有序数组 三、总结 一、前言 Do you study today?up在上一次已经讲解完毕了有关顺序表的所有知识,不知道大家是否已经沉淀完毕了呢?有一句老话说得好啊——光看不练假把…...
减少采样空间方法 变成后验概率
又 因为后验概率很难计算 --所以通过引入变分分布来近似 后验概率分布 同时 引入 kl散度来度量 近似的效果好不好 什么是kl散度 kl散度带变分: 第一个问题 :积分变期望 问题二:贝叶斯公式 第三个问题:为啥可以独立出来 因为相比…...
如何使用K8S快速部署测试环境
目录 一、Windows 系统使用 Rancher Desktop 二、Linux系统 集群使用 Ansible 一键部署 三、Linux系统使用 kubeadm 快速搭建单节点集群 四、Kubernetes (K8S) 快速部署测试环境 4.1 准备 K8S 集群 4.2部署测试应用 4.3访问测试服务 4.4持久化存储(可选&…...
GAMES101-现代计算机图形学入门(Animation/simulation)
目录 一些科普Keyframe AnimatorPhysical Simulation质点弹簧系统 Mass Spring Rope粒子系统运动学 Forward Kinematics逆运动学Inverse KinematicsRiggingMotion Capture 第二次课 cont.Single Particle Simulation流体模拟 Fluid Simulation GitHub主页:https://g…...
2两数相加解题记录
哎呀,以为这道题也不用写题解的……结果还是有坑没跳出来。 最开始想法先计算总和再求出链表 func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {// 先算出这个值。测试用例会int类型溢出total : 0wei : 1for l1!nil && l2!nil {total (l1.Vall…...
uniapp 获取dom信息(封装获取元素信息工具函数)
在uniapp开发中,需要获取到dom的信息,需要用到uniapp的指定方式 uni.createSelectorQuery(),但是每次需要用到的时候都需要很长一段的繁琐代码,本篇文章将呈现获取dom信息方法封装,话不多说,上菜࿱…...
Mybatis_Plus中常用的IService方法
查询 方法名 查询记录总数 /*** 查询总记录数** see Wrappers#emptyWrapper()*/default long count() {return count(Wrappers.emptyWrapper());} 方法实现 Testpublic void testGetCount(){long count userService.count();System.out.println("总记录数:&…...
【华为OD技术面试真题 - 技术面】- Java面试题(16)
华为OD面试真题精选 专栏:华为OD面试真题精选 目录: 2024华为OD面试手撕代码真题目录以及八股文真题目录 线程创建的方式 1. 通过继承Thread类 创建一个自定义线程类,继承Java中的Thread类,并重写run()方法。然后通过调用start()方法来启动线程。 示例代码: // 继承Th…...
React(六)React过渡动画-CSS编写方式
React过渡动画 react-transition-group介绍 在开发中,我们想要给一个组件的显示和消失添加某种过渡动画,提高用户体验→可通过react-transition-group实现。React曾为开发者提供过动画插件 react-addons-css-transition-group,后由社区维护…...
计算机视觉初步(环境搭建)
1.anaconda 建议安装在D盘,官网正常安装即可,一般可以安装windows版本 安装成功后,可以在电脑应用里找到: 2.创建虚拟环境 打开anaconda prompt, 可以用conda env list 查看现有的环境,一般打开默认bas…...