springboot定时计划实践

2020-08-01 10:36:42  卢浮宫  版权声明:本文为站长原创文章,转载请写明出处


一、背景

    最近公司有一个需求是预处理一些事件,事件数量不多,但是各自的行为准则都不相同。

综合考虑之下,决定使用springboot的定时计划来解决这个问题。


二、需要满足的需求大致如下

    1、能针对事件在后面的某个时间点做预处理

    2、支持多任务

    3、支持任务配置

    4、支持处理结果响应


三、整体的构建方案及数据库支持

    1、大体流程如下

        

    2、相关数据库支持

        定时器属性配置表

        CREATE TABLE `XXX_timer_setting` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(45) DEFAULT NULL COMMENT '标题',
`params` varchar(200) DEFAULT NULL COMMENT '定时器相关执行参数',
`receiver` varchar(45) DEFAULT NULL COMMENT '接收人号码(工作手机,用于企业微信消息发送)',
`state` tinyint(4) DEFAULT '0' COMMENT '是否启用 0:启用;1:不启用',
`time_out_val` int(11) DEFAULT NULL COMMENT '延时(分)',
`deviate` int(11) DEFAULT NULL COMMENT '偏差时间(分)PS: 这个数值必须必执行间隔大',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`remark` varchar(45) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='定时器执行参数';

        定时计划任务表

        CREATE TABLE `XXX_timer_task` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`event` int(11) DEFAULT NULL COMMENT '事件',
`params` varchar(512) DEFAULT NULL COMMENT '参数,对应不同实体类',
`state` tinyint(4) DEFAULT NULL COMMENT '状态: 0:待执行;1:执行成功;2:执行失败',
`end_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '结束时间',
`create_user_id` int(11) DEFAULT NULL COMMENT '操作人',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`remark` varchar(45) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COMMENT='定时计划任务';


四、核心代码如下(这里使用的是Scheduled Task)

     在class上使用注解 @EnableScheduling
    /**
* 功能描述: 根据定时计划配置,生成指定的定时器
* Param: [taskRegistrar]
* Return: void
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
List<BhTimerSettingEntity> bhTimerSettingEntityList = bhTimerSettingService.lambdaQuery().eq(BhTimerSettingEntity::isState, 0).list();
bhTimerSettingEntityList.forEach(item->{
taskRegistrar.addTriggerTask(
() -> timerHandler(item),
triggerContext -> {
String cron = item.getParams();
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
);
});
}


    /**
* 功能描述: 定时任务处理项
* Param: [bhTimerSettingEntity]
* Return: void
*/
private void timerHandler(BhTimerSettingEntity bhTimerSettingEntity){
Integer airOrderEnum = TaskEventEnum.STATE_00.getValue();
// 读取定时计划任务表未执行完成的数据列表(状态为0)
List<BhTimerTaskEntity> bhTimerTaskEntityList = bhTimerTaskService.lambdaQuery().eq(BhTimerTaskEntity::getState, 0).list();
bhTimerTaskEntityList.forEach(item->{
if(airOrderEnum.equals(item.getEvent())){
// XXX处理
XXXMainHandle(bhTimerSettingEntity, item);
}else{
// XXX预留

}
});
}
    /**
* 功能描述: XXX定时任务处理
* Param: [bhTimerSettingEntity, bhTimerTaskEntity]
* Return: void
*/
private void XXXMainHandle(BhTimerSettingEntity bhTimerSettingEntity, BhTimerTaskEntity bhTimerTaskEntity){
// 获取操作时间偏差值(分)
int deviate = bhTimerSettingEntity.getDeviate();
AirOrderMainDTO airOrderMainDTO = JSONObject.parseObject(bhTimerTaskEntity.getParams(), AirOrderMainDTO.class);
LocalDateTime createTime = bhTimerTaskEntity.getCreateTime();
// 执行时间区间 为 (创建时间 + 延时 - 时间偏差,创建时间 + 延时 + 时间偏差)
LocalDateTime startTime = createTime.plusMinutes(bhTimerSettingEntity.getTimeOutVal()).minusMinutes(deviate);
LocalDateTime endTime = createTime.plusMinutes(bhTimerSettingEntity.getTimeOutVal()).plusMinutes(deviate);
LocalDateTime now = LocalDateTime.now();
if(now.isAfter(startTime) && now.isBefore(endTime)){
// 如果当前时间在此区间中,则进行预订任务
if(bhTimerTaskEntity.getParams().isEmpty()){
return;
} // XX存在性和状态监测 // 相关业务处理

bhTimerTaskEntity.setState(1);
// 计划任务状态修改
UpdateWrapper<BhTimerTaskEntity> bhTimerTaskEntityUpdateWrapper = new UpdateWrapper<BhTimerTaskEntity>();
bhTimerTaskEntityUpdateWrapper.eq("id", bhTimerTaskEntity.getId()).set("state", bhTimerTaskEntity.getState());
if(!bhTimerTaskService.update(bhTimerTaskEntityUpdateWrapper)){
log.error("id:" + bhTimerTaskEntity.getId() + " ->任务状态更新失败");
// 发送企业微信消息(可以参见本站中关于企业微信发送消息的文章)
buildAndSendMsg(bhTimerSettingEntity, airOrderMainDTO);
return;
}
}
}
    /**
* @author XA
* date 2020/7/6 18:01
* description 消息发送处理
* params
* return
*/
@Component
public class BhMessageHandler {

@Value("${qywx.url}")
private String url;

private final Logger log = LoggerFactory.getLogger(BhMessageHandler.class);

public void sendMsg(String params){
String result = "";

HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(url);
try {
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setHeader("Accept", "application/json");
if (StringUtils.isNotBlank(params)) {
httpPost.setEntity(new StringEntity(params, Charset.forName("UTF-8")));
}
HttpResponse response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
result = EntityUtils.toString(response.getEntity());
log.info("返回数据:" + result);
} else {
log.info("请求失败");
}
} catch (IOException e) {
log.error("请求异常");
}
} }


五、后记

    在执行线上环境打包时因为maven的预检测机制,可能会因为无法连接到线上环境而导致打包失败。

这个时候我们只需要再pom文件中加入如下代码即可跳过这个检测已成功打包线上环境。

    pom相关配置

    <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<skipTests>true</skipTests><!--添加以跳过检测 -->
</properties>




更多精彩请关注guangmuhua.com


最新评论:

‘’‘’
2020-10-12 11:33:29
1楼