👉 这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:
《项目实战(视频)》:从书中学,往事上“练”
《互联网高频面试题》:面朝简历学习,春暖花开
《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题
《精进 Java 学习指南》:系统学习,互联网主流技术栈
《必读 Java 源码专栏》:知其然,知其所以然
👉这是一个或许对你有用的开源项目
国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。
功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:
Boot 地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
Cloud 地址:https://gitee.com/zhijiantianya/yudao-cloud
视频教程:https://doc.iocoder.cn
来源:juejin.cn/post/
7266087843239084090
1 前言
良好的系统设计必须要做到开闭原则,随着业务的不断迭代更新,核心代码也会被不断改动,出错的概率也会大大增加。但是大部分增加的功能都是在扩展原有的功能,既要保证性能又要保证质量,我们往往都会使用异步线程池来处理,然而却增加了很多不确定性因素。
由此我设计了一套通用的异步处理SDK,可以很轻松的实现各种异步处理
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
2 目的
通过异步处理不仅能够保证方法能够得到有效的执行而且不影响主流程
更重要的是各种兜底方法保证数据不丢失,从而达到最终一致性
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
3 优点
无侵入设计,独立数据库,独立定时任务,独立消息队列,独立人工执行界面(统一登录认证)
使用spring事务事件机制,即使异步策略解析失败也不会影响业务
如果你的方法正在运行事务,会等事务提交后或回滚后再处理事件
就算事务提交了,异步策略解析失败了,我们还有兜底方案执行(除非数据库有问题,消息队列有问题,方法有bug)
4 原理
容器初始化bean完成后遍历所有方法,把有@AsyncExec注解的方法缓存起来
方法运行时通过AOP切面发布事件
事务事件监听处理异步执行策略
@TransactionalEventListener(fallbackExecution = true, phase = TransactionPhase.AFTER_COMPLETION)
fallbackExecution=true
没有事务正在运行,依然处理事件TransactionPhase.AFTER_COMPLETION
事务提交后和事务回滚后都处理事件
5 组件
kafka 消息队列
xxl job 定时任务
mysql 数据库
spring 切面
vue 界面
6 设计模式
策略
模板方法
动态代理
7 流程图
![2150096fe31771e7f764de2235fefd2b.png](https://img-blog.csdnimg.cn/img_convert/2150096fe31771e7f764de2235fefd2b.png)
8 数据库脚本
CREATE TABLE `async_req` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`application_name` varchar(100) NOT NULL DEFAULT '' COMMENT '应用名称',
`sign` varchar(50) NOT NULL DEFAULT '' COMMENT '方法签名',
`class_name` varchar(200) NOT NULL DEFAULT '' COMMENT '全路径类名称',
`method_name` varchar(100) NOT NULL DEFAULT '' COMMENT '方法名称',
`async_type` varchar(50) NOT NULL DEFAULT '' COMMENT '异步策略类型',
`exec_status` tinyint NOT NULL DEFAULT '0' COMMENT '执行状态 0:初始化 1:执行失败 2:执行成功',
`exec_count` int NOT NULL DEFAULT '0' COMMENT '执行次数',
`param_json` longtext COMMENT '请求参数',
`remark` varchar(200) NOT NULL DEFAULT '' COMMENT '业务描述',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_applocation_name` (`application_name`) USING BTREE,
KEY `idx_exec_status` (`exec_status`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='异步处理请求';
CREATE TABLE `async_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`async_id` bigint NOT NULL DEFAULT '0' COMMENT '异步请求ID',
`error_data` longtext COMMENT '执行错误信息',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_async_id` (`async_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='异步处理日志';
9 异步策略
![c779442ecd762591baf72714fedd3e90.png](https://img-blog.csdnimg.cn/img_convert/c779442ecd762591baf72714fedd3e90.png)
10 安全级别
![8520e72e0ca3ddb4e136777134308e42.png](https://img-blog.csdnimg.cn/img_convert/8520e72e0ca3ddb4e136777134308e42.png)
11 执行状态
![8a723416e7d2435ed4891e0c90f8cf3c.png](https://img-blog.csdnimg.cn/img_convert/8a723416e7d2435ed4891e0c90f8cf3c.png)
12 流程图
![6329f37bfcd810b2c72c00d38b630e5d.png](https://img-blog.csdnimg.cn/img_convert/6329f37bfcd810b2c72c00d38b630e5d.png)
![b85965c2423ae5a33e9113a630f14fbf.png](https://img-blog.csdnimg.cn/img_convert/b85965c2423ae5a33e9113a630f14fbf.png)
![a1bc95782e8f5bfc8dcc330c9e7519e9.png](https://img-blog.csdnimg.cn/img_convert/a1bc95782e8f5bfc8dcc330c9e7519e9.png)
13 apollo 配置
# 开关:默认关闭
async.enabled=true
# 应用名称
spring.application.name=xxx
# 数据源 druid
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/fc_async?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true
spring.datasource.username=user
spring.datasource.password=xxxx
spring.datasource.filters=config
spring.datasource.connectionProperties=config.decrypt=true;config.decrypt.key=yyy
#静态地址
spring.resources.add-mappings=true
spring.resources.static-locations=classpath:/static/
# 以下配置都有默认值
# 核心线程数
async.executor.thread.corePoolSize=10
# 最大线程数
async.executor.thread.maxPoolSize=50
# 队列容量
async.executor.thread.queueCapacity=10000
# 活跃时间
async.executor.thread.keepAliveSeconds=600
# 执行成功是否删除记录:默认删除
async.exec.deleted=true
# 自定义队列名称前缀:默认应用名称
async.topic=${spring.application.name}
# 重试执行次数:默认5次
async.exec.count=5
# 重试最大查询数量
async.retry.limit=100
# 补偿最大查询数量
async.comp.limit=100
# 登录拦截:默认false
async.login=false
14 用法
1,异步开关
scm.async.enabled=true
2,在需要异步执行的方法加注解 (必须是spring代理方法)
@AsyncExec(type = AsyncExecEnum.SAVE_ASYNC, remark = "数据字典")
3,人工处理地址
http://localhost:8004/async/index.html
15 注意
1,应用名称
spring.application.name
2,队列名称
${async.topic:${spring.application.name}}_async_queue
自定义topic:async.topic=xxx
3,自己业务要做幂等
4,一个应用公用一个队列
自产自消
5,定时任务
异步重试定时任务(2分钟重试一次,可配置重试次数)
异步补偿定时任务(一小时补偿一次,创建时间在一小时之前的)
16 效果展示
![971022e90af4a32c18fcaf9d9180a47a.png](https://img-blog.csdnimg.cn/img_convert/971022e90af4a32c18fcaf9d9180a47a.png)
17 github地址
https://github.com/xiongyanokok/fc-async.git
欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,“长按”或“扫描”下方二维码噢:
星球的内容包括:项目实战、面试招聘、源码解析、学习路线。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)