【Easylive】视频删除方法详解:重点分析异步线程池使用

【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版

方法整体功能

这个deleteVideo方法是一个综合性的视频删除操作,主要完成以下功能:

  1. 权限验证:检查视频是否存在及用户是否有权限删除
  2. 核心数据删除:删除视频主信息、投稿信息
  3. 经济系统调整:扣除用户发布视频获得的硬币
  4. 搜索索引清理:从Elasticsearch中移除文档
  5. 异步清理关联数据:使用线程池异步删除分P视频、弹幕、评论等关联数据及物理文件

重点:异步线程池部分详解

1. 线程池初始化

private static ExecutorService executorService = Executors.newFixedThreadPool(10);

线程池类型:固定大小线程池(10个线程)
特点
• 池中线程数量固定不变
• 适合已知并发量的稳定负载场景
• 超出线程数的任务会在队列中等待
潜在问题
• 使用无界队列(默认LinkedBlockingQueue),可能导致OOM
• 静态变量生命周期与应用一致,可能造成线程泄漏

2. 异步任务执行逻辑

executorService.execute(() -> {// 异步任务代码块
});

任务封装:使用Lambda表达式封装Runnable任务
执行方式execute()方法提交任务到线程池
与事务的关系
• 异步任务在新线程中执行
不受主方法@Transactional注解影响,形成独立的事务上下文
• 若异步操作需要事务,需在任务内部添加事务注解

3. 异步任务具体操作

(1) 查询和删除分P视频
VideoInfoFileQuery videoInfoFileQuery = new VideoInfoFileQuery();
videoInfoFileQuery.setVideoId(videoId);
List<VideoInfoFile> videoInfoFileList = this.videoInfoFileMapper.selectList(videoInfoFileQuery);
videoInfoFileMapper.deleteByParam(videoInfoFileQuery);

操作顺序:先查询后删除
目的:获取文件路径用于后续物理删除

(2) 删除关联投稿信息
VideoInfoFilePostQuery videoInfoFilePostQuery = new VideoInfoFilePostQuery();
videoInfoFilePostQuery.setVideoId(videoId);
videoInfoFilePostMapper.deleteByParam(videoInfoFilePostQuery);

直接删除:无需查询,根据videoId直接删除

(3) 删除弹幕数据
VideoDanmuQuery videoDanmuQuery = new VideoDanmuQuery();
videoDanmuQuery.setVideoId(videoId);
videoDanmuMapper.deleteByParam(videoDanmuQuery);

批量删除:通过videoId一次性删除所有关联弹幕

(4) 删除评论数据
VideoCommentQuery videoCommentQuery = new VideoCommentQuery();
videoCommentQuery.setVideoId(videoId);
videoCommentMapper.deleteByParam(videoCommentQuery);

级联删除:通常需要确保评论的关联数据(回复、点赞等)也被清理

(5) 物理文件删除
for (VideoInfoFile item : videoInfoFileList) {try {FileUtils.deleteDirectory(new File(appConfig.getProjectFolder() + item.getFilePath()));} catch (IOException e) {log.error("删除文件失败,文件路径:{}", item.getFilePath());}
}

关键点
• 使用deleteDirectory删除整个目录
• 捕获并记录IO异常,避免任务中断
• 文件路径拼接了项目基础目录(appConfig.getProjectFolder())

4. 异步设计的优缺点分析

优点
  1. 响应速度:主线程快速返回,用户体验好
  2. 资源隔离:IO密集型操作不影响核心业务
  3. 错误隔离:文件删除失败不影响主流程
缺点及风险
  1. 事务不一致

    // 主事务提交后异步任务才执行
    // 若异步任务失败,系统处于不一致状态
    
  2. 错误处理缺失

    // 当前实现没有记录任务执行结果
    // 无法知道异步操作是否成功
    
  3. 资源竞争

    // 固定10个线程可能在高并发时成为瓶颈
    // 文件删除操作可能阻塞其他异步任务
    

5. 改进建议

(1) 增强型线程池配置
private static ExecutorService executorService = new ThreadPoolExecutor(5, // 核心线程数20, // 最大线程数60, TimeUnit.SECONDS, // 空闲线程存活时间new ArrayBlockingQueue<>(1000), // 有界队列new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
(2) 添加任务结果处理
Future<?> future = executorService.submit(() -> {// 任务代码
});// 可选:通过Future跟踪任务状态
future.get(10, TimeUnit.SECONDS); // 带超时的等待
(3) 事务补偿机制
@TransactionalEventListener(phase = AFTER_COMMIT)
public void handleAfterCommit(VideoDeleteEvent event) {// 主事务提交后执行异步清理asyncCleanService.cleanVideoResources(event.getVideoId());
}
(4) 完善日志监控
executorService.execute(() -> {MDC.put("traceId", UUID.randomUUID().toString());try {// 任务代码log.info("视频资源清理完成: {}", videoId);} catch (Exception e) {log.error("视频资源清理失败: {}", videoId, e);// 发送告警或记录失败状态} finally {MDC.clear();}
});

总结

这个删除方法通过线程池实现了:

  1. 核心数据同步删除:保证关键数据立即清除
  2. 资源异步清理:提升响应速度
  3. 物理文件删除:释放存储空间

关键改进方向
• 线程池参数优化
• 完善错误处理和状态跟踪
• 考虑引入事务事件机制
• 增加监控和告警能力

这种设计适合对实时性要求高但允许最终一致性的场景,是典型的"快速响应+后台清理"架构模式。

异步线程池及executorService.execute详解

一、异步线程池基础

1. 线程池核心概念

线程池是一种线程管理机制,它维护着多个线程,避免频繁创建和销毁线程带来的性能开销。在Java中,主要通过ExecutorService接口及其实现类来使用线程池。

2. 线程池关键参数

参数说明示例值
corePoolSize核心线程数10
maximumPoolSize最大线程数50
keepAliveTime空闲线程存活时间60秒
workQueue任务队列new LinkedBlockingQueue(1000)
threadFactory线程创建工厂Executors.defaultThreadFactory()
handler拒绝策略AbortPolicy

3. 线程池工作流程

  1. 提交任务时,优先使用核心线程处理
  2. 核心线程全忙时,任务进入队列
  3. 队列满时,创建新线程(不超过maxPoolSize)
  4. 线程数达最大值且队列满时,触发拒绝策略

二、executorService.execute方法详解

1. 方法签名

void execute(Runnable command)

2. 核心特点

异步执行:立即返回,不阻塞调用线程
无返回值:适用于不需要获取结果的场景
异常处理:任务异常会传递给未捕获异常处理器

3. 执行流程

Caller Executor Queue Worker execute(task) 立即执行 放入队列 队列非空时取出执行 alt [有可用核心线程] [无可用核心线程] 任务完成 Caller Executor Queue Worker

4. 在示例代码中的使用

executorService.execute(() -> {// 1. 查询和删除分P视频VideoInfoFileQuery videoInfoFileQuery = new VideoInfoFileQuery();videoInfoFileQuery.setVideoId(videoId);List<VideoInfoFile> videoInfoFileList = this.videoInfoFileMapper.selectList(videoInfoFileQuery);videoInfoFileMapper.deleteByParam(videoInfoFileQuery);// 2. 删除其他关联数据...// 3. 删除物理文件for (VideoInfoFile item : videoInfoFileList) {try {FileUtils.deleteDirectory(new File(appConfig.getProjectFolder() + item.getFilePath()));} catch (IOException e) {log.error("删除文件失败,文件路径:{}", item.getFilePath());}}
});

5. 为什么使用execute而不是submit?

对比项executesubmit
返回值Future对象
异常处理直接抛出封装在Future中
适用场景简单异步任务需要获取结果的任务
示例代码当前场景适合需要结果时使用

在当前场景下:
• 不需要获取清理操作的结果
• 简单的日志记录已足够
• 更轻量级的执行方式

三、线程池配置优化建议

1. 当前实现的潜在问题

private static ExecutorService executorService = Executors.newFixedThreadPool(10);

• 使用无界队列(默认LinkedBlockingQueue),可能导致OOM
• 固定线程数无法应对突发流量
• 缺少合理的拒绝策略

2. 推荐改进方案

private static ExecutorService executorService = new ThreadPoolExecutor(5,                              // 核心线程数20,                             // 最大线程数60, TimeUnit.SECONDS,           // 空闲线程存活时间new ArrayBlockingQueue<>(1000), // 有界队列new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

3. 各参数说明

  1. corePoolSize=5:保持5个常驻线程
  2. maxPoolSize=20:突发流量时可扩展到20线程
  3. keepAliveTime=60s:空闲线程60秒后回收
  4. 有界队列(1000):防止资源耗尽
  5. CallerRunsPolicy:队列满时由调用线程执行任务

四、异常处理机制

1. 当前实现的异常处理

try {FileUtils.deleteDirectory(...);
} catch (IOException e) {log.error("删除文件失败...");
}

• 仅记录日志,无恢复机制
• 异常不会传播到主线程

2. 增强型异常处理方案

方案1:全局异常处理器
executorService = new ThreadPoolExecutor(// ...其他参数new ThreadPoolExecutor.AbortPolicy() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor e) {// 记录被拒绝的任务log.warn("Task rejected: {}", r.toString());super.rejectedExecution(r, e);}}
);// 设置未捕获异常处理器
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {log.error("Uncaught exception in thread: {}", t.getName(), e);
});
方案2:封装任务
public class SafeRunnable implements Runnable {private final Runnable task;public SafeRunnable(Runnable task) {this.task = task;}@Overridepublic void run() {try {task.run();} catch (Exception e) {log.error("Task execution failed", e);// 可添加重试或补偿逻辑}}
}// 使用方式
executorService.execute(new SafeRunnable(() -> {// 任务代码
}));

五、性能监控建议

1. 添加线程池监控

// 定时打印线程池状态
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {ThreadPoolExecutor tpe = (ThreadPoolExecutor) executorService;log.info("Pool stats: active={}, queue={}/{}, completed={}",tpe.getActiveCount(),tpe.getQueue().size(),tpe.getQueue().remainingCapacity(),tpe.getCompletedTaskCount());
}, 1, 1, TimeUnit.MINUTES);

2. 关键监控指标

指标说明健康值参考
activeCount活动线程数< maxPoolSize
queueSize队列大小< queueCapacity * 0.8
completedTaskCount已完成任务持续增长
rejectedCount被拒绝任务= 0

六、实际应用场景分析

1. 当前视频删除场景特点

耗时操作:文件删除可能很慢
非关键路径:不影响主业务流程
允许延迟:最终一致性即可
可能失败:文件可能被占用等

2. 为什么适合使用线程池?

  1. 解耦:将清理操作与主业务分离
  2. 提速:主线程快速返回
  3. 可控:通过线程池限制资源使用
  4. 可扩展:方便添加重试等机制

3. 潜在风险及应对

风险应对措施
线程泄漏使用有界队列,合理配置存活时间
任务丢失添加持久化队列或任务记录
资源竞争监控和动态调整线程池参数
异常传播完善任务级别的异常处理

七、总结最佳实践

  1. 选择合适的线程池类型:根据场景选择fixed/cached/custom
  2. 使用有界队列:防止资源耗尽
  3. 配置合理的拒绝策略:如CallerRunsPolicy
  4. 完善异常处理:任务级别和全局级别
  5. 添加监控:实时了解线程池状态
  6. 考虑任务重要性:关键任务建议使用带返回值的submit

在视频删除场景中,通过线程池异步处理清理任务是一种合理的设计,但需要注意:
• 线程池参数的合理配置
• 异常情况的妥善处理
• 重要操作的日志记录
• 系统资源的监控告警

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/75000.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《比特信使的七重试炼:从数据丢失到CA认证的守护史诗》

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 88万阅读 1.6万收藏 第一章&#xff1a;初现危机——数据丢失的阴云 比特城的清晨总是被数据流的光芒点亮&#xff0c;但这一天&#xff0c;工程师艾琳的实验室却笼罩在阴霾中。她刚刚尝试通过古老的“疾风…

如何更好的理解 beforeEach 全局前置守卫,在处理路由跳转前触发,怎么实现常用的全局权限校验、登录状态检查的呢?

以下将深入讲解 Vue Router 的全局前置守卫 beforeEach 在权限系统中的实现原理和实战应用&#xff0c;结合企业级项目代码进行拆解&#xff08;基于 Vue 3 TypeScript Pinia&#xff09;。 一、前置守卫核心机制 1.1 执行时机与特性 全局前置守卫在路由跳转前触发&#xf…

VMware上的windows虚拟机安装使用Docker方法

因为在实体机上使用Docker会导致VMware无法启动虚拟机&#xff0c;所以尝试了在虚拟机中安装Docker. 1. 创建Windows虚拟机. windows至少是Win10 1.9***或者Win 11. 这是Docker Desktop要求的。 2. 虚拟机CPU要开启虚拟化功能。 虚拟机的CPU开启虚拟化 虚拟机的memory要不小…

项目中集成ECharts图表(通过定时任务SpringTask统计每天的订单金额)

项目应用Echarts ①、前端终端安装Echarts npm install echarts --save ②、src/views创建order目录&#xff0c;在order目录下创建orderStatistics.vue ③、src/router/modules目录下创建order.js&#xff0c;配置路由 const layout ()>import(/layout/index.vue) …

2022第十三届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(题解解析)

记录刷题的过程、感悟、题解。 希望能帮到&#xff0c;那些与我一同前行的&#xff0c;来自远方的朋友&#x1f609; 大纲&#xff1a; 1、九进制转十进制-&#xff08;解析&#xff09;-简单的进制转化问题&#x1f604; 2、顺子日期-&#xff08;解析&#xff09;-考察日期 3…

python应用之使用pdfplumber 解析pdf文件内容

目录标题 一. 通过 pdfplumber.open() 解析复杂PDF&#xff1a;1-2. 报错&#xff1a;V2 &#xff1a; 1-3. v3 使用tk 库&#xff0c;弹框选择文件运行环境准备完整代码保存运行测试步骤方式二&#xff1a;命令行方式&#xff08;适用于自动化&#xff09; 测试用例示例常见问…

力扣热题100刷题day61|234.回文链表(两种方法)

一、回文链表 234.回文链表 两种解法 解法1&#xff1a;时间复杂度O(n) 空间复杂度O(n) 遍历链表&#xff0c;计算链表长度&#xff0c;创建同样长度大小的数组&#xff0c;用数组存储链表中所有元素&#xff0c;之后双指针遍历链表&#xff0c;一个从头开始&#xff0c;一…

vue3+element-plus动态与静态表格数据渲染

一、表格组件&#xff1a; <template> <el-table ref"myTable" :data"tableData" :header-cell-style"headerCellStyle" header-row-class-name"my-table-header" cell-class-name"my-td-cell" :row-style"r…

Kafka 中的生产者分区策略

Kafka 中的 生产者分区策略 是决定消息如何分配到不同分区的机制。这个策略对 Kafka 的性能、负载均衡、消息顺序性等有重要影响。了解它对于高效地使用 Kafka 进行消息生产和消费至关重要。 让我们一起来看 Kafka 中 生产者的分区策略&#xff0c;它如何工作&#xff0c;以及…

《从零搭建Vue3项目实战》(AI辅助搭建Vue3+ElemntPlus后台管理项目)零基础入门系列第二篇:项目创建和初始化

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 《从零搭建Vue3项目实战》&#xff08;AI辅助…

全国产FMC子卡-16bit 8通道2.4G

国产化FMC DA子卡&#xff0c;16bit 8通道2.4GS/s 全国产FMC子卡是一款高分辨率、高采样率的全国产多通道标准双宽DAC FMC子板。其接口电气和结构设计均依据FMC标准(ANSI/VITA 57.1)&#xff0c;通过两个高密度FMC连接器&#xff08;HPC&#xff09;连接至FPGA载板。它提供8路A…

linux-添加开机自启动指定脚本

一、systemd 服务&#xff08;主流方法&#xff09; 适用于使用systemd的现代发行版&#xff08;Ubuntu 16.04/CentOS 7&#xff09; 创建服务文件 sudo nano /etc/systemd/system/your_script.service写入服务配置&#xff08;示例&#xff09;&#xff1a; [Unit] Descri…

Spring MVC 返回 JSON 视图的方式及对比(6种)

Spring MVC 返回 JSON 视图的方式及对比&#xff08;新增 MappingJackson2JsonView&#xff09; 1. 方式一&#xff1a;ResponseBody 注解 作用&#xff1a;直接返回对象&#xff0c;由消息转换器&#xff08;如 Jackson&#xff09;序列化为 JSON。 适用场景&#xff1a;简单…

瑞芯微RK3568嵌入式AI项目实战:智能家居项目(二)

RK3568智能家居项目实战指南&#xff1a;从入门到精通的完整制作流程 瑞芯微RK3568作为一款高性能嵌入式处理器&#xff0c;凭借其四核Cortex-A55架构、1T算力NPU和丰富的外设接口&#xff0c;成为智能家居项目开发的理想平台。下面我将推荐几个典型的RK3568智能家居项目&…

GStreamer开发笔记(一):GStreamer介绍,在windows平台部署安装,打开usb摄像头对比测试

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/147049923 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、O…

Spring Boot 3.4.3 和 Spring Security 6.4.2 实现基于内存和 MySQL 的用户认证

在 Web 应用开发中&#xff0c;用户认证是保障系统安全的基础需求。Spring Boot 3.4.3 结合 Spring Security 6.4.2 提供了强大的安全框架支持&#xff0c;可以轻松实现基于内存或数据库的用户认证功能。本文将详细介绍如何在 Spring Boot 3.4.3 中集成 Spring Security 6.4.2&…

HOW - Axios 拦截器特性

目录 Axios 介绍拦截器特性1. 统一添加 Token&#xff08;请求拦截器&#xff09;2. 处理 401 未授权&#xff08;响应拦截器&#xff09;3. 统一处理错误信息&#xff08;响应拦截器&#xff09;4. 请求 Loading 状态管理5. 自动重试请求&#xff08;如 429 过载&#xff09;6…

JVM核心机制:类加载×字节码引擎×垃圾回收机制

&#x1f680;前言 “为什么你的Spring应用启动慢&#xff1f;为什么GC总是突然卡顿&#xff1f;答案藏在JVM的核心机制里&#xff01; 本文将用全流程图解字节码案例&#xff0c;带你穿透三大核心机制&#xff1a; 类加载&#xff1a;双亲委派如何防止恶意代码入侵&#xff…

coze生成流程图和思维导图工作流

需求&#xff1a;通过coze平台实现生成流程图和思维导图&#xff0c;要求支持文档上传 最终工作流如下&#xff1a; 入参&#xff1a; 整合用户需求文件内容的工作流&#xff1a;https://blog.csdn.net/YXWik/article/details/147040071 选择器分发&#xff0c;不同的类型走…

网络安全应急响应-文件痕迹排查

在Windows系统的网络安全应急响应中&#xff0c;文件痕迹排查是识别攻击行为的关键步骤。以下是针对敏感目录的详细排查指南及扩展建议&#xff1a; 1. 临时目录排查&#xff08;Temp/Tmp&#xff09; 路径示例&#xff1a; C:\Windows\TempC:\Users\<用户名>\AppData\L…