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

libilibi项目优化(1)使用Redis实现缓存

第一版

获取视频信息使用旁路缓存

  • 当视频信息存在缓存中时(命中),直接从缓存中获取。
  • 不存在缓存中时,先从数据库中查出对应的信息,写入缓存后再放回数据。
//获取视频详细信息@RequestMapping("/getVideoInfo")public ResponseVO getVideoInfo(@NotEmpty String videoId) {//旁路缓存模式,先从缓存中拿VideoInfo videoInfo = redisComponent.getVideoInfoDetail(videoId);if(videoInfo == null){//缓存中不存在就从数据库中取videoInfo = videoInfoService.getVideoInfoByVideoId(videoId);}if(videoInfo==null){throw new BusinessException(ResponseCodeEnum.CODE_404);}//将视频信息保存到缓存中redisComponent.saveVideoInfoDeTail(videoInfo);//获取当前用户对应的点赞和投币信息TokenUserInfoDto userInfoDto = getTokenUserInfoDto();List<UserAction> userActionList = new ArrayList<>();if(userInfoDto!=null){UserActionQuery actionQuery = new UserActionQuery();actionQuery.setVideoId(videoId);actionQuery.setUserId(userInfoDto.getUserId());//查询视频对应用户的点赞投币收藏信息actionQuery.setActionTypeArray(new Integer[]{UserActionTypeEnum.VIDEO_LIKE.getType(),UserActionTypeEnum.VIDEO_COLLECT.getType(),UserActionTypeEnum.VIDEO_COIN.getType(),});userActionList = userActionService.findListByParam(actionQuery);}VideoInfoResultVo resultVo = new VideoInfoResultVo();//设置用户的点赞投币收藏信息resultVo.setUserActionList(userActionList);resultVo.setVideoInfo(CopyTools.copy(videoInfo, VideoInfoVo.class));return getSuccessResponseVO(resultVo);}

用户点赞、收藏使用异步缓存写入

在进行更新视频点赞、收藏数量等信息时,并非直接修改数据库,而是先修改缓存中的数据,再利用消息队列,或定时任务等方式,将缓存中的数据更新到数据库

@Override@Transactional(rollbackFor = Exception.class)public void saveAction(UserAction bean) {//旁路缓存模式,想从缓存中拿VideoInfo videoInfo = redisComponent.getVideoInfoDetail(bean.getVideoId());if(videoInfo == null){//缓存中不存在就从数据库中取videoInfo = videoInfoMapper.selectByVideoId(bean.getVideoId());}if(videoInfo==null){throw new BusinessException(ResponseCodeEnum.CODE_404);}//设置视频对应的用户idbean.setVideoUserId(videoInfo.getUserId());//获得对应的用户行为(点赞,收藏,投币,评论点赞)UserActionTypeEnum actionTypeEnum = UserActionTypeEnum.getByType(bean.getActionType());if(actionTypeEnum==null){throw new BusinessException(ResponseCodeEnum.CODE_600);}//从数据库中根据视频id,评论id(若为评论点赞的话),行为类型,和用户行为来查询对应的行为记录UserAction dbAction = userActionMapper.selectByVideoIdAndCommentIdAndActionTypeAndUserId(bean.getVideoId(),bean.getCommentId(), bean.getActionType(), bean.getUserId());bean.setActionTime(new Date());switch (actionTypeEnum){//点赞和收藏case VIDEO_LIKE:case VIDEO_COLLECT://若存在点赞和收藏的记录,则取消点赞或收藏if(dbAction!=null){userActionMapper.deleteByActionId(dbAction.getActionId());}else{//添加对应的行为记录userActionMapper.insert(bean);}//若之前点过赞或收藏过则改变数量为-1,否则为1Integer changeCount = dbAction == null? Constants.ONE:-Constants.ONE;//更新视频对应的点赞或收藏信息if (actionTypeEnum.getType() == 2){Integer likeCount = videoInfo.getLikeCount();likeCount += changeCount;videoInfo.setLikeCount(likeCount);}else{Integer collectCount = videoInfo.getCollectCount();collectCount += changeCount;videoInfo.setCollectCount(collectCount);}//videoInfoMapper.updateCountInfo(bean.getVideoId(),actionTypeEnum.getField(),changeCount);if(actionTypeEnum == UserActionTypeEnum.VIDEO_COLLECT){//更新es的收藏数量esSearchComponent.updateDocCount(videoInfo.getVideoId(), SearchOrderTypeEnum.VIDEO_COLLECT.getField(), changeCount);}break;//投币case VIDEO_COIN:if (videoInfo.getUserId().equals(bean.getUserId())) {throw new BusinessException("UP主不能给自己投币");}if (dbAction != null) {throw new BusinessException("对本稿件的投币枚数已用完");}//更新当前用户的硬币数量, 返回的整数即为成功更新的行数Integer updateCoinCountInfo = userInfoMapper.updateCoinCountInfo(bean.getUserId(), -bean.getActionCount());//未成功更新说明硬币数量不够if(updateCoinCountInfo == 0){throw new BusinessException("硬币数量不足");}updateCoinCountInfo = userInfoMapper.updateCoinCountInfo(bean.getVideoUserId(), bean.getActionCount());//并发操作,数据库行上锁if(updateCoinCountInfo == 0){throw new BusinessException("投币失败");}//添加对应的行为记录userActionMapper.insert(bean);//更新视频对应的点赞或收藏信息//videoInfoMapper.updateCountInfo(bean.getVideoId(),actionTypeEnum.getField(), bean.getActionCount());break;}//将视频信息保存到缓存中redisComponent.saveVideoInfoDeTail(videoInfo);}
使用定时任务同步缓存与数据库
@Scheduled(cron = "0 0/5 * * * ?") // 每5分钟执行一次public void updateCachePeriodically() {try {// 从Redis中批量获取视频信息Map<String, VideoInfo> videoInfoBatch = redisComponent.getVideoInfoBatch();if (videoInfoBatch == null || videoInfoBatch.isEmpty()) {log.info("No video info found in Redis cache.");return;}// 提取视频信息列表List<VideoInfo> videoInfoList = new ArrayList<>(videoInfoBatch.values());// 批量更新数据库videoInfoService.addOrUpdateBatch(videoInfoList);log.info("Successfully updated {} video infos in the database.", videoInfoList.size());} catch (Exception e) {log.error("Error occurred during periodic cache update", e);}}

在你的第一版实现中,已经很好地结合了旁路缓存(视频基本信息)和异步写入缓存(点赞、收藏、播放量等高频修改信息)。以下是对你现有实现的分析和进一步优化的建议,以确保数据一致性和系统的高效性。


现有实现的优点

  1. 旁路缓存模式

    • 视频基本信息通过旁路缓存模式实现,减少了数据库的读取压力。
    • 缓存未命中时,从数据库加载数据并更新缓存,保证了数据的最终一致性。
  2. 异步写入缓存

    • 点赞、收藏等高频修改信息直接更新缓存,然后通过定时任务批量同步到数据库,减少了数据库的写入压力。
    • 使用消息队列或定时任务确保数据最终一致性。
  3. 定时任务同步

    • 定时任务定期从缓存中获取数据并同步到数据库,避免了实时同步带来的性能开销。

潜在问题和优化建议

1. 缓存穿透问题

在旁路缓存模式下,如果视频信息不存在,每次请求都会直接查询数据库,可能导致数据库压力过大。

优化建议

  • 在缓存中存储一个空对象或特殊标记(如 nullempty),表示该 videoId 对应的视频信息不存在。
  • 示例:
    if (videoInfo == null) {videoInfo = videoInfoService.getVideoInfoByVideoId(videoId);if (videoInfo == null) {redisComponent.saveVideoInfoDetail(new VideoInfo(videoId, "empty"), 5, TimeUnit.MINUTES); // 存储空对象,设置较短的过期时间throw new BusinessException(ResponseCodeEnum.CODE_404);}
    }
    
2. 缓存更新冲突

在多线程环境下,多个请求可能同时从数据库加载数据并更新缓存,导致缓存写入冲突。

优化建议

  • 使用分布式锁(如 Redisson)控制缓存的写入操作。
  • 示例:
    if (videoInfo == null) {redisComponent.lockVideoInfo(videoId); // 加锁videoInfo = videoInfoService.getVideoInfoByVideoId(videoId);if (videoInfo == null) {throw new BusinessException(ResponseCodeEnum.CODE_404);}redisComponent.saveVideoInfoDetail(videoInfo);redisComponent.unlockVideoInfo(videoId); // 解锁
    }
    
3. 数据一致性问题

在异步写入缓存模式下,缓存数据可能因为服务故障而丢失,导致数据不一致。

优化建议

  • 消息队列:将每次用户行为(如点赞、收藏)记录到消息队列中,后台消费者定期从队列中读取行为记录并更新缓存和数据库。
  • 持久化更新日志:在更新缓存时,将更新操作记录到持久化存储(如日志文件或数据库),以便在缓存服务故障时恢复数据。
  • 示例:
    // 发送消息到队列
    messageQueue.send(new VideoActionMessage(bean.getVideoId(), actionTypeEnum, changeCount));
    
4. 定时任务的优化

定时任务每次同步所有缓存数据,可能导致性能问题,尤其是在缓存数据量较大时。

优化建议

  • 增量更新:只同步缓存中发生变化的数据,而不是全量同步。
  • 设置缓存过期时间:为缓存中的高频修改信息设置较短的过期时间(如 5 分钟),确保数据不会长时间不一致。
  • 示例:
    @Scheduled(cron = "0 0/5 * * * ?")
    public void updateCachePeriodically() {List<VideoMetrics> changedMetrics = redisComponent.getChangedMetrics();if (changedMetrics == null || changedMetrics.isEmpty()) {log.info("No changed video metrics found in Redis cache.");return;}videoInfoService.updateMetricsBatch(changedMetrics);log.info("Successfully updated {} video metrics in the database.", changedMetrics.size());
    }
    
5. 数据库更新的原子性

在更新数据库时,可能存在并发问题,导致数据不一致。

优化建议

  • 使用数据库事务(@Transactional)确保更新操作的原子性。
  • 在更新数据库时,使用乐观锁或悲观锁,避免并发冲突。

优化后的代码示例

获取视频信息
@RequestMapping("/getVideoInfo")
public ResponseVO getVideoInfo(@NotEmpty String videoId) {VideoInfo videoInfo = redisComponent.getVideoInfoDetail(videoId);if (videoInfo == null) {redisComponent.lockVideoInfo(videoId); // 加锁videoInfo = videoInfoService.getVideoInfoByVideoId(videoId);if (videoInfo == null) {redisComponent.saveVideoInfoDetail(new VideoInfo(videoId, "empty"), 5, TimeUnit.MINUTES); // 存储空对象redisComponent.unlockVideoInfo(videoId); // 解锁throw new BusinessException(ResponseCodeEnum.CODE_404);}redisComponent.saveVideoInfoDetail(videoInfo);redisComponent.unlockVideoInfo(videoId); // 解锁}// 获取用户行为信息TokenUserInfoDto userInfoDto = getTokenUserInfoDto();List<UserAction> userActionList = new ArrayList<>();if (userInfoDto != null) {UserActionQuery actionQuery = new UserActionQuery();actionQuery.setVideoId(videoId);actionQuery.setUserId(userInfoDto.getUserId());actionQuery.setActionTypeArray(new Integer[]{UserActionTypeEnum.VIDEO_LIKE.getType(),UserActionTypeEnum.VIDEO_COLLECT.getType(),UserActionTypeEnum.VIDEO_COIN.getType()});userActionList = userActionService.findListByParam(actionQuery);}VideoInfoResultVo resultVo = new VideoInfoResultVo();resultVo.setUserActionList(userActionList);resultVo.setVideoInfo(CopyTools.copy(videoInfo, VideoInfoVo.class));return getSuccessResponseVO(resultVo);
}
用户行为处理
@Override
@Transactional(rollbackFor = Exception.class)
public void saveAction(UserAction bean) {VideoInfo videoInfo = redisComponent.getVideoInfoDetail(bean.getVideoId());if (videoInfo == null) {videoInfo = videoInfoMapper.selectByVideoId(bean.getVideoId());if (videoInfo == null) {throw new BusinessException(ResponseCodeEnum.CODE_404);}}bean.setVideoUserId(videoInfo.getUserId());UserActionTypeEnum actionTypeEnum = UserActionTypeEnum.getByType(bean.getActionType());if (actionTypeEnum == null) {throw new BusinessException(ResponseCodeEnum.CODE_600);}UserAction dbAction = userActionMapper.selectByVideoIdAndCommentIdAndActionTypeAndUserId(bean.getVideoId(), bean.getCommentId(), bean.getActionType(), bean.getUserId());bean.setActionTime(new Date());switch (actionTypeEnum) {case VIDEO_LIKE:case VIDEO_COLLECT:if (dbAction != null) {userActionMapper.deleteByActionId(dbAction.getActionId());} else {userActionMapper.insert(bean);}Integer changeCount = dbAction == null ? Constants.ONE : -Constants.ONE;updateVideoMetrics(bean.getVideoId(), actionTypeEnum, changeCount);break;case VIDEO_COIN:// 投币逻辑break;}redisComponent.saveVideoInfoDetail(videoInfo); // 更新缓存messageQueue.send(new VideoActionMessage(bean.getVideoId(), actionTypeEnum, changeCount)); // 发送消息到队列
}
更新视频指标
private void updateVideoMetrics(String videoId, UserActionTypeEnum actionType, Integer changeCount) {VideoMetrics metrics = redisComponent.getVideoMetrics(videoId);if (metrics == null) {metrics = new VideoMetrics(videoId);}switch (actionType) {case VIDEO_LIKE:metrics.setLikeCount(metrics.getLikeCount() + changeCount);break;case VIDEO_COLLECT:metrics.setCollectCount(metrics.getCollectCount() + changeCount);break;}redisComponent.saveVideoMetrics(metrics);
}
定时任务同步
@Scheduled(cron = "0 0/5 * * * ?")
public void updateCachePeriodically() {List<VideoMetrics> changedMetrics = redisComponent.getChangedMetrics();if (changedMetrics == null || changedMetrics.isEmpty()) {log.info("No changed video metrics found in Redis cache.");return;}videoInfoService.updateMetricsBatch(changedMetrics);log.info("Successfully updated {} video metrics in the database.", changedMetrics.size());
}

总结

通过上述优化,你的实现能够更好地解决以下问题:

  1. 缓存穿透:通过存储空对象避免无效的数据库查询。
  2. 缓存更新冲突:使用分布式锁确保缓存更新的线程安全。
  3. 数据一致性:结合消息队列和定时任务确保缓存与数据库的最终一致性。
  4. 性能优化:通过增量更新和缓存过期时间减少定时任务的压力。

这种设计既保证了系统的高性能,又确保了数据的一致性。

第二版(规划中)

  • 将视频的播放量,点赞,收藏信息独立出来,使用Hash进行缓存。
  • 定时任务只更新有变动的视频数据
  • 实现用户点赞高亮信息从SET中获取

相关文章:

libilibi项目优化(1)使用Redis实现缓存

第一版 获取视频信息使用旁路缓存 当视频信息存在缓存中时&#xff08;命中&#xff09;&#xff0c;直接从缓存中获取。不存在缓存中时&#xff0c;先从数据库中查出对应的信息&#xff0c;写入缓存后再放回数据。 //获取视频详细信息RequestMapping("/getVideoInfo&q…...

The Rust Programming Language 学习 (二)

通用编程概念 变量和可变性 默认情况下变量是不可变的&#xff08;immutable&#xff09;,不过你也可以选择让变量是可变的&#xff08;mutable&#xff09;. 变量的遮蔽 你可以声明和前面变量具有相同名称的新变量,说这个是第一个变量被第二个变量遮蔽&#xff08;shadow&…...

http链接转成https的链接的几种方法

以下是一个将HTTP链接转换为HTTPS的JavaScript函数&#xff0c;处理了多种常见输入情况&#xff1a; function convertToHttps(url) {if (typeof url ! string) return url;// 移除首尾空格并处理空字符串const trimmedUrl url.trim();if (!trimmedUrl) return https://;// 替…...

STM32——串口通信 UART

一、基础配置 Universal Asynchronous Receiver Transmitter 异步&#xff0c;串行&#xff0c;全双工 TTL电平 &#xff1a;高电平1 低电平0 帧格式&#xff1a; 起始位1bit 数据位8bit 校验位1bit 终止位1bit NVIC Settings一栏使能接受中断。 之前有设置LCD&#xff0c;…...

mybatis日期格式与字符串不匹配bug

异常特征&#xff1a;java.lang.IllegalArgumentException: invalid comparison: java.time.LocalDateTime and java.lang.String ### Error updating database. Cause: java.lang.IllegalArgumentException: invalid comparison: java.time.LocalDateTime and java.lang.Str…...

文献分享: ConstBERT固定数目向量编码文档

&#x1f602;图放这了&#xff0c;大道至简的 idea \text{idea} idea不愧是 ECIR \text{ECIR} ECIR &#x1f449;原论文 1. ConstBERT \textbf{1. ConstBERT} 1. ConstBERT的原理 1️⃣模型的改进点&#xff1a;相较于 ColBERT \text{ColBERT} ColBERT为每个 Token \text{Tok…...

学习记录-用例设计编写

黑马测试视频记录 目录 一、 软件测试流程 二、测试用例编写格式 1、等价类法 2、边界值分析法 3、 判定表法 4、场景法​编辑 5、错误推荐法 一、 软件测试流程 二、测试用例编写格式 1、等价类法 2、边界值分析法 3、 判定表法 4、场景法 5、错误推荐法 时间紧任务重…...

学习工具的一天之(burp)

第一呢一定是先下载 【Java环境】&#xff1a;Java Downloads | Oracle 下来是burp的下载 Download Burp Suite Community Edition - PortSwigger 【下载方法二】关注的一个博主 【BurpSuite 安装激活使用详细上手教程 web安全测试工具】https://www.bilibili.com/video/BV…...

el-tree右键节点动态位置展示菜单;el-tree的节点图片动态根据节点属性color改变背景色;加遮罩层(opacity)

一、el-tree右键节点动态位置展示菜单 关键:@node-contextmenu="handleRightClick"与@node-click=“handleNodeClick” <div class="content"><el-tabs class="tabs" @tab-click="handleClick" v-model="Modal"…...

K8s 1.27.1 实战系列(一)准备工作

一、主机规划与硬件要求 1、节点数量 至少需要 3 台服务器(1 台 Master 节点,2 台 Worker 节点)。本地测试可缩容:若仅用于测试,可缩减为 1 个 Master 和 1 个 Worker,但需注意稳定性风险。2、硬件配置 ​Master 节点:建议 2 核 CPU、8GB 内存、80GB 硬盘。​Worker 节…...

说一下SpringBoot3新特新和JDK17新特性

JDK1.8&#xff08;Java8&#xff09;新特性 stream流式编程 流处理 Stream API 提供了对集合数据进行操作的一种高效、简洁的方式。它支持顺序和并行的聚合操作 如&#xff1a;过滤&#xff08;filter&#xff09;、排序&#xff08;sort&#xff09;、映射&#xff08;map&…...

Linux系统服务安全检测手记

一&#xff1a;服务器ip暴露ip和端口的安全问题 服务器IP和端口暴露在外网中确实存在一定的安全风险&#xff0c;以下是几个主要的安全问题及相应的缓解措施&#xff1a; ### 主要安全问题 1. **直接攻击**&#xff1a; - 暴露的IP地址和开放的端口可能成为黑客直接攻击的…...

鸿蒙与DeepSeek深度整合:构建下一代智能操作系统生态

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 https://www.captainbed.cn/north 目录 技术融合背景与价值鸿蒙分布式架构解析DeepSeek技术体系剖析核心整合架构设计智能调度系统实现…...

[创业之路-329]:华为铁三角实施的步骤

一、通用过程 华为铁三角实施的步骤主要包括以下几个关键阶段&#xff1a; 1、明确角色与职责 确定铁三角成员&#xff1a;组建由客户经理&#xff08;AR&#xff09;、解决方案经理&#xff08;SR&#xff09;和交付经理&#xff08;FR&#xff09;组成的铁三角团队。制定岗…...

1.15-16-17-18迭代器与生成器,函数,数据结构,模块

目录 15&#xff0c;Python3 迭代器与生成器15-1 迭代器15-1-1 基础知识15-1-2 迭代器与for循环工作原理 15-2 生成器&#xff08;本质就是迭代器&#xff09;15-2-1 yield 表达式15-2-2 三元表达式15-2-3 列表生成式15-2-4 其他生成器&#xff08;——没有元祖生成式——&…...

java面向对象(详细讲解)

第一章 类和对象 1.面向对象的介绍 1.面向过程&#xff1a;自己的事情自己做&#xff0c;代表语言c语言 2.面向对象&#xff1a;自己的事情别人做&#xff0c;代表语言java 3.为啥要使用面向对象思想编程&#xff1a;很多功能别人给我们实现好了&#xff0c;我们只需要拿过…...

代码随想录二刷|图论2

图论 基础知识 1 无向图 &#xff08;1&#xff09;度&#xff1a;一个顶点连n条边就度为n &#xff08;2&#xff09;权 加权无向图&#xff1a;有边长的无向图 &#xff08;3&#xff09;通道&#xff1a;两个顶点之间有一些边和点&#xff0c;并且没有重复的边 路&am…...

毕业项目推荐:基于yolov8/yolov5/yolo11的暴力行为检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…...

服务器CPU微架构

1、微架构图 前端&#xff1a;预解码、解码、分支预测、L1指令缓存、指令TLB缓存 后端&#xff1a;顺序重排缓存器ROB处理依赖&#xff0c;调度器送到执行引擎 执行引擎&#xff1a;8路超标量&#xff0c;每一路可以进行独立的微操作处理 Port0、1、5、6支持整数、浮点数的加…...

用本地浏览器打开服务器上使用的Tensorboard

文章目录 前言一、Tensorboard的安装二、使用步骤1.服务器上的设置2.在本地打开 总结 前言 最近有使用服务器上的Tensorboard的需求&#xff0c;踩了几个雷&#xff0c;现已在搜索和帮助下解决&#xff0c;总结于此。 一、Tensorboard的安装 pip install tensorboard2.12.0注…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...