当前位置: 首页 > news >正文

订单30分钟未支付自动取消怎么实现?

目录

  • 了解需求

  • 方案 1:数据库轮询

  • 方案 2:JDK 的延迟队列

  • 方案 3:时间轮算法

  • 方案 4:redis 缓存

  • 方案 5:使用消息队列

了解需求

在开发中,往往会遇到一些关于延时任务的需求。

例如

  • 生成订单 30 分钟未支付,则自动取消

  • 生成订单 60 秒后,给用户发短信

对上述的任务,我们给一个专业的名字来形容,那就是延时任务。那么这里就会产生一个问题,这个延时任务和定时任务的区别究竟在哪里呢?一共有如下几点区别

定时任务有明确的触发时间,延时任务没有

定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期

定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务

下面,我们以判断订单是否超时为例,进行方案分析

方案 1:数据库轮询

思路

该方案通常是在小型项目中使用,即通过一个线程定时的去扫描数据库,通过订单时间来判断是否有超时的订单,然后进行 update 或 delete 等操作

实现

可以用 quartz 来实现的,简单介绍一下

maven 项目引入一个依赖如下所示

<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.2</version>
</dependency>

调用 Demo 类 MyJob 如下所示

package com.rjzheng.delay1;
​
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
​
public class MyJob implements Job {
​public void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("要去数据库扫描啦。。。");}
​public static void main(String[] args) throws Exception {// 创建任务JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();// 创建触发器 每3秒钟执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group3").withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever()).build();Scheduler scheduler = new StdSchedulerFactory().getScheduler();// 将任务及其触发器放入调度器scheduler.scheduleJob(jobDetail, trigger);// 调度器开始调度任务scheduler.start();}
​
}

运行代码,可发现每隔 3 秒,输出如下

要去数据库扫描啦。。。

优点

简单易行,支持集群操作

缺点

  • 对服务器内存消耗大

  • 存在延迟,比如你每隔 3 分钟扫描一次,那最坏的延迟时间就是 3 分钟

  • 假设你的订单有几千万条,每隔几分钟这样扫描一次,数据库损耗极大

方案 2:JDK 的延迟队列

思路

该方案是利用 JDK 自带的 DelayQueue 来实现,这是一个无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素,放入 DelayQueue 中的对象,是必须实现 Delayed 接口的。

DelayedQueue 实现工作流程如下图所示

其中 Poll():获取并移除队列的超时元素,没有则返回空

take():获取并移除队列的超时元素,如果没有则 wait 当前线程,直到有元素满足超时条件,返回结果。

实现

定义一个类 OrderDelay 实现 Delayed,代码如下

package com.rjzheng.delay2;
​
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
​
public class OrderDelay implements Delayed {
​private String orderId;
​private long timeout;
​OrderDelay(String orderId, long timeout) {this.orderId = orderId;this.timeout = timeout + System.nanoTime();}
​public int compareTo(Delayed other) {if (other == this) {return 0;}OrderDelay t = (OrderDelay) other;long d = (getDelay(TimeUnit.NANOSECONDS) - t.getDelay(TimeUnit.NANOSECONDS));return (d == 0) ? 0 : ((d < 0) ? -1 : 1);}
​// 返回距离你自定义的超时时间还有多少public long getDelay(TimeUnit unit) {return unit.convert(timeout - System.nanoTime(), TimeUnit.NANOSECONDS);}
​void print() {System.out.println(orderId + "编号的订单要删除啦。。。。");}
​
}

运行的测试 Demo 为,我们设定延迟时间为 3 秒

package com.rjzheng.delay2;
​
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
​
public class DelayQueueDemo {
​public static void main(String[] args) {List<String> list = new ArrayList<String>();list.add("00000001");list.add("00000002");list.add("00000003");list.add("00000004");list.add("00000005");
​DelayQueue<OrderDelay> queue = newDelayQueue < OrderDelay > ();long start = System.currentTimeMillis();for (int i = 0; i < 5; i++) {//延迟三秒取出queue.put(new OrderDelay(list.get(i), TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS)));try {queue.take().print();System.out.println("After " + (System.currentTimeMillis() - start) + " MilliSeconds");} catch (InterruptedException e) {e.printStackTrace();}}}
​
}

输出如下

00000001编号的订单要删除啦。。。。
After 3003 MilliSeconds
00000002编号的订单要删除啦。。。。
After 6006 MilliSeconds
00000003编号的订单要删除啦。。。。
After 9006 MilliSeconds
00000004编号的订单要删除啦。。。。
After 12008 MilliSeconds
00000005编号的订单要删除啦。。。。
After 15009 MilliSeconds

可以看到都是延迟 3 秒,订单被删除

优点

效率高,任务触发时间延迟低。

缺点

  • 服务器重启后,数据全部消失,怕宕机

  • 集群扩展相当麻烦

  • 因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现 OOM 异常

  • 代码复杂度较高

方案 3:时间轮算法

思路

先上一张时间轮的图(这图到处都是啦)

时间轮算法可以类比于时钟,如上图箭头(指针)按某一个方向按固定频率轮动,每一次跳动称为一个 tick。这样可以看出定时轮由个 3 个重要的属性参数,ticksPerWheel(一轮的 tick 数),tickDuration(一个 tick 的持续时间)以及 timeUnit(时间单位),例如当 ticksPerWheel=60,tickDuration=1,timeUnit=秒,这就和现实中的始终的秒针走动完全类似了。

如果当前指针指在 1 上面,我有一个任务需要 4 秒以后执行,那么这个执行的线程回调或者消息将会被放在 5 上。那如果需要在 20 秒之后执行怎么办,由于这个环形结构槽数只到 8,如果要 20 秒,指针需要多转 2 圈。位置是在 2 圈之后的 5 上面(20 % 8 + 1)

实现

我们用 Netty 的 HashedWheelTimer 来实现

给 Pom 加上下面的依赖

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.24.Final</version>
</dependency>

测试代码 HashedWheelTimerTest 如下所示

package com.rjzheng.delay3;
​
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
​
import java.util.concurrent.TimeUnit;
​
public class HashedWheelTimerTest {
​static class MyTimerTask implements TimerTask {
​boolean flag;
​public MyTimerTask(boolean flag) {this.flag = flag;}
​public void run(Timeout timeout) throws Exception {System.out.println("要去数据库删除订单了。。。。");this.flag = false;}}
​public static void main(String[] argv) {MyTimerTask timerTask = new MyTimerTask(true);Timer timer = new HashedWheelTimer();timer.newTimeout(timerTask, 5, TimeUnit.SECONDS);int i = 1;while (timerTask.flag) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(i + "秒过去了");i++;}}
​
}

输出如下

1秒过去了
2秒过去了
3秒过去了
4秒过去了
5秒过去了
要去数据库删除订单了。。。。
6秒过去了

优点

效率高,任务触发时间延迟时间比 delayQueue 低,代码复杂度比 delayQueue 低。

缺点

  • 服务器重启后,数据全部消失,怕宕机

  • 集群扩展相当麻烦

  • 因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现 OOM 异常

方案 4:redis 缓存

思路一

利用 redis 的 zset,zset 是一个有序集合,每一个元素(member)都关联了一个 score,通过 score 排序来取集合中的值

添加元素:ZADD key score member [score member …]

按顺序查询元素:ZRANGE key start stop [WITHSCORES]

查询元素 score:ZSCORE key member

移除元素:ZREM key member [member …]

测试如下

添加单个元素
redis> ZADD page_rank 10 google.com
(integer) 1
​
添加多个元素
redis> ZADD page_rank 9 baidu.com 8 bing.com
(integer) 2
​
redis> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"
5) "google.com"
6) "10"
​
查询元素的score值
redis> ZSCORE page_rank bing.com
"8"
​
移除单个元素
redis> ZREM page_rank google.com
(integer) 1
​
redis> ZRANGE page_rank 0 -1 WITHSCORES
1) "bing.com"
2) "8"
3) "baidu.com"
4) "9"

那么如何实现呢?我们将订单超时时间戳与订单号分别设置为 score 和 member,系统扫描第一个元素判断是否超时,具体如下图所示

实现一

package com.rjzheng.delay4;
​
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Tuple;
​
import java.util.Calendar;
import java.util.Set;
​
public class AppTest {
​private static final String ADDR = "127.0.0.1";
​private static final int PORT = 6379;
​private static JedisPool jedisPool = new JedisPool(ADDR, PORT);
​public static Jedis getJedis() {return jedisPool.getResource();}
​//生产者,生成5个订单放进去public void productionDelayMessage() {for (int i = 0; i < 5; i++) {//延迟3秒Calendar cal1 = Calendar.getInstance();cal1.add(Calendar.SECOND, 3);int second3later = (int) (cal1.getTimeInMillis() / 1000);AppTest.getJedis().zadd("OrderId", second3later, "OID0000001" + i);System.out.println(System.currentTimeMillis() + "ms:redis生成了一个订单任务:订单ID为" + "OID0000001" + i);}}
​//消费者,取订单
​public void consumerDelayMessage() {Jedis jedis = AppTest.getJedis();while (true) {Set<Tuple> items = jedis.zrangeWithScores("OrderId", 0, 1);if (items == null || items.isEmpty()) {System.out.println("当前没有等待的任务");try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}continue;}int score = (int) ((Tuple) items.toArray()[0]).getScore();Calendar cal = Calendar.getInstance();int nowSecond = (int) (cal.getTimeInMillis() / 1000);if (nowSecond >= score) {String orderId = ((Tuple) items.toArray()[0]).getElement();jedis.zrem("OrderId", orderId);System.out.println(System.currentTimeMillis() + "ms:redis消费了一个任务:消费的订单OrderId为" + orderId);}}}
​public static void main(String[] args) {AppTest appTest = new AppTest();appTest.productionDelayMessage();appTest.consumerDelayMessage();}
​
}

此时对应输出如下

可以看到,几乎都是 3 秒之后,消费订单。

然而,这一版存在一个致命的硬伤,在高并发条件下,多消费者会取到同一个订单号,我们上测试代码 ThreadTest

package com.rjzheng.delay4;
​
import java.util.concurrent.CountDownLatch;
​
public class ThreadTest {
​private static final int threadNum = 10;private static CountDownLatch cdl = newCountDownLatch(threadNum);
​static class DelayMessage implements Runnable {public void run() {try {cdl.await();} catch (InterruptedException e) {e.printStackTrace();}AppTest appTest = new AppTest();appTest.consumerDelayMessage();}}
​public static void main(String[] args) {AppTest appTest = new AppTest();appTest.productionDelayMessage();for (int i = 0; i < threadNum; i++) {new Thread(new DelayMessage()).start();cdl.countDown();}}
​
}

输出如下所示

显然,出现了多个线程消费同一个资源的情况。

解决方案

(1)用分布式锁,但是用分布式锁,性能下降了,该方案不细说。

(2)对 ZREM 的返回值进行判断,只有大于 0 的时候,才消费数据,于是将 consumerDelayMessage()方法里的

if(nowSecond >= score){String orderId = ((Tuple)items.toArray()[0]).getElement();jedis.zrem("OrderId", orderId);System.out.println(System.currentTimeMillis()+"ms:redis消费了一个任务:消费的订单OrderId为"+orderId);
}

修改为

if (nowSecond >= score) {String orderId = ((Tuple) items.toArray()[0]).getElement();Long num = jedis.zrem("OrderId", orderId);if (num != null && num > 0) {System.out.println(System.currentTimeMillis() + "ms:redis消费了一个任务:消费的订单OrderId为" + orderId);}
}

在这种修改后,重新运行 ThreadTest 类,发现输出正常了

思路二

该方案使用 redis 的 Keyspace Notifications,中文翻译就是键空间机制,就是利用该机制可以在 key 失效之后,提供一个回调,实际上是 redis 会给客户端发送一个消息。是需要 redis 版本 2.8 以上。

实现二

在 redis.conf 中,加入一条配置

notify-keyspace-events Ex

运行代码如下

package com.rjzheng.delay5;
​
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPubSub;
​
public class RedisTest {
​private static final String ADDR = "127.0.0.1";private static final int PORT = 6379;private static JedisPool jedis = new JedisPool(ADDR, PORT);private static RedisSub sub = new RedisSub();
​public static void init() {new Thread(new Runnable() {public void run() {jedis.getResource().subscribe(sub, "__keyevent@0__:expired");}}).start();}
​public static void main(String[] args) throws InterruptedException {init();for (int i = 0; i < 10; i++) {String orderId = "OID000000" + i;jedis.getResource().setex(orderId, 3, orderId);System.out.println(System.currentTimeMillis() + "ms:" + orderId + "订单生成");}}
​static class RedisSub extends JedisPubSub {@Overridepublic void onMessage(String channel, String message) {System.out.println(System.currentTimeMillis() + "ms:" + message + "订单取消");
​}}
}

输出如下

可以明显看到 3 秒过后,订单取消了

ps:redis 的 pub/sub 机制存在一个硬伤,官网内容如下

原:Because Redis Pub/Sub is fire and forget currently there is no way to use this feature if your application demands reliable notification of events, that is, if your Pub/Sub client disconnects, and reconnects later, all the events delivered during the time the client was disconnected are lost.

翻: Redis 的发布/订阅目前是即发即弃(fire and forget)模式的,因此无法实现事件的可靠通知。也就是说,如果发布/订阅的客户端断链之后又重连,则在客户端断链期间的所有事件都丢失了。因此,方案二不是太推荐。当然,如果你对可靠性要求不高,可以使用。

优点

(1) 由于使用 Redis 作为消息通道,消息都存储在 Redis 中。如果发送程序或者任务处理程序挂了,重启之后,还有重新处理数据的可能性。

(2) 做集群扩展相当方便

(3) 时间准确度高

缺点

需要额外进行 redis 维护

方案 5:使用消息队列

思路

我们可以采用 rabbitMQ 的延时队列。RabbitMQ 具有以下两个特性,可以实现延迟队列

RabbitMQ 可以针对 Queue 和 Message 设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为 dead letter

lRabbitMQ 的 Queue 可以配置 x-dead-letter-exchange 和 x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了 deadletter,则按照这两个参数重新路由。结合以上两个特性,就可以模拟出延迟消息的功能,具体的,我改天再写一篇文章,这里再讲下去,篇幅太长。

优点

高效,可以利用 rabbitmq 的分布式特性轻易的进行横向扩展,消息支持持久化增加了可靠性。

缺点

本身的易用度要依赖于 rabbitMq 的运维.因为要引用 rabbitMq,所以复杂度和成本变高。

相关文章:

订单30分钟未支付自动取消怎么实现?

目录了解需求方案 1&#xff1a;数据库轮询方案 2&#xff1a;JDK 的延迟队列方案 3&#xff1a;时间轮算法方案 4&#xff1a;redis 缓存方案 5&#xff1a;使用消息队列了解需求在开发中&#xff0c;往往会遇到一些关于延时任务的需求。例如生成订单 30 分钟未支付&#xff0…...

< 开源项目框架:推荐几个开箱即用的开源管理系统 - 让开发不再复杂 >

文章目录&#x1f449; SCUI Admin 中后台前端解决方案&#x1f449; Vue .NetCore 前后端分离的快速发开框架&#x1f449; next-admin 适配移动端、pc的后台模板&#x1f449; django-vue-admin-pro 快速开发平台&#x1f449; Admin.NET 通用管理平台&#x1f449; RuoYi 若…...

内网渗透-基础环境

解决依赖&#xff0c;scope安装 打开要给cmd powershell 打开远程 Set-ExecutionPolicy RemoteSigned -scope CurrentUser; 我试了好多装这东西还是得科学上网&#xff0c;不然不好用 iwr -useb get.scoop.sh | iex 查看下载过的软件 安装sudo 安装git 这里一定要配置bu…...

Go语言学习的第一天(对于Go学习的认识和工具选择及环境搭建)

首先学习一门新的语言&#xff0c;我们要知道这门语言可以帮助我们做些什么&#xff1f;为什么我们要学习这门语言&#xff1f;就小wei而言学习这门语言是为了区块链&#xff0c;因为自身是php出身&#xff0c;因为php的一些特性只能通过一些算法模拟的做一个虚拟链&#xff0c…...

C和C++到底有什么关系

C++ 读作”C加加“,是”C Plus Plus“的简称。顾名思义,C++是在C的基础上增加新特性,玩出了新花样,所以叫”C Plus Plus“,就像 iPhone 6S 和 iPhone 6、Win10 和 Win7 的关系。 C语言是1972年由美国贝尔实验室研制成功的,在当时算是高级语言,它的很多新特性都让汇编程序…...

14个Python处理Excel的常用操作,非常好用

自从学了Python后就逼迫用Python来处理Excel&#xff0c;所有操作用Python实现。目的是巩固Python&#xff0c;与增强数据处理能力。 这也是我写这篇文章的初衷。废话不说了&#xff0c;直接进入正题。 数据是网上找到的销售数据&#xff0c;长这样&#xff1a; 一、关联公式:…...

async/await 用法

1. 什么是 async/await async/await 是 ES8&#xff08;ECMAScript 2017&#xff09;引入的新语法&#xff0c;用来简化 Promise 异步操作。在 async/await 出 现之前&#xff0c;开发者只能通过链式 .then() 的方式处理 Promise 异步操作。示例代码如下&#xff1a; import …...

好意外,发现永久免费使用的云服务器

原因就不说了&#xff0c;说一下过程&#xff0c;在百度搜pythonIDE的时候&#xff0c;发现了一个网站 https://lightly.teamcode.com/https://lightly.teamcode.com/ 就是这个网站&#xff0c;看见这个免费试用&#xff0c;一开始觉得没什么&#xff0c;在尝试使用的过程中发…...

VSCode使用技巧,代码编写效率提升2倍以上!

VSCode是一款开源免费的跨平台文本编辑器&#xff0c;它的可扩展性和丰富的功能使得它成为了许多程序员的首选编辑器。在本文中&#xff0c;我将分享一些VSCode的使用技巧&#xff0c;帮助您更高效地使用它。 1. 插件 VSCode具有非常丰富的插件生态系统&#xff0c;通过安装插…...

SQL执行过程详解

1 、用户在客户端执行 SQL 语句时&#xff0c;客户端把这条 SQL 语句发送给服务端&#xff0c;服务端的进程&#xff0c;会处理这条客户端的SQL语句。 2 、服务端进程收集到SQL信息后&#xff0c;会在进程全局区PGA 中分配所需内存&#xff0c;存储相关的登录信息等。 3 、客…...

【物联网NodeJs-5天学习】第四天存储篇⑤ ——PM2,node.js应用进程管理器

【NodeJs-5天学习】第四天存储篇⑤ ——PM2&#xff0c;node.js应用进程管理器1. 前言2. 官方说明3. 安装PM24. PM2常用命令4.1 启动命令4.2 重新启动命令4.3 热重载命令4.4 停止命令4.5 删除命令4.6 查看进程运行状态4.4 显示某一个进程的具体信息4.8 显示日志信息4.9 终端监控…...

【C++学习】【STL】deque容器

dequeDouble Ended Queues(双向队列)deque和vector很相似&#xff0c;但是它允许在容器头部快速插入和删除&#xff08;就像在尾部一样&#xff09;。所耗费的时间复杂度也为常数阶O(1)。并且更重要的一点是&#xff0c;deque 容器中存储元素并不能保证所有元素都存储到连续的内…...

当 App 有了系统权限,真的可以为所欲为?

看到群里发了两篇文章&#xff0c;出于好奇&#xff0c;想看看这些个 App 在利用系统漏洞获取系统权限之后&#xff0c;都干了什么事&#xff0c;于是就有了这篇文章。由于准备仓促&#xff0c;有些 Code 没有仔细看&#xff0c;感兴趣的同学可以自己去研究研究&#xff0c;多多…...

vue3.js的介绍

一.vue.js简述 Vue是一套用于构建用户开源的MVVM结构的Javascript渐进式框架&#xff0c;尤雨溪在2015年10月27日发布了vue.js 1.0Eavangelion版本&#xff0c;在2016年9月30日发布了2.0Ghost in the Shell版本&#xff0c;目前项目由官方负责 vue的核心只关注图层&#xff0…...

【Three.js】shader特效 能量盾

shader特效之能量盾前言效果噪点图主要代码index.htmldepth-fs.jsdepth-vs.jsshield-fs.jsshield-vs.js相关项目前言 效果噪点图 为了可以自定义能量球的效果&#xff0c;这里使用外部加载来的噪点图做纹理&#xff0c;省去用代码写特效的过程。 主要代码 index.html <…...

【6000字长文】需求评审总是被怼?强烈推荐你试试这三招

前段时间和一个合作部门的产品新人沟通需求,结束的时候,他问了我一个问题,“你在产品新人阶段,最害怕做的事情是什么”? 我不假思索的回答说,“需求评审,是曾经最不想面对的环节,甚至在评审之前几个小时就开始心跳加速了。当然这也是产品修炼路上的必经之路,其实只要掌…...

Hive介绍及DDL

1.OLTP和OLAP OLTP&#xff1a; 联机事务处理系统。在前台接收的用户数据可以立即传送到后台进行处理&#xff0c;并在很短的时间内给出处理结果。关系型数据库是OLTP典型应用&#xff0c;如MySQL OLTP环境开展数据分析是否可行&#xff1f; 为了更好的开展数据分析&#x…...

Simulink 自动代码生成电机控制:在某国产ARM0定点MCU上实现自动代码生成无感电机控制

目录 前言 开发流程 定点化的技巧 代码生成运行演示 总结 前言 这次尝试了在国产arm0内核的MCU上实现Simulink自动代码生成永磁同步电机无传感控制。机缘巧合之下拿到了一块国产MCU的电机控制板和一个5000RPM的小电机。最后实现了无传感控制&#xff0c;在这里总结下一些经…...

MySQL基本查询

文章目录表的增删查改Create&#xff08;创建&#xff09;单行数据 全列插入多行数据 指定列插入插入否则更新替换Retrieve&#xff08;读取&#xff09;SELECT列全列查询指定列查询查询字段为表达式查询结果指定别名结果去重WHERE 条件基本比较BETWEEN AND 条件连接OR 条件连…...

你需要知道的 7 个 Vue3 技巧

VNode 钩子在每个组件或html标签上&#xff0c;我们可以使用一些特殊的&#xff08;文档没写的&#xff09;钩子作为事件监听器。这些钩子有&#xff1a;onVnodeBeforeMountonVnodeMountedonVnodeBeforeUpdateonVnodeUpdatedonVnodeBeforeUnmountonVnodeUnmounted我主要是在组件…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...