一次线上事故,我顿悟了异步的精髓
点击上方“芋道源码”,选择“设为星标”
管她前浪,还是后浪?
能浪的浪,才是好浪!
每天 10:33 更新文章,每天掉亿点点头发...
源码精品专栏
原创 | Java 2021 超神之路,很肝~
中文详细注释的开源项目
RPC 框架 Dubbo 源码解析
网络应用框架 Netty 源码解析
消息中间件 RocketMQ 源码解析
数据库中间件 Sharding-JDBC 和 MyCAT 源码解析
作业调度中间件 Elastic-Job 源码解析
分布式事务中间件 TCC-Transaction 源码解析
Eureka 和 Hystrix 源码解析
Java 并发源码
来源:勇哥java实战分享
前言
1 业务场景
2 线程池模式
3 本地内存 + 定时任务
4 MQ 模式
5 Agent 服务 + MQ 模式
6 总结
前言
在高并发的场景下,异步 是一个极其重要的优化方向。
前段时间,生产环境发生一次事故,笔者认为事故的场景非常具备典型性 。
写这篇文章,笔者想和大家深入探讨该场景的架构优化方案。希望大家读完之后,可以对异步 有更深刻的理解。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
1 业务场景
老师登录教研平台,会看到课程列表,点击课程后,课程会以视频的形式展现出来。
访问课程详情页面,包含两个核心动作:
读取课程视频信息 :
从缓存服务器 Redis 获取课程的视频信息 ,返回给前端,前端通过视频组件渲染。
写入课程观看行为记录 :
当教师观看视频的过程中,浏览器每隔3秒发起请求,教研服务将观看行为记录插入到数据库表中。而且随着用户在线人数越多,写操作的频率也会指数级增长。
上线初期,这种设计运行还算良好,但随着在线用户的增多,系统响应越来越慢,大量线程阻塞在写入视频观看进度表上的 Dao 方法。上。
首先我们会想到一个非常直观的方案,提升写入数据库的能力 。
优化 SQL 语句;
提升 MySQL 数据库硬件配置 ;
分库分表。
这种方案其实也可以满足我们的需求,但是通过扩容硬件并不便宜,另外写操作可以允许适当延迟和丢失少量数据,那这种方案更显得性价比不足。
那么架构优化的方向应该是:“减少写动作的耗时,提升写动作的并发度” , 只有这样才能让系统更顺畅的运行。
于是,我们想到了第二种方案:写请求异步化 。
线程池模式
本地内存 + 定时任务
MQ 模式
Agent 服务 + MQ 模式
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
2 线程池模式
2014年,笔者在艺龙旅行网负责红包系统相关工作。运营系统会调用红包系统给特定用户发送红包,当这些用户登录 app 后,app 端会调用红包系统的激活红包接口 。
激活红包接口是一个写操作,速度也比较快(20毫秒左右),接口的日请求量在2000万左右。
应用访问高峰期,红包系统会变得不稳定,激活接口经常超时,笔者为了快速解决问题,采取了一个非常粗糙的方案:
"控制器收到请求后,将写操作放入到独立的线程池中后,立即返回给前端,而线程池会异步执行激活红包方法 "。
坦率的讲,这是一个非常有效的方案,优化后,红包系统非常稳定。
回到教研的场景,见下图,我们也可以设计类似线程池模型的方案:
使用线程池模式,需要注意如下几点:
1、线程数不宜过高,避免占用过多的数据库连接 ;
2、需要考虑评估线程池队列的大小,以免出现内存溢出的问题。
3 本地内存 + 定时任务
开源中国统计浏览数的方案非常经典。
用户访问过一次文章、新闻、代码详情页面,访问次数字段加 1 , 在 oschina 上这个操作是异步的,访问的时候只是将数据在内存中保存,每隔固定时间将这些数据写入数据库。
示例代码如下:
我们可以借鉴开源中国的方案 :
控制器接收请求后,观看进度信息存储到本地内存 LinkedBlockingQueue 对象里;
异步线程每隔1分钟从队列里获取数据 ,组装成 List 对象,最后调用 Jdbc batchUpdate 方法批量写入数据库;
批量写入主要是为了提升系统的整体吞吐量,每次批量写入的 List 大小也不宜过大 。
这种方案优点是:不改动原有业务架构,简单易用,性能也高。该方案同样需要考虑内存溢出的风险。
4 MQ 模式
很多同学们会想到 MQ 模式 ,消息队列最核心的功能是异步 和解耦 ,MQ 模式架构清晰,易于扩展。
核心流程如下:
控制器接收写请求,将观看视频行为记录转换成消息 ;
教研服务发送消息到 MQ ,将写操作成功信息返回给前端 ;
消费者服务从 MQ 中获取消息 ,批量操作数据库 。
这种方案优点是:
MQ 本身支持高可用和异步,发送消息效率高 , 也支持批量消费;
消息在 MQ 服务端会持久化,可靠性要比保存在本地内存高;
不过 MQ 模式需要引入新的组件,增加额外的复杂度。
5 Agent 服务 + MQ 模式
互联网大厂还有一种常见的异步 的方案:Agent 服务 + MQ 模式。
教研服务器上部署 Agent 服务(独立的进程) , 教研服务接收写请求后,将请求按照固定的格式(比如 JSON )写入到本次磁盘中,然后给前端返回成功信息。
Agent 服务会监听文件变动,将文件内容发送到消息队列 , 消费者服务获取观看行为记录,将其存储到 MySQL 数据库中。
还有一种演进,假设我们不想在应用中依赖消息队列,不生成本地文件,可以采用如下的方式:
这种方案最大的优点是:架构分层清晰,业务服务不需要引入 MQ 组件。
笔者原来接触过的性能监控平台,或者日志分析平台都使用这种模式。
6 总结
学习需要一层一层递进的思考。
第一层:什么场景下需要异步
大量写操作占用了过多的资源,影响了系统的正常运行;
写操作异步后,不影响主流程,允许适当延迟;
第二层:异步的外功心法
本文提到了四种异步方式:
线程池模式
本地内存 + 定时任务
MQ 模式
Agent 服务 + MQ 模式
它们的共同特点是:将写操作命令存储在一个池子后,立刻响应给前端,减少写动作的耗时。任务服务异步从池子里获取任务后执行。
第三层:异步的本质
在笔者看来,异步是更细粒度的使用系统资源的一种方式 。
在教研课程详情场景里,数据库的资源是固定的,但写操作占据大量数据库资源,导致整个系统的阻塞,但写操作并不是最核心的业务流程,它不应该占用那么多的系统资源。
我们使用异步的解决方案时,无论是使用线程池,还是本地内存 + 定时任务 ,亦或是 MQ ,对数据库资源的使用都需要在合理的范围内,只有这样系统才能顺畅的运行。
欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:
已在知识星球更新源码解析如下:
最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。
提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
一次线上事故,我顿悟了异步的精髓相关推荐
- “���”引发的线上事故
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 最近遇到了一起依赖升级 + 异常数据引发的线上事故,教训惨痛,本文 ...
- RPC的超时设置,一不小心就是线上事故
来自:IT人的职场进阶 上面这张监控图,对于服务端的研发同学来说再熟悉不过了.在日常的系统维护中,『服务超时』应该属于监控报警最多的一类问题. 尤其在微服务架构下,一次请求可能要经过一条很长的链路,跨 ...
- 醉了,RPC 超时设置也能引起线上事故!
上面这张监控图,对于服务端的研发同学来说再熟悉不过了.在日常的系统维护中,『服务超时』应该属于监控报警最多的一类问题. 尤其在微服务架构下,一次请求可能要经过一条很长的链路,跨多个服务调用后才能返回结 ...
- 同时设置超时时间_刚入职的小菜鸡,设错了RPC超时,搞了个线上事故
上面这张监控图,对于服务端的研发同学来说再熟悉不过了.在日常的系统维护中,『服务超时』应该属于监控报警最多的一类问题. 尤其在微服务架构下,一次请求可能要经过一条很长的链路,跨多个服务调用后才能返回结 ...
- 程序员惊魂 12 小时:“���”引发线上事故
作者 | 饶全成 来源 | 码农桃花源(ID:CoderPark) 最近遇到了一起依赖升级 + 异常数据引发的线上事故,教训惨痛,本文对此进行回故和总结. 背景 起因是我们使用的服务框架版本比较老,G ...
- RPC 的超时设置,一不小心就是线上事故!
作者 | 骆俊武 来源 | IT人的职场进阶(ID:BestITer) 上面这张监控图,对于服务端的研发同学来说再熟悉不过了.在日常的系统维护中,『服务超时』应该属于监控报警最多的一类问题. 尤其在微 ...
- 线上事故的善后——事故通告
在我们的日常工作中,出现线上事故是难免的,而作为项目最后一环的测试,往往首当其冲:"测试怎么没测出来?"是人们首先想到的.于是出于这种先入为主的想法,测试往往会承担过量的责任.而作 ...
- 【Linux服务器开发系列】一场redis线上事故引发的思考丨redis持久化 rdb和aof丨redis主从复制
一场redis线上事故引发的思考 1. 事故背景介绍 2. redis持久化 rdb和aof 3. redis主从复制 4. 解决方案详解 [Linux服务器开发系列]一场redis线上事故引发的思考 ...
- 线上事故应该由谁来承担?
前不久线上发生了一个事故,主线是这样的,XX 平台对接了 web 端和手机终端,一个伸手不见五指的夜晚,web 端出现了问题,SRE 发现故障后迅速发起了 oncall 机制,建立了作战室.一个小时后 ...
- 线程池运用不当的一次线上事故
来自:IT人的职场进阶 在高并发.异步化等场景,线程池的运用可以说无处不在.线程池从本质上来讲,即通过空间换取时间,因为线程的创建和销毁都是要消耗资源和时间的,对于大量使用线程的场景,使用池化管理可以 ...
最新文章
- 动软代码生成器教程——懒人有福了
- 视频直播:Windows中各类画面源的截取和合成方法总结
- 编译bluez-utils-3.36,死活找不到bluez D-bus的解决方法
- 根据另外一个表来更新,增加字段
- Java—File类详解及实践
- openstack 云_使用OpenStack打造云事业
- 145元!苹果上架一块儿“天价抹布” ,你会买吗?
- 重学JavaScript系列之一_引用类型
- Dubbo服务暴露(导出)流程
- PPP协议基础与工作流程
- 从360和QQ打架看客户端的高精尖武器技术发展:自己留着,防止忘记!
- idea 之Java文件图标为红色解决办法
- easyphp mysql_EasyPHP 数据库空密码
- 中国移动H1S-3光猫破解路由器桥接教程
- tolower()函数用法
- UPDATE或者DELETE忘加WHERE条件的恢复
- graphpad画生存曲线怎么样去掉删失点_手把手教你用graphpadprism绘制生存曲线
- 满江红--大宋提刑官
- 实现对 2:3 或者3:2的图片进行1:1裁剪
- 云呐|加强实验室固定资产设备在线信息化管理