# asyncmdDemo **Repository Path**: wangwendi/asyncmdDemo ## Basic Information - **Project Name**: asyncmdDemo - **Description**: 异步命令组件demo - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2019-07-29 - **Last Updated**: 2024-11-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # asyncmdDemo 异步命令组件demo 需要根据sql文件夹里的sql语句创建表 然后修改jdbc.properties里的数据库url和用户名密码 组件相关配置都在 spring-service.xml文件中 三个测试功能 - SmsController 接收发送短信请求异步发送 - GiveRewardController 接收赠送奖励请求 异步赠送奖励 - CouponController 定时赠送指定用户一批优惠券 - OrderCreateListener和OrderModifyListener实现mq的顺序消费 保证同一笔订单肯定先处理订单创建消息 再处理订单修改消息 com.asyncmdDemo.asyn.asynbiz 存放异步命令业务对象 com.asyncmdDemo.asyn.asyncmd 存放异步命令对象 com.asyncmdDemo.asyn.asynexecuter 存放异步命令执行器 com.asyncmdDemo.asyn.callback 执行器执行异常回调 SmsAsynCmd异步命令对象里设置里一些异步命令个性化的设置 #### 使用逻辑 新建一个异步命令对象 一个异步命令执行器 把请求传过来的数据保存到这个异步命令对象中 调用组件到一个服务 就会把对象保存到表中 并且异步的执行创建的异步命令执行器 会把异步命令对象作为入参传进去 使用者只要在异步命令执行器中把后续逻辑写好就可以 重试之类的机制组件会自动完成 ## 快速使用 1、引入jar ``` com.bojiw asyncmd-core 1.8 ``` 2、在spring的xml文件中 引入xml文件 ``` ``` 3、配置AsynGroupConfig ``` ``` 4、创建自己业务的asynBizObject,asynCmd,asynExecuter 主要是继承AsynBizObject,AsynCmd,AbstractAsynExecuter 例子: ``` /** * 短信业务模型 * @author wangwendi * @version $Id: SmsBiz.java, v 0.1 2019年07月23日 下午8:58 wangwendi Exp $ */ public class SmsBiz extends AsynBizObject { /** * 手机号 */ private String mobiles; /** * 短信内容 */ private String content; public String getMobiles() { return mobiles; } public void setMobiles(String mobiles) { this.mobiles = mobiles; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } ``` ``` /** * 短信异步命令 * @author wangwendi * @version $Id: SmsAsynCmd.java, v 0.1 2019年07月23日 下午8:57 wangwendi Exp $ */ public class SmsAsynCmd extends AsynCmd { /** * 必须实现 返回对应的AsynBizObject **/ @Override protected Class getObject() { return SmsBiz.class; } } ``` ``` /** * 短信异步命令执行器 需要注入到spring容器中 加@Service注解或者xml配置bean * @author wangwendi * @version $Id: SmsExecuter.java, v 0.1 2019年07月23日 下午9:01 wangwendi Exp $ */ @Service public class SmsExecuter extends AbstractAsynExecuter { /** * 具体业务逻辑 调度方式有三种 异步调度(保存异步命令以后,立刻扔到线程池执行)、调度中心调度(保存异步命令以后不做任何操作 由定时任务捞取数据进行执行)、同步调度(保存异步命令以后立刻同步执行executer方法) 默认是异步调度 可以设置其他三种方式调度 设置方法看demo或者详细配置项 **/ @Override protected void executer(SmsAsynCmd cmd) { SmsBiz content = cmd.getContent(); System.out.println("发送短信"); System.out.println("短信手机号" + content.getMobiles()); } } ``` 5、执行源码sql目录下的asyn.sql创建表 根据前面第三步设置的asynGroupConfig.tableNum分表数量来创建对应的表数量 如上面设置的4 则创建asyn_cmd00,asyn_cmd01,asyn_cmd02,asyn_cmd03 4张表 6、使用异步命令门面服务AsynExecuterFacade 保存异步命令 可以直接用@Autowired注解注入就可以 ``` @Autowired private AsynExecuterFacade asynExecuterFacade; //创建业务对象 创建异步命令对象 然后调用方法 就会自动执行SmsExecuter.executer方法 SmsBiz smsBiz = new SmsBiz(); smsBiz.setContent(content); smsBiz.setMobiles(mobiles); SmsAsynCmd asynCmd = new SmsAsynCmd(); asynCmd.setContent(smsBiz); asynCmd.setBizId(bizId); asynExecuterFacade.saveExecuterAsynCmd(asynCmd); ``` ## 注意点 - 为了保证可靠性 当出现极端情况下 比如执行器执行成功了 修改命令状态时系统挂了 会出现重复执行的情况 所以执行器需要保证幂等 - 如果应用系统已经使用了quartz 需要版本为2.1.7以上 ## 分表情况根据业务id查询保存在哪张表 调用SubTableUtil#getIndex(分表数量,bizId) 可以获取异步命令在哪张表中 方便排查问题 ## 详细配置项 有两个地方可以配置 一个为全局配置项AsynGroupConfig 一个为设置个性化配置项AbstractAsynExecuter ``` ``` 对不同异步命令对象进行个性化对配置 ``` //如果有需要对某个异步命令对象做个性化设置 可以通过注解的方式 设置调度方式为调度中心调度 设置调度频率为 4s,4s,4m @AsynCmdConf(dispatchMode = DispatchMode.DISPATCH,executerFrequency = "4s,4s") public class SmsAsynCmd extends AsynCmd { public static final String name = "sms"; /** * 需要返回业务对象类 组件里要用 * @return */ @Override protected Class getObject() { return SmsBiz.class; } } ``` 设置异步命令执行器的排序值 数值越大 越早执行 默认100 ``` //设置排序值为90 @AsynExecuterConf(sort = 90) @Service public class SmsExecuter extends AbstractAsynExecuter { ``` ## 模型 asynCmdDB(异步命令对象) | 字段名 | 描述 | | :------------ | :------------ | | cmdId | 单表唯一id由表自增 | | cmdType | 异步命令类型 异步命令对象类的名称 | | bizId | 业务id 全局唯一 根据这个ID做hash然后取余获取分表位 计算逻辑和hashmap计算下标为相同 | | content | 业务上下文 业务对象AsynBizObject的json数据 | | executeNum | 异步命令对象执行次数 重试一次加1 | | nextTime | 下一次执行时间 根据设置的重试频率来计算 | | status | 状态 分为"INIT","初始化" "EXECUTE","执行中" "SUCCESS","成功" "ERROR","失败" | | createHostName | 执行插入异步命令服务器的主机名 方便排查问 | | createIp | 执行插入异步命令服务器的ip地址 方便排查问题 | | updateHostName | 更新异步命令服务器的主机名 方便排查问题 | | updateIp | 更新异步命令服务器的ip地址 方便排查问题 | | createName | 创建异步命令的人 默认system | | successExecuters | 执行成功的异步命令执行器类名 如果一个异步命令对象对应多个异步命令执行器 通过这个可以看有成功执行的异步命令对象 | | env | 所在环境 | | exception | 异步命令执行时的异常信息 | | rely_biz_id | 依赖的异步命令业务id | | gmtCreate | 创建时间 | | gmtModify | 修改时间 | AsynCmdHistoryDO异步命令历史模型 字段和内容和异步命令对象模型一样 只有创建时间和更新时间不同 ## 三种调度方式 DispatchMode 异步调度 ![image](http://qiniu.bojiw.com/20197/2019730202546image.png) 调度中心调度 ![image](http://qiniu.bojiw.com/20197/2019730203025image.png) 同步调度 ![image](http://qiniu.bojiw.com/20197/2019730203249image.png)