springboot-redis设置定时触发任务详解
最近研究了一下“redis定时触发”,网上查了查资料,这里记录一下。
从Redis 2.8.0开始,Redis加入了发布/订阅模式以及键空间消息提醒(keyspace notification)功能。键空间消息提醒提供了允许客户端通过订阅指定信道获取Redis数据变化的能力。
需要注意的是,键空间消息提醒并非可靠的,它不会对订阅端是否接收到消息进行确认。例如某个订阅的客户端暂时断开连接,在其直到恢复连接期间发生的事件将无法再次获得。
配置
在默认情况下,Redis并未开启键空间消息提醒功能。为了打开该功能,需要通过notify-keyspace-events配置进行设置,例如:
redis> CONFIG GET notify-keyspace-events
1) "notify-keyspace-events"
2) ""
redis> CONFIG SET notify-keyspace-events KEA
OK
redis> CONFIG GET notify-keyspace-events
1) "notify-keyspace-events"
2) "AKE"
在上述示例中将notify-keyspace-events配置为KEA,代表除未命中外的所有事件。
其中,K与E代表事件的两种类型——Keyspace与Keyevent。
- Keyspace代表与事件名称相关的消息,例如订阅对指定键进行的操作事件;
- Keyevent代表与键名称相关的消息,例如订阅发生键过期事件的相关键名称。
关于更多的notify-keyspace-events配置,可参考下面的描述:
- K:Keyspace事件,将会以__keyspace@<db>__作为事件的前缀
- E:Keyevent事件,将会以__keyevent@<db>__作为事件的前缀
- g:非特定类型的通用命令,例如DEL、EXPIRE、RENAME等
- $:字符串命令,例如SET、INCR等
- l:列表命令,例如LPUSH、LPOP等
- s:集合命令,例如SADD、SREM等
- h:哈希表命令,例如HSET、HINCRBY等
- z:有序集合命令,例如ZSET、ZREM等
- t:流命令,例如XADD、XDEL等
- x:过期事件(在每个发生键过期的时侯产生)
- e:淘汰事件(在每个发生键被淘汰的时候产生)
- m:未命中事件(在访问某个不存在的键使产生)
- A:配置g$lshztxe的别名,但不包括未命中事件m
订阅指定事件
在完成配置后,可通过SUBSCRIBE命令订阅指定信道实现对一个或多个指定事件的订阅。例如通过订阅__keyevent@0__:expired实现订阅数据库0中的键过期事件例如示例1:订阅键过期事件。
订阅的信道的格式为__<type>@<db>__:<event>,其包括了事件类型(keyspace或keyevent)、数据库(例如数据库0)以及事件(例如expired)三部分组成。对应事件的名称,可参考下文命令事件章节。
另外,也可以通过PSUBSCRIBE命令订阅一个或多个复合正则表达式匹配的信道。例如通过订阅__key*@*__:*订阅Redis中所有数据库中的所有事件。
命令事件
Redis为许多命令提供了不同的事件,在本文中将选择其中部分命令及其对应的事件进行介绍:
- DEL:在某个键被删除时产生del事件
- EXPIRE、PEXPIRE、EXPIREAT以及PEXPIREAT:当设置正数过期时间或未来时间的时间戳,则产生expire事件,否则产生del事件(将立即被删除)
- SET以及同类的SETEX、SETNX、GETSET:产生set事件,若使用SETEX则也会产生expire事件
- MSET:将会为每个键都产生一个set事件
- LPUSH、LPUSHX与RPUSH、RPUSHX:根据插入的方向分别产生lpush或rpush事件
- RPOP、LPOP:分别产生rpop与lpop事件,若移出的是列表中的最后一个元素,将会同时产生del事件
- LSET:产生lset事件
- LREM:产生lrem事件,同样若移除的元素为列表中的最后一个元素时将同时产生del事件
- HSET、HSETNX以及HMSET:产生一个hset事件
- HDEL:产生一个hdel事件,且在移除后哈希表为空的情况下产生del事件
- SADD:产生一个sadd事件
- SREM:产生一个srem事件,且在移除后集合为空的情况下产生del事件
- SMOVE:原键中产生srem事件且在目标键中产生sadd事件
- SINTERSTORE、SUNIONSTORE、SDIFFSTORE:分别产生sinterstore、sunionstore以及sdiffstore事件,且在结果为空集且目标键存在的情况下,将会产生del事件
- ZADD:无论添加几个元素都只产生一个zadd事件
- ZREM:无论移除几个元素都只产生一个zrem事件,当移除后有序集合为空时产生del事件
- XADD:产生xadd事件,若使用MAXLEN子命令可能会同时产生xtrim事件
- XDEL:产生xdel事件
- PERSIST:如果对应的键所关联的过期事件成功被移除,则产生persist事件
- 在键发生过期时产生expired事件
- 在达到maxmemory设定的内存值后发生键淘汰时产生evicted事件
关于更多的命令相关事件,请参考keyspace notification相关文档:
Redis keyspace notifications | Redis
实例
配置
springboot-maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
springboot提供了spring-boot-starter-data-redis,里面也已经封装了spring中redis的配置spring-data-redis。
项目代码
http请求访问时,service层函数内部调用redisTemplate在Redis里设置一个注销key
例子1:多份数据,关键数据在value中
代码例子如下:
个人平台账号可申请注销
@Autowiredprivate RedisTemplate redisTemplate;public void deleteUser(String key, String value){// 关键代码redisTemplate.opsForValue().set(key, value);// 设置七天注销时间redisTemplate.expire(key, DEFAULT_DELETE_TIME, TimeUnit.SECONDS);// 监听过期key,获取value使用String tempKey = key + "_2";redisTemplate.opsForValue().set(tempKey, value);}
注意:
redis里面设置了两个key,原因在于:key过期之后,在ResdisExpirationListener 的 onMessage函数中 无法拿到key对应的value,所以设置两个,key不同但是value一样。这个value是为了key到期之后触发想要的任务函数。
申请注销后七天内可撤销注销
public void withdrawDeleteUser(String key) {redisTemplate.delete(key);// 删除临时keyredisTemplate.delete(key + "_2");
}
注意: redis里面设置的两个key都必须删除新建RedisListenerConfig.class
@Component
public class RedisListenerConfig {@Bean@Primarypublic RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory){RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);return container;}
}
新建ResdisExpirationListener.class
业务最主要在onMessage函数,key过期后会自动触发该函数,message信息是key,根据redisTemplate和另外设置的一个key拿到value,这个value在触发的定时任务函数里面用到了,所以必须拿到。
@Slf4j
@Component
@Transactionalpublic class ResdisExpirationListener extends KeyExpirationEventMessageListener {@AutowiredUserService userService;@Autowiredprivate RedisTemplate redisTemplate;public ResdisExpirationListener(RedisMessageListenerContainer listenerContainer) {super(listenerContainer);}@Overridepublic void onMessage(Message message, byte[] pattern){String messageKey = message.toString();// 业务实现if(messageKey.contains(RedisKey.DELETE_USER)){String userKey = redisTemplate.opsForValue().get(messageKey + "_2").toString();Long userId = Long.parseLong(userKey);// 触发定时任务userService.deleteUserProcess(userId);// 删除临时keyredisTemplate.delete(messageKey + "_2");}}
}
好了,可以跑跑看了。
例子2:关键数据在key中
添加redis监听
package com.cpl.tsl.listener;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;import java.util.Date;/*** RedisKey键监听以及业务逻辑处理** @author: lll* @date: 2022年03月07日 14:03:49*/
@Component
public class RedisTaskListener extends KeyExpirationEventMessageListener {private static final Logger logger = LoggerFactory.getLogger(RedisTaskListener.class);@Value("${applicationName:tsl}")private String applicationName;/*** @param listenerContainer 监听器*/public RedisTaskListener(RedisMessageListenerContainer listenerContainer) {super(listenerContainer);}@Overridepublic void onMessage(Message message, byte[] pattern) {String expiredKey = message.toString();// 将拿到的过期键使用之前拼接时的特殊符号分割成字符数组String[] expiredKeyArr = expiredKey.split("\\|");String businessSign = expiredKeyArr[0].toString();String expiredTimeSign = expiredKeyArr[1].toString();logger.info(businessSign +":"+ expiredTimeSign);Date date = new Date();// 只有本业务才执行以下操作if (businessSign.equals(applicationName + ":ORDERINFO")) {logger.info("订单超时,已取消");} else {logger.error("非订单业务不做处理");}}
}
添加controller调动接口
package com.cpl.tsl.controller;import com.cpl.tsl.utils.RedisUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.Date;
import java.util.UUID;/*** 测试类**/
@RestController
@RequestMapping("/")
@Api(tags = "测试模块")
public class TestController {private static final Logger logger = LoggerFactory.getLogger(TestController.class);@Resourceprivate RedisTemplate<String, String> template;@Resourceprivate RedisUtil redisUtil;@Value("${applicationName:tsl}")private String applicationName;/*** redis设置定时key,模拟订单下单,超时提醒或者去掉订单**/@RequestMapping(value = "/putkeys", method = RequestMethod.POST)@ApiOperation(value = "测试redis存储参数", notes = "测试redis存储参数")public String putRedisTaskKeys() {/*** 存入订单信息*/Date date = new Date();//设置超时时间30秒Long overTime = new Long(30);//创建订单号String orderNo = UUID.randomUUID().toString();//订单信息String orderInfo = "这是订单号为:" + orderNo + " 的订单,价格是:2000元,下单时间是:" + date;//redis keyString redisKey = applicationName + ":ORDERINFO|" + orderNo;redisUtil.set(redisKey, orderInfo, overTime);logger.info("下单时间:" + date);logger.info("订单的redisKey " + redisKey + " 订单信息 " + orderInfo);return "下单成功";}/*** 手动处理订单,从redis移除订单**/@RequestMapping(value = "/removeKeys", method = RequestMethod.POST)@ApiOperation(value = "测试redis移除参数", notes = "测试redis移除参数")public String removeRedisTaskKeys(@ApiParam(name = "orderNo", value = "订单号", required = true) @RequestParam("orderNo") String orderNo) {/*** 处理订单*///拼接redis keyString redisKey = applicationName + ":ORDERINFO|" + orderNo;//删除redis keyredisUtil.del(redisKey);logger.info("订单redisKey " + redisKey + " 已处理");return "处理完成";}}
例子3
application.yml
redis:
localhost: localhost
port: 6379
database: 7
password:
# 过期事件订阅,接收7号数据库中所有key的过期事件
listen-pattern: __keyevent@7__:expired
Redis 事件广播配置类
import com.coisini.springbootlearn.core.listener.RedisMessageListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
@Configuration
public class RedisListenerConfiguration {
@Value("${spring.redis.listen-pattern}")
public String pattern;
@Bean
public RedisMessageListenerContainer listenerContainer(RedisConnectionFactory redisConnection) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnection);
/**
* Topic是消息发布(Pub)者和订阅(Sub)者之间的传输中介
*/
Topic topic = new PatternTopic(this.pattern);
container.addMessageListener(new RedisMessageListener(), topic);
return container;
}
}
Redis 事件广播监听器
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
public class RedisMessageListener implements MessageListener {
/**
* Redis 事件监听回调
* @param message
* @param pattern
*/
@Override
public void onMessage(Message message, byte[] pattern) {
byte[] body = message.getBody();
String expiredKey = new String(body);
System.out.println("监听到已过期的key:" + expiredKey);
/**
* 监听到过期事件回调
* TODO:
*/
}
}
测试接口
@RestController
@RequestMapping("/redis")
public class RedisController {
@Autowired
private StringRedisTemplate redisTemplate;
@GetMapping(value = "/setExpiredVal")
public String setExpiredVal(@RequestParam String name) {
// 设置 20s 后过期
redisTemplate.opsForValue().set("name", name, 20, TimeUnit.SECONDS);
return "setVal is ok";
}
}
优缺点
在 Spring Boot 中整合 Redis 监听订单超时主要的优缺点:
优点:
-
实时性:使用 Redis 来监听订单超时,可以实现实时性处理。当订单超时时,处理操作可以立即触发,而不需要定期轮询数据库或其他方式。
-
高性能:Redis 是一个内存数据库,因此具有高性能。它能够快速存储和检索数据,适合用于订单超时处理。
-
可扩展性:Redis 支持分布式部署,因此可以轻松扩展应用程序以处理更多订单。可以使用 Redis Sentinel 或 Redis Cluster 来实现高可用性和负载均衡。
-
减轻数据库压力:将订单超时的检查和处理从数据库转移到 Redis,可以减轻数据库服务器的负载,因为不再需要频繁地查询数据库。
-
简化代码:Redis 提供了内置的过期键和发布/订阅功能,这些功能使订单超时的处理逻辑更加简单和可维护。
缺点:
-
单一点故障:如果 Redis 实例发生故障,可能导致订单超时处理不可用。为了解决这个问题,可以使用 Redis Sentinel 或 Redis Cluster 来提高可用性。
-
不适合持久性数据:Redis 是一个内存数据库,不适合用于持久性数据存储。如果订单数据需要长期保留,仍然需要在数据库中保留订单信息。
-
配置和维护:Redis 需要一些配置和维护工作,包括备份、监控、调整内存限制等。这可能需要额外的管理工作。
-
消息队列的竞争条件:如果多个实例同时处理订单超时,可能会引发竞争条件,需要在代码中进行处理。
-
性能成本:虽然 Redis 具有高性能,但在大规模订单处理时,可能需要更多的 Redis 实例和更强大的硬件,这可能带来一些成本。
参考
Redis键空间通知详解_notify-keyspace-events-CSDN博客
Spring boot整合 redis实现订单超时处理 - 掘金
https://www.cnblogs.com/vergilyn/p/7285457.html
相关文章:
springboot-redis设置定时触发任务详解
最近研究了一下“redis定时触发”,网上查了查资料,这里记录一下。 从Redis 2.8.0开始,Redis加入了发布/订阅模式以及键空间消息提醒(keyspace notification)功能。键空间消息提醒提供了允许客户端通过订阅指定信道获取…...
Video anomaly detection with spatio-temporal dissociation 论文阅读
Video anomaly detection with spatio-temporal dissociation 摘要1.介绍2.相关工作3. Methods3.1. Overview3.2. Spatial autoencoder3.3. Motion autoencoder3.4. Variance attention module3.5. Clustering3.6. The training objective function 4. Experiments5. Conclusio…...
svn 安装
安装系统 ubuntu 22 安装命令: sudo apt-get install subversion 创建第一个工程: 创建版本库、项目 1、先创建svn根目录文件夹 sudo mkdir /home/svn 2、创建项目的目录文件夹 sudo mkdir /home/svn/demo_0 svnadmin create /home/svn/demo_0 配置&a…...
slurm 23.11.0集群 debian 11.5 安装
slurm 23.11.0集群 debian 11.5 安装 用途 Slurm(Simple Linux Utility for Resource Management, http://slurm.schedmd.com/ )是开源的、具有容错性和高度可扩展的Linux集群超级计算系统资源管理和作业调度系统。超级计算系统可利用Slurm对资源和作业进行管理&a…...
ffmpeg可以做什么
用途 FFmpeg是一个功能强大的多媒体处理工具,可以处理音频和视频文件。它是一个开源项目,可在各种操作系统上运行,包括Linux、Windows和Mac OS X等。以下是FFmpeg可以做的一些主要任务: 转换媒体格式:可将一个媒体格式…...
一种缩小数据之间差距的算法
先上代码: /** * 缩小数据之间的差距,但是大小关系不变的方法* param {Array} features */function minMaxData(data) {for (let i 0; i < data.length; i) {const f data[i];const x f[1];const yf[2];//此处5根据实际情况设置const y2 Math.pow(…...
【Axure RP9】动态面板使用------案例:包括轮播图和多方式登入及左侧菜单栏案例
目录 一 动态面板简介 1.1 动态面板是什么 二 轮播图 2.1 轮播图是什么 2.2 轮播图应用场景 2.3 制作实播图 三 多方式登入 3.1多方式登入是什么 3.3 多方式登入实现 四 左侧菜单栏 4.1左侧菜单栏是什么 4.2 左侧菜单栏实现 一 动态面板简介 1.1 动态面板是什么…...
在接口实现类中,加不加@Override的区别
最近的软件构造实验经常需要设计接口,我们知道Override注解是告诉编译器,下面的方法是重写父类的方法,那么单纯实现接口的方法需不需要加Override呢? 定义一个类实现接口,使用idea时,声明implements之后会…...
优质全套SpringMVC教程
三、SpringMVC 在SSM整合中,MyBatis担任的角色是持久层框架,它能帮我们访问数据库,操作数据库 Spring能利用它的两大核心IOC、AOP整合框架 1、SpringMVC简介 1.1、什么是MVC MVC是一种软件架构的思想(不是设计模式-思想就是我们…...
微信小程序---使用npm包安装Vant组件库
在小程序项目中,安装Vant 组件库主要分为如下3步: 注意:如果你的文件中不存在pakage.json,请初始化一下包管理器 npm init -y 1.通过 npm 安装(建议指定版本为1.3.3) 通过npm npm i vant/weapp1.3.3 -S --production 通过y…...
GPT-4V被超越?SEED-Bench多模态大模型测评基准更新
📖 技术报告 SEED-Bench-1:https://arxiv.org/abs/2307.16125 SEED-Bench-2:https://arxiv.org/abs/2311.17092 🤗 测评数据 SEED-Bench-1:https://huggingface.co/datasets/AILab-CVC/SEED-Bench SEED-Bench-2&…...
数据库_mongoDB
1 介绍 MongoDB 是一种 NoSQL 数据库,它将每个数据存储为一个文档,这里的文档类似于 JSON/BSON 对象,具体数据结构由键值(key/value)对组成。字段值可以包含其他文档,数组及文档数组。其数据结构非常松散&…...
Layui实现自定义的table列悬停事件并气泡提示信息
1、概要 使用layui组件实现table的指定列悬停时提示信息,因为layui组件中没有鼠标悬停事件支持,所以需要结合js原生事件来实现这个功能,并结合layui的tips和列的templte属性气泡提示实现效果。 2、效果图 3、代码案例 <!DOCTYPE html&g…...
Tomcat从认识安装到详细使用
文章目录 一.什么是Tomact?二.Tomcat的安装1.下载安装包2.一键下载3.打开Tomcat进行测试4.解决Tomcat中文服务器乱码 三.Tomcat基本使用1.启动与关闭Tomcat2.Tomcat部署项目与浏览器访问项目 四.Tomcat操作中的常见问题1.启动Tomcat后,启动窗口一闪而过?…...
07-Eventing及实践
1 Knative Eventing的相关组件 Knative Eventing具有四个最基本的组件:Sources、Brokers、Triggers 和 Sinks 事件会从Source发送至SinkSink是能够接收传入的事件可寻址(Addressable)或可调用(Callable)资源 Knative S…...
Linux下Netty实现高性能UDP服务
前言 近期笔者基于Netty接收UDP报文进行业务数据统计的功能,因为Netty默认情况下处理UDP收包只能由一个线程负责,无法像TCP协议那种基于主从reactor模型实现多线程监听端口,所以笔者查阅网上资料查看是否有什么方式可以接收UDP收包的性能瓶颈…...
Ubuntu 22.04 Tesla V100s显卡驱动,CUDA,cuDNN,MiniCONDA3 环境的安装
今天来将由《蓝创精英团队》带来一个Ubuntu 显卡环境的安装,主要是想记录下来,方便以后快捷使用。 主要的基础环境 显卡驱动 (nvidia-smi)CUDA (nvidia-smi 可查看具体版本)cuDNN (cuda 深度学习加速库)Conda python环境管理(Miniconda3) Nvidia 驱动…...
FFmpeg转码流程和常见概念
视频格式:mkv,flv,mov,wmv,avi,mp4,m3u8,ts等等 FFmpeg的转码工具,它的处理流程是这样的: 从输入源获得原始的音视频数据,解封装得到压缩封装的音…...
【01】GeoScene生产海图或者电子航道图
1.1 什么是电子海图制图模块 GeoScene海事模块是一个用于管理和制作符合国际水文组织(IHO)S-100系列标准和S-57标准的海事数据的系统。提供了S-100和S-57工具,用于加载基于S-100的要素目录、创建基于S-57传输结构的数据、输入数据、符号化数…...
TWS蓝牙耳机的船运模式
TWS蓝牙耳机的船运模式 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群赠送语音信号处理降噪算法,蓝牙耳机音频,DSP音频项目核心开发资料, TWS蓝牙耳机的船运模式是指在将耳机从一个地方运送到另一个地方时,…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
全面解析各类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…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
