异步化
概念对比
- 同步:做完一件事后再去做另外一件事
- 异步:不用等一件事做完,就可以做另外一件事,比如将热水器打开烧水,设置100度会发出通知提醒,然后去做另外一件事,烧水时人可以同时处理别的事,水烧好后,人能收到通知,就知道水烧好了。
业务流程分析
标准异步化的业务流程
-
当用户要进行耗时很长的操作时,用户点击分析后,不需要在界面傻等,而是应该将这个任务保存到数据库中记录下来
-
用户要执行新任务时:
- 提交任务成功:
- 如果我们的程序还有多余的空闲线程,可以立即去做这个任务
- 如果我们的程序都在忙,那就放到等待队列中
- 提交任务失败:任务队列满了
- 拒绝这个任务,再也不执行
- 通过保存到数据库中的记录来看到提交失败的任务,并且在程序闲的时候,可以把任务从数据库中取到程序中,再去执行
- 提交任务成功:
-
我们的程序(线程)从任务队列中取出任务依次执行,完成每一件事要修改一下任务的状态
-
用户可以查询任务的状态,或者在任务执行成功或者失败时能得到通知
-
如果要执行的任务很复杂,包含很多环节,每一个小任务完成时,要在程序(数据库)中记录一下任务的执行状态,进度
系统的业务流程
- 用户点击分析时按钮时,先把图表立刻保存到数据库中(作为一个任务)
- 用户可以在图表管理页面查看所有图表(已生成的、生成中的、生成失败)的信息和状态
- 用户可以修改生成失败的图表信息,点击重新生成
线程池
线程池的实现
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)
参数说明
- corePoolSize(核心线程数=>相当于正式员工):正常情况下,我们的系统应该能同时工作的线程数(随时就绪状态)
- maximumPoolSize(最大线程数=>哪怕任务再多,也只招这些人):极限情况下,我们的线程池最多有多少个线程
- keepAliveTime(空闲线程存活时间):超过这个时间,多余的线程就会自动结束工作,从而释放无用的线程资源
- TimeUnit unit(空闲线程存活时间的单位):秒、分
- workQueue(工作队列):用于存放给线程执行的任务,存在一个队列的长度(一定要设置一个长度,不要说队列长度无限,因为也会占用资源)
- threadFactory(线程工厂):控制每个线程的生成、线程的属性(比如线程名)
- rejectedExecutionHandler(拒绝策略):当任务队列满时,我们采取什么措施,比如抛异常、不抛异常、自定义策略
资源隔离策略
- 比如重要的任务一个队列,非重要的任务一个队列,保证这两个队列互不干扰(VIP任务、普通任务)
线程池参数设置
当前AI的API处理能力为4QPS,每个任务处理时间为10秒,那么线程池的参数设置如下:
| 参数 | 值 | 说明 |
|---|---|---|
| corePoolSize | 4 | 正常情况下可以设置为4 |
| maximumPoolSize | ≤4 | 考虑极限情况,不超过4 |
| keepAliveTime | 秒级或分钟级 | 多余线程的存活时间 |
| TimeUnit | TimeUnit.SECONDS | 时间单位设置为秒 |
| workQueue | 20 | 结合实际情况设置队列长度 |
| threadFactory | 自定义 | 控制线程的生成和属性(如线程名) |
| rejectedExecutionHandler | 自定义策略 | 抛异常并标记数据库任务状态为"任务满了,已拒绝" |
一般情况下,任务分为IO密集型和计算密集型两种。
- IO密集型:吃带宽、硬盘、内存的读写资源,corePoolSize可以设置大一点,一般经验值是2n左右,建议以IO的能力为主
- 计算密集型:吃CPU资源,比如音视频处理、图像处理、数学计算等,corePoolSize为CPU核数+1(空余线程),可以让每个线程都能利用好CPU的每个核,而且线程之间不用频繁切换(减少打架、开销)
- 混合型:既吃CPU又吃IO,比如爬虫、日志处理、数据库查询、网络请求等,可以设置corePoolSize为CPU核数,然后设置workQueue为有界队列,防止任务过多导致OOM
实现工作流程
- 给chart表中增加任务状态字段(比如排队中、执行中、已完成、失败),任务执行信息字段(用于记录任务执行中、或者执行失败时,保存错误信息)
- 用户可以在图表管理页面查看所有图表(已生成的、生成中的、生成失败)的信息和状态
- 任务:先修改图表任务状态为“执行中”,等执行成功后修改为“已完成”、保存执行结果,执行失败时修改为“失败”,记录任务失败信息
- 用户可以在图表管理页面查看所有图表(已生成、生成中、生成失败)的信息和状态
库表设计
status varchar(128) not null default 'wait' comment 'wait,running,succeed,failed',
add execMessage text null comment '执行信息';
进一步优化
- 增加重试功能(使用guava的Retrying重试)
- 考虑到AI生成脏数据的情况,在后端进行异常处理
- 如果任务没进入队列中(或者任务队列满了),可以用定时任务把他放到队列中(补偿)
- 给任务增加超时时间,超时自动设置为失败
- 反向压力:通过第三方调用的服务状态来选择系统当前的策略,比如根据AI服务的当前任务队列数来控制自己系统的核心线程数,从而最大化利用资源
- 我的图表页面增加一个刷新、定时自动刷新的按钮,保证获取到图表的最新状态(前端轮询)
- 任务执行成功或者失败,给用户发送消息通知(实时:websocket、server sid event)
744

被折叠的 条评论
为什么被折叠?



