【技术派后端篇】技术派中基于 Redis 的缓存实践
在互联网应用追求高并发和高可用的背景下,缓存对于提升程序性能至关重要。相较于本地缓存 Guava Cache 和 Caffeine,Redis 具有显著优势。Redis 支持集群和分布式部署,能横向扩展缓存容量和负载能力,适应大型分布式系统的缓存需求;支持数据持久化存储,可将缓存数据存于磁盘,保障数据不丢失;支持多种数据结构,如字符串、哈希表、列表、集合和有序集合等,提供更灵活的缓存能力;具备主从同步和哨兵机制,实现高可用性和容错能力;还提供丰富的数据处理命令,如排序、聚合、管道和 Lua 脚本等,便于高效处理缓存数据。
Redis 本质上是一个开源的基于内存的 NoSQL 数据库,更适合作为数据库前的缓存层组件。它支持多种数据结构,如 String、List、Set、Hash、ZSet 等,并且支持数据持久化,通过快照和日志将内存数据保存到硬盘,重启后可再次加载使用,其主从复制、哨兵等特性使其成为广受欢迎的缓存中间件。
在 Java 后端开发中,Redis 是面试常考的技术栈之一,是开发四大件(Java 基础、Spring Boot、MySQL、Redis)之一,因此在开发和学习中应扎实掌握 Redis 相关知识。
1 Redis 与 Spring Boot 的整合
-
添加依赖:在 pom.xml 中添加 Redis 依赖,Spring Boot 默认使用 Lettuce 作为 Redis 连接池,可避免频繁创建和销毁连接,提升应用性能和可靠性。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
配置 Redis:在 application.yml 中进行配置,本地配置指定 host 和 port 即可,生产环境配置可参考相关教程。
redis:host: localhostport: 6379password:
-
启动 Redis 服务:以
Window
系统为例,使用终端工具启动 Redis 服务,可通过redis-cli ping
检查 Redis 服务是否安装和运行正常,使用redis-server
启动服务,默认端口为 6379。
-
编写测试类:编写 Redis 测试类
RedisTemplateDemo
,快速验证 Redis 在项目中的可用性。@SpringBootTest(classes = QuickForumApplication.class) public class RedisTemplateDemo {@Autowiredprivate RedisTemplate<Object, Object> redisTemplate;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Testpublic void testPut() {redisTemplate.opsForValue().set("itwanger", "沉默王二");stringRedisTemplate.opsForList().rightPush("girl", "陈清扬");stringRedisTemplate.opsForList().rightPush("girl", "小转玲");stringRedisTemplate.opsForList().rightPush("girl", "茶花女");}@Testpublic void testGet() {Object value = redisTemplate.opsForValue().get("itwanger");System.out.println(value);List<String> girls = stringRedisTemplate.opsForList().range("girl", 0, -1);System.out.println(girls);} }
@SpringBootTest(classes = QuickForumApplication.class)
注解指定项目启动类,@Autowired
注解注入 RedisTemplate
和 StringRedisTemplate
。RedisTemplate
可操作任意类型数据,StringRedisTemplate
仅能操作字符串类型数据。testPut()
方法分别操作 Redis 中的字符串和列表类型数据,testGet()
方法获取相应数据。
2 在 Spring Boot 中使用 Redis 操作不同数据结构
-
字符串:注入
RedisTemplate
,使用opsForValue()
方法获取操作对象,通过set()
设置键值对,get()
获取值。@Autowired private RedisTemplate<String, Object> redisTemplate;public void set(String key, Object value) {redisTemplate.opsForValue().set(key, value); }public Object get(String key) {return redisTemplate.opsForValue().get(key); }
-
列表:注入
StringRedisTemplate
,利用opsForList()
方法获取操作对象,rightPush()
向列表右侧添加元素,range()
获取指定下标范围元素。@Autowired private StringRedisTemplate stringRedisTemplate;public void push(String key, String value) {stringRedisTemplate.opsForList().rightPush(key, value); }public List<String> range(String key, int start, int end) {return stringRedisTemplate.opsForList().range(key, start, end); }
-
哈希:使用
opsForHash()
方法获取操作对象,put()
添加字段和值,get()
获取指定字段值。@Autowired private RedisTemplate<String, Object> redisTemplate;public void hset(String key, String field, Object value) {redisTemplate.opsForHash().put(key, field, value); }public Object hget(String key, String field) {return redisTemplate.opsForHash().get(key, field); }
-
集合:通过
opsForSet()
方法获取操作对象,add()
添加元素,members()
获取所有元素。@Autowired private StringRedisTemplate stringRedisTemplate;public void sadd(String key, String value) {stringRedisTemplate.opsForSet().add(key, value); }public Set<String> smembers(String key) {return stringRedisTemplate.opsForSet().members(key); }
-
有序集合:使用
opsForZSet()
方法获取操作对象,add()
添加元素和分值,range()
获取指定下标范围元素。@Autowired private RedisTemplate<String, Object> redisTemplate;public void zadd(String key, String value, double score) {redisTemplate.opsForZSet().add(key, value, score); }public Set<Object> zrange(String key, long start, long end) {return redisTemplate.opsForZSet().range(key, start, end); }
3 技术派中的 Redis 实例应用
技术派使用 Redis 缓存用户 session 信息和 sitemap(用于帮助搜索引擎更好索引网站内容的 XML 文件)。
3.1 RedisClient 类
基于 RedisTemplate
封装,简化使用成本。代码路径:com.github.paicoding.forum.core.cache.RedisClient
。
ForumCoreAutoConfig
配置类通过构造方法注入 RedisTemplate
,并调用 RedisClient.register(redisTemplate)
注册到 RedisClient
中,RedisTemplate
由 Spring Boot 自动配置机制注入。
private static RedisTemplate<String, String> template;public static void register(RedisTemplate<String, String> template) {RedisClient.template = template;
}
public class ForumCoreAutoConfig {@Autowiredprivate ProxyProperties proxyProperties;public ForumCoreAutoConfig(RedisTemplate<String, String> redisTemplate) {RedisClient.register(redisTemplate);}
3.2 用户 session 相关操作
- 验证码校验成功后,调用
RedisClient
的setStrWithExpire
方法存储 session,该方法使用RedisTemplate
的execute
方法,通过RedisCallback
接口的doInRedis
方法执行 Redis 命令,调用RedisConnection
的setEx
方法设置键值对及过期时间。
/*** 带过期时间的缓存写入** @param key* @param value* @param expire s为单位* @return*/
public static Boolean setStrWithExpire(String key, String value, Long expire) {return template.execute(new RedisCallback<Boolean>() {@Overridepublic Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {return redisConnection.setEx(keyBytes(key), expire, valBytes(value));}});
}
- 用户登出时,调用
del
方法删除 session。
public static void del(String key) {template.execute((RedisCallback<Long>) con -> con.del(keyBytes(key)));
}
- 用户登录时,调用
getStr
方法根据 session 获取用户 ID。
public static String getStr(String key) {return template.execute((RedisCallback<String>) con -> {byte[] val = con.get(keyBytes(key));return val == null? null : new String(val);});
}
具体调用代码位于com.github.paicoding.forum.service.user.service.help.UserSessionHelper
。
/*** 使用jwt来存储用户token,则不需要后端来存储session了*/
@Slf4j
@Component
public class UserSessionHelper {@Component@Data@ConfigurationProperties("paicoding.jwt")public static class JwtProperties {/*** 签发人*/private String issuer;/*** 密钥*/private String secret;/*** 有效期,毫秒时间戳*/private Long expire;}private final JwtProperties jwtProperties;private Algorithm algorithm;private JWTVerifier verifier;public UserSessionHelper(JwtProperties jwtProperties) {this.jwtProperties = jwtProperties;algorithm = Algorithm.HMAC256(jwtProperties.getSecret());verifier = JWT.require(algorithm).withIssuer(jwtProperties.getIssuer()).build();}public String genSession(Long userId) {// 1.生成jwt格式的会话,内部持有有效期,用户信息String session = JsonUtil.toStr(MapUtils.create("s", SelfTraceIdGenerator.generate(), "u", userId));String token = JWT.create().withIssuer(jwtProperties.getIssuer()).withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getExpire())).withPayload(session).sign(algorithm);// 2.使用jwt生成的token时,后端可以不存储这个session信息, 完全依赖jwt的信息// 但是需要考虑到用户登出,需要主动失效这个token,而jwt本身无状态,所以再这里的redis做一个简单的token -> userId的缓存,用于双重判定RedisClient.setStrWithExpire(token, String.valueOf(userId), jwtProperties.getExpire() / 1000);return token;}public void removeSession(String session) {RedisClient.del(session);}/*** 根据会话获取用户信息** @param session* @return*/public Long getUserIdBySession(String session) {// jwt的校验方式,如果token非法或者过期,则直接验签失败try {DecodedJWT decodedJWT = verifier.verify(session);String pay = new String(Base64Utils.decodeFromString(decodedJWT.getPayload()));// jwt验证通过,获取对应的userIdString userId = String.valueOf(JsonUtil.toObj(pay, HashMap.class).get("u"));// 从redis中获取userId,解决用户登出,后台失效jwt token的问题String user = RedisClient.getStr(session);if (user == null || !Objects.equals(userId, user)) {return null;}return Long.valueOf(user);} catch (Exception e) {log.info("jwt token校验失败! token: {}, msg: {}", session, e.getMessage());return null;}}
}
3.3 sitemap 相关操作
- 获取 sitemap 时,调用
hGetAll
方法,使用RedisTemplate
的execute
方法执行 Redis 命令,通过RedisCallback
接口的doInRedis
方法调用RedisConnection
的hGetAll
方法获取哈希表所有字段和值,并进行类型转换后返回。
public static <T> Map<String, T> hGetAll(String key, Class<T> clz) {Map<byte[], byte[]> records = template.execute((RedisCallback<Map<byte[], byte[]>>) con -> con.hGetAll(keyBytes(key)));if (records == null) {return Collections.emptyMap();}Map<String, T> result = Maps.newHashMapWithExpectedSize(records.size());for (Map.Entry<byte[], byte[]> entry : records.entrySet()) {if (entry.getKey() == null) {continue;}result.put(new String(entry.getKey()), toObj(entry.getValue(), clz));}return result;}
- 添加文章时,调用
hSet
方法,使用RedisTemplate
的execute
方法,通过RedisCallback
接口的doInRedis
方法,根据值的类型转换为字符串后调用RedisConnection
的hSet
方法设置哈希表字段值,返回设置结果。
public static <T> T hGet(String key, String field, Class<T> clz) {return template.execute((RedisCallback<T>) con -> {byte[] records = con.hGet(keyBytes(key), valBytes(field));if (records == null) {return null;}return toObj(records, clz);});
}
- 移除文章时,调用
hDel
方法,使用RedisTemplate
的execute
方法,通过RedisCallback
接口的doInRedis
方法调用RedisConnection
的hDel
方法删除字段,返回删除结果。
public static <T> Boolean hDel(String key, String field) {return template.execute(new RedisCallback<Boolean>() {@Overridepublic Boolean doInRedis(RedisConnection connection) throws DataAccessException {return connection.hDel(keyBytes(key), valBytes(field)) > 0;}});
}
- 初始化 sitemap 时,先调用
del
方法,再调用hMSet
方法,hMSet
方法使用RedisTemplate
的execute
方法,通过RedisCallback
接口的doInRedis
方法调用RedisConnection
的hMSet
方法一次性设置多个哈希表字段值。
/*** fixme: 加锁初始化,更推荐的是采用分布式锁*/
private synchronized void initSiteMap() {long lastId = 0L;RedisClient.del(SITE_MAP_CACHE_KEY);while (true) {List<SimpleArticleDTO> list = articleDao.getBaseMapper().listArticlesOrderById(lastId, SCAN_SIZE);// 刷新文章的统计信息list.forEach(s -> countService.refreshArticleStatisticInfo(s.getId()));// 刷新站点地图信息Map<String, Long> map = list.stream().collect(Collectors.toMap(s -> String.valueOf(s.getId()), s -> s.getCreateTime().getTime(), (a, b) -> a));RedisClient.hMSet(SITE_MAP_CACHE_KEY, map);if (list.size() < SCAN_SIZE) {break;}lastId = list.get(list.size() - 1).getId();}
}
为提升搜索引擎对技术派的收录,开发了 sitemap 自动生成工具,在 SitemapServiceImpl
中通过定时任务每天 5:15 分刷新站点地图,确保数据一致性。
/*** 采用定时器方案,每天5:15分刷新站点地图,确保数据的一致性*/@Scheduled(cron = "0 15 5 * * ?")public void autoRefreshCache() {log.info("开始刷新sitemap.xml的url地址,避免出现数据不一致问题!");refreshSitemap();log.info("刷新完成!");}
4 关于 RedisTemplate 的 execute 方法
RedisTemplate
的 execute(RedisCallback<T> action)
方法用于执行任意 Redis 命令,接收 RedisCallback
接口作为参数,将 Redis 连接传递给回调接口执行命令。
@Nullable
public <T> T execute(RedisCallback<T> action) {return this.execute(action, this.isExposeConnection());
}
以下是测试用例示例:
@Test
public void testExecute() {redisTemplate.execute(new RedisCallback<Object>() {@Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {connection.set("itwanger".getBytes(), "沉默王二".getBytes());byte[] value = connection.get("itwanger".getBytes());String strValue = new String(value);System.out.println(strValue);return null;}});
}
5 总结
Redis 是高性能的内存数据存储系统,支持多种数据结构。在 Spring Boot 中使用 Redis 作为缓存可提升应用性能和响应速度,通过 spring-boot-starter-data-redis
整合 Redis 操作简便。可使用 RedisTemplate
执行各种 Redis 命令,技术派使用 Redis 缓存 session 和 sitemap,应用了字符串和哈希表数据结构。未来将进一步介绍 Redis 其他数据结构和高级功能,如发布/订阅、事务、Lua 脚本等。
6 参考链接
- 技术派Redis的缓存示例
- 项目仓库(GitHub)
- 项目仓库(码云)
相关文章:
【技术派后端篇】技术派中基于 Redis 的缓存实践
在互联网应用追求高并发和高可用的背景下,缓存对于提升程序性能至关重要。相较于本地缓存 Guava Cache 和 Caffeine,Redis 具有显著优势。Redis 支持集群和分布式部署,能横向扩展缓存容量和负载能力,适应大型分布式系统的缓存需求…...
系统安装及应用
重点 账号安全控制 系统引导和登陆控制 弱口令检测 端口扫描 前言 随着信息技术的快速发展,系统安全成为我们日常生活和工作中不可或缺的一部分。本章节主要探讨系统安全及应用,涵盖了账号安全控制、系统引导和登录控制、弱口令检测以及端口扫描等多个方面,为我们提供了一…...
发布事件和Insert数据库先后顺序
代码解释 csharp await PublishCreatedAsync(entity).ConfigureAwait(false); await Repository.InsertAsync(entity).ConfigureAwait(false);PublishCreatedAsync(entity):这是一个异步方法,其功能是发布与实体创建相关的事件。此方法或许会通知其他组…...
【英语语法】词法---冠词
目录 冠词一、不定冠词:a / an1. 基本用法2. 主要使用场景3. 特殊情况 二、定冠词:the1. 基本用法2. 主要使用场景3. 特殊情况 三、零冠词1. 基本规则2. 特殊情况 四、冠词对比五、常见错误总结 冠词 冠词是英语中用于限定名词的一类虚词,分…...
android的 framework 有哪些知识点和应用场景
Android Framework 知识点 1. 四大组件 Activity(活动) 是 Android 应用中最基本的组件,用于实现用户界面。一个 Activity 通常对应一个屏幕的内容。有自己的生命周期,包括 onCreate、onStart、onResume、onPause、onStop、onDe…...
Prompt 攻击与防范:大语言模型安全的新挑战
随着大语言模型(LLM)在企业服务、智能助手、搜索增强等领域的广泛应用,围绕其"Prompt"机制的安全问题也逐渐引起关注。其中最具代表性的,就是所谓的 Prompt Injection(提示词注入)攻击。 本文将…...
Ubuntu20.04安装Pangolin遇到的几种报错的解决方案
1.添加两个编译选项 /usr/include/OpenEXR/half.h:121:13: note: because ‘half’ has user-provided ‘half& half::operator(half)’121 | half & operator (half h);| ^~~~~~~~ 解决方案: 在CMakeList中添加以下两句: …...
软考 中级软件设计师 考点知识点笔记总结 day14 关系代数 数据库完整性约束
文章目录 6.5 关系代数6.5.1 关系代数—七种基本运算 6.6 数据库完整性约束6.7 关系型数据库SQL简介 6.5 关系代数 候选码(键):若关系中的某一属性或属性组的值能唯一标识一个元组,则称该属性或属性组为候选码。 主码࿰…...
前端vue监听 -watch
前端vue监听 -watch 前言基本用法监听简单数据属性监听对象属性 高级用法深度监听对象即时触发监听监听计算属性 注意事项 前言 在 Vue.js 里,watch 选项可用于响应式地监听数据的变化,当被监听的数据发生改变时,就会触发相应的回调函数来执…...
Linux之信号
目录 一、预备知识 二、信号的产生 一、键盘产生信号 二、系统调用 三、调用系统命令向进程发信号 kill 四、硬件异常 五、软件条件 三、信号的保存 四、信号的处理 一、预备知识 1.信号!信号量。两者没有任何关系 2.什么是信号? 定义一&…...
微软Edge浏览器字体设置
前言 时间:2025年4月 自2025年4月起,微软Edge浏览器的默认字体被微软从微软雅黑替换成了Noto Sans,如下图。Noto Sans字体与微软雅黑风格差不多,但在4K以下分辨率的显示器上较微软雅黑更模糊,因此低分辨率的显示器建议…...
Java中 关于编译(Compilation)、类加载(Class Loading) 和 运行(Execution)的详细区别解析
以下是Java中 编译(Compilation)、类加载(Class Loading) 和 运行(Execution) 的详细区别解析: 1. 编译(Compilation) 定义 将Java源代码(.java文件&#x…...
[python] set
1.添加元素 在 Python 中,向 set 添加一个元素可以使用 add() 方法。如果添加的元素已经存在于 set 中,add() 不会重复添加(因为 set 具有自动去重的特性)。 方法 1:add(element)(添加单个元素࿰…...
转化率提升47%?亚马逊用户行为预测模型深度解读
在亚马逊运营的战场上,谁能更精准地读懂用户行为,谁就更可能赢得转化率的胜利。近年来,越来越多卖家借助“用户行为预测模型”来优化Listing布局、广告投放策略、甚至库存管理,而这些数据驱动的决策也确确实实地带来了质的提升。 …...
C++计算 n! 中末尾零的数量
* 详细说明* 给定一个整数作为输入。目标是找出该数的阶乘结果中末尾零的数量。 一个数 N 的阶乘是范围 [1, N] 内所有数的乘积。* * 我们知道,只有当一个数是 10 的倍数或者有因数对 (2, 5) 时,才会产生末尾零。 在任何大于 5 的数的阶乘中,…...
大模型中超参数TopK是什么
大模型中的超参数Top-K是文本生成过程中的关键控制参数,主要用于平衡生成结果的确定性与多样性。以下从定义、工作原理、应用场景及与其他参数的协同关系进行详细阐述: 一、Top-K的定义与核心机制 基本定义 Top-K(Top-K Sampling)是一种基于概率采样的文本生成策略。其核心…...
NetApp ONTAP 9 故障磁盘更换操作指南
以前写过一篇7-mode的磁盘更换文档,好几个朋友反馈说命令都没有,都不对。主要原因是客户现在的环境都是ontap 9的cluster-mode环境了,所以很多命令都不一样了。为此,这里专门就ontap 9的cluster-mode写一篇磁盘更换操作指南&#…...
leetcode day 35 01背包问题 416+1049
0-1背包问题 (1)第一种情况:二维dp[i][j]数组 dp[i][j]表示[0,i]的物品放入容量为j背包的最大价值 不放物品i,dp[i][j]dp[i-1][j] 放物品i,dp[i][j]dp[i-1][j-w[i]]v[i] 递推公式为: dp[i][j]dp[i-1][j];//不放 if(w[i]<j)dp…...
MySQL的基本操作
显示所有数据库: SHOW DATABASES; 系统默认数据库: 数据库名用途information_schema存储 MySQL 服务器元数据(如数据库、表、列信息),只读mysql存储用户权限、密码、日志等核心数据(不要随意修改ÿ…...
CSS伪类、clip-path实现三角形、箭头绘制
<template><div :class"$options.name"><div class"triangle-container1"><!-- 伪类三角形:向右 --><div class"triangle-RM"></div><!-- 伪类三角形:向下 --><div class&q…...
基于大模型的腹股沟疝全流程预测与诊疗方案研究报告
目录 一、引言 1.1 研究背景与目的 1.2 研究方法与创新点 二、大模型在腹股沟疝术前评估中的应用 2.1 腹股沟疝概述与诊断方法 2.2 术前评估指标与数据收集 2.3 大模型预测原理与实现 2.4 预测结果与传统评估对比 三、基于大模型预测的手术方案制定 3.1 手术方式选择…...
零基础上手Python数据分析 (20):Seaborn 统计数据可视化 - 轻松绘制精美统计图表!
写在前面 —— 告别 Matplotlib 繁琐定制,拥抱 Seaborn 便捷之美,让统计可视化更高效 在前面两篇博客中,我们学习了 Python 数据可视化的基石 Matplotlib,掌握了绘制基础图表和进行高级定制的技巧。 Matplotlib 功能强大且灵活,能够满足几乎所有的二维绘图需求。 然而,…...
elasticsearch7.15节点磁盘空间满了迁移数据到新磁盘
一.数据安全迁移 在 Elasticsearch 中设置某个节点临时不可用(例如进行维护或升级),可以通过以下步骤安全地操作,避免数据丢失或集群状态异常 1: 排除节点分片分配,触发分片迁移到其他节点 PUT /_cluster/settings {&…...
MCP案例—客户端和服务端
MCP简介 Model Context Protocol (模型上下文协议),简称MCP,MCP是一种协议,用于LLM与外部拓展资源交互的协议。 想了解具体细节可参考作者本篇文章MCP理论指南 准备 本篇文章将带你通过python创建MCP客户端及服务端,并连接到本…...
排序模型(Learning to Rank)
排序模型(Learning to Rank) 要解决的问题 排序模型旨在解决信息检索中的排序优化问题。例如: 搜索引擎中对候选网页的排序推荐系统中物品的展示顺序广告系统中广告位的分配 核心挑战:根据上下文特征,将最相关/最有…...
L1-1、Prompt 是什么?为什么它能“控制 AI”?
*Prompt 入门 L1-1 想象一下,你只需输入一句话,AI 就能自动为你写一篇文案、生成一份报告、甚至规划你的创业计划。这种“对话即编程”的背后魔法,就是 Prompt 的力量。 🔍 一、Prompt 的定义与由来 Prompt(提示词&am…...
RolmOCR重磅开源:基于Qwen2.5-VL,速度提升40%,手写/倾斜文档识别准确率超92%
向大家介绍一款全新的开源OCR模型——RolmOCR!这款由Reducto AI团队基于阿里巴巴强大的Qwen2.5-VL-7B-Instruct视觉语言模型微调而来的利器,不仅在速度和效率上实现了显著提升(据称处理速度相比其前身olmOCR提升了约40%)ÿ…...
系统架构设计(二):基于架构的软件设计方法ABSD
“基于架构的软件设计方法”(Architecture-Based Software Design, ABSD)是一种通过从软件架构层面出发指导详细设计的系统化方法。它旨在桥接架构设计与详细设计之间的鸿沟,确保系统的高层结构能够有效指导后续开发。 ABSD 的核心思想 ABS…...
[langchain教程]langchain03——用langchain构建RAG应用
RAG RAG过程 离线过程: 加载文档将文档按一定条件切割成片段将切割的文本片段转为向量,存入检索引擎(向量库) 在线过程: 用户输入Query,将Query转为向量从向量库检索,获得相似度TopN信息将…...
Android 图片加载框架 Glide 详细介绍
一、简单使用 1、加载图片 导入依赖 implementation("com.github.bumptech.glide:glide:4.16.0")编写代码 private static final String url = "http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg";btnPic.setOnClickList…...
vue2解析html中的公式,使用vue-katex
文本是markdown格式,需要解析markdown <p v-html"md.render(text)"></p>import MarkdownIt from markdown-it ...const mdRender MarkdownIt(); ...data中md: new MarkdownIt(),现在文本中会出现数学公式,解析使用vue-katex 1.…...
使用Unity Cache Server提高效率
2021年1月20日19:04:28 1 简介 Unity Cache Server,翻译过来就是Unity缓存服务器 1.1 缓存服务器の官方介绍 Unity 有一个完全自动的资源管线。每当修改 .psd 或 .fbx 文件等源资源时,Unity 都会检测到更改并自动将其重新导入。随后,Unity 以内部格式存储从文件导入的数…...
【C++】模板2.0
最近学习了一些模板的知识,速写本博客作为学习笔记,若有兴趣,欢迎垂阅读! 1.非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名…...
深入解析 Linux 文件系统中的软硬链接:从原理到实践
引言 在 Linux 系统中,软链接(Symbolic Link) 和 硬链接(Hard Link) 是文件管理的两大核心机制。它们如同文件系统的“快捷方式”与“分身术”,既能节省存储空间,又能实现灵活的文件管理。但两…...
JumpServer多用户VNC桌面配置指南:实现多端口远程访问
在当今的云计算和远程工作环境中,高效且安全地管理多用户远程桌面访问变得越来越重要。本文将详细介绍如何在JumpServer中配置多个VNC桌面,以满足不同用户的远程访问需求。我们将以创建第二个桌面为例,为用户user2配置VNC访问。 一、背景说明 JumpServer作为一款优秀的开源…...
【数据结构入门训练DAY-19】总结数据结构中的栈
文章目录 前言一、栈的思想二、栈的解题思路结语 前言 本次训练内容: 栈的复习。总结栈的基本操作 一、栈的思想 在数据结构中,栈是一种很常见的算法。栈——就像你往桶里放东西似的,要取出桶内的物体就得先把桶顶的物品取出来ÿ…...
MyBatis-Plus 防止 SQL 注入最佳实践指南
🚫 MyBatis-Plus 防止 SQL 注入最佳实践指南 作者:William Dawson 标签:Java、MyBatis-Plus、安全、SQL 注入、防护 💥 什么是 SQL 注入? SQL 注入是一种常见的安全漏洞,攻击者通过恶意构造 SQL 输入参数&…...
AI之pdf解析:Tesseract、PaddleOCR、RapidPaddle(可能为 RapidOCR)和 plumberpdf 的对比分析及使用建议
目录标题 Tesseract、PaddleOCR、RapidPaddle(可能为 RapidOCR)和 plumberpdf 的对比分析1. Tesseract类型: 开源 OCR 引擎特点:缺点:适用场景: 2. PaddleOCR (推荐)类型:特点:缺点:适用场景: 复杂版式文档、多语言混合文本、需要高精度识别的场景&#…...
经典文献阅读之--Kinematic-ICP(动态优化激光雷达与轮式里程计融合)
0. 简介 传统的激光雷达里程计系统通过点云配准来计算移动机器人的自运动(ego-motion),但它们通常没有考虑机器人的运动学特性,这可能导致不准确的运动估计,特别是在机器人不可能发生某些运动(如沿z轴的小…...
【显卡占用】kill程序后,显卡仍被占用
如果 kill 程序执行了,但显卡仍然显示被占用,咋个办? 如图所示,GPU-Util占用为0%,但显示占用48G,且无法再上程序: 执行命令: fuser -v /dev/nvidia* kill pid若上述方法无法解决&am…...
在 macOS 上合并 IntelliJ IDEA 的项目窗口
在使用 IntelliJ IDEA 开发时,可能会打开多个项目窗口,这可能会导致界面变得混乱。为了提高工作效率,可以通过合并项目窗口来简化界面。本文将介绍如何在 macOS 上合并 IntelliJ IDEA 的项目窗口。 操作步骤 打开 IntelliJ IDEA: 启动你的 I…...
IO流--字节流详解
IO流 用于读写数据的(可以读写文件,或网络中的数据) 概述: I指 Input,称为输入流:负责从磁盘或网络上将数据读到内存中去 O指Output,称为输出流,负责写数据出去到网络或磁盘上 因…...
6N60-ASEMI机器人功率器件专用6N60
编辑:ll 6N60-ASEMI机器人功率器件专用6N60 型号:6N60 品牌:ASEMI 封装:TO-220F 批号:最新 最大漏源电流:6A 漏源击穿电压:600V RDS(ON)Max:1.20Ω …...
实现侧边栏点击标题列表,和中间列表区域联动效果
左侧边栏标题列表实现: -------------------html-----------------------<divclass"uav":class"{ hidden: !isVisible, visible: isVisible }"><ul id"toc"><liv-for"(item, index) in HotList":key"…...
基于MuJoCo物理引擎的机器人学习仿真框架robosuite
Robosuite 基于 MuJoCo 物理引擎,能支持多种机器人模型,提供丰富多样的任务场景,像基础的抓取、推物,精细的开门、拧瓶盖等操作。它可灵活配置多种传感器,提供本体、视觉、力 / 触觉等感知数据。因其对强化学习友好&am…...
kafka监控kafka manager(CMAK)部署配置
一、准备工作 1.1、服务器信息梳理 角色IP操作系统安装服务监控机10.45.19.20Linux CentOS 7.9CMAK3.0.0.5、ZooKeeper3.9.0、JDK11、JDK1.8被监控机 Kafka broker.id 050.50.50.101Linux CentOS 7.9Kafka、ZooKeeper(任意版本)被监控机 Kafka broker.…...
线程池的介绍
目录 一、什么是线程池 二、线程池的详细内容 三、线程池的简化 一、什么是线程池 提到线程池,我们可能想到 常量池,可以先来说说常量池: 像是字符串常量,在Java程序最初构建的时候,就已经准备好了,等程…...
day33和day34图像处理OpenCV
文章目录 一、图像预处理12 图像梯度处理12.3 Sobel算子12.4 Laplacian算子1.原理:2.语法: 13 图像边缘检测思路13.1 高斯滤波去噪点13.2 计算图像的梯度与方向13.3 非极大值抑制13.4 双阈值筛选13.5 Canny方法和使用 14 绘制图像轮廓14.1 什么是轮廓14.…...
电脑硬盘常见的几种接口类型
一、传统接口(机械硬盘为主) 1. SATA 接口(Serial ATA) 特点: 最主流的机械硬盘(HDD)接口,广泛用于台式机和笔记本电脑。传输速度较慢,理论最大带宽为 6 Gbpsÿ…...
Windows网络及服务:制作系统盘
今天我要介绍的是一个比较有意思且好玩的一个小玩意儿:关于系统盘的制作; 注明:对于系统盘的制作,以及接下来的课程,基本是作为动手课业来进行的,这也是作为程序员的必要进行的一项活动。 对于系统盘&…...