学成在线19,分布式事务,corn定期时间表达式

corn定期时间表达式

Spring Task 定时任务
3.1 需求分析
根据分布式事务的研究结果,订单服务需要定时扫描任务表向MQ发送任务。本节研究定时任务处理的方案,并实
现定时任务扫描任务表并向MQ发送消息。
实现定时任务的方案如下:
1 、使用jdk的Timer和TimerTask实现
可以实现简单的间隔执行任务,无法实现按日历去调度执行任务。
2、使用Quartz实现
Quartz 是一个异步任务调度框架,功能丰富,可以实现按日历调度。
3、使用Spring Task实现
Spring 3.0后提供Spring Task实现任务调度,支持按日历调度,相比Quartz功能稍简单,但是在开发基本够用,支
持注解编程方式。
本项目使用Spring Task实现任务调度。
3.2 Spring Task 串行任务(案例)
3.2.1 编写任务类
在Spring boot启动类上添加注解:@EnableScheduling
新建任务测试类TestTask,编写测试方法如下:

import com.xuecheng.framework.domain.task.XcTask;
import com.xuecheng.order.config.RabbitMQConfig;
import com.xuecheng.order.service.TaskService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

/**
 * @author Administrator
 * @version 1.0
 **/
@Component
public class ChooseCourseTask {
    private static final Logger LOGGER = LoggerFactory.getLogger(ChooseCourseTask.class);

    @Autowired
    TaskService taskService;

    @RabbitListener(queues = RabbitMQConfig.XC_LEARNING_FINISHADDCHOOSECOURSE)
    public void receiveFinishChoosecourseTask(XcTask xcTask){
        if(xcTask!=null && StringUtils.isNotEmpty(xcTask.getId())){
            taskService.finishTask(xcTask.getId());
        }
    }

    @Scheduled(cron="0/3 * * * * *")
    //定时发送加选课任务
    public void sendChoosecourseTask(){
        //得到1分钟之前的时间
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(new Date());
        calendar.set(GregorianCalendar.MINUTE,-1);
        //当前时间-1分钟,就是一分钟之前的时间
        Date time = calendar.getTime();
        List<XcTask> xcTaskList = taskService.findXcTaskList(time, 100);
        System.out.println(xcTaskList);
        //调用service发布消息,将添加选课的任务发送给mq
        for(XcTask xcTask:xcTaskList){
            //取任务
            if(taskService.getTask(xcTask.getId(),xcTask.getVersion())>0){
                String ex = xcTask.getMqExchange();//要发送的交换机
                String routingKey = xcTask.getMqRoutingkey();//发送消息要带routingKey
                taskService.publish(xcTask,ex,routingKey);
            }

        }
    }

    //定义任务调试策略
//    @Scheduled(cron="0/3 * * * * *")//每隔3秒去执行
//       @Scheduled(fixedRate = 3000) //在任务开始后3秒执行下一次调度
//       @Scheduled(fixedDelay = 3000) //在任务结束后3秒后才开始执行
//    	@Scheduled(initialDelay=3000, fixedRate=5000) //第一次延迟3秒,以后每隔5秒执行一次
        public void task1(){
        LOGGER.info("===============测试定时任务1开始===============");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LOGGER.info("===============测试定时任务1结束===============");

    }

    //定义任务调试策略
//    @Scheduled(cron="0/3 * * * * *")//每隔3秒去执行
//    @Scheduled(fixedRate = 3000) //在任务开始后3秒执行下一次调度
//       @Scheduled(fixedDelay = 3000) //在任务结束后3秒后才开始执行
    public void task2(){
        LOGGER.info("===============测试定时任务2开始===============");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LOGGER.info("===============测试定时任务2结束===============");

    }
}

cron表达式详解:

cron 表达式包括6部分:
秒(0~59) 分钟(0~59) 小时(0~23) 月中的天(1~31) 月(1~12) 周中的天
(填写MON,TUE,WED,THU,FRI,SAT,SUN,或数字1~7 1表示MON,依次类推)
特殊字符介绍:
“/”字符表示指定数值的增量
“*”字符表示所有可能的值
“-”字符表示区间范围
“,” 字符表示列举
“?”字符仅被用于月中的天和周中的天两个子表达式,表示不指定值

例子:
0/3 * * * * * 每隔3秒执行
0 0/5 * * * * 每隔5分钟执行
0 0 0 * * * 表示每天0点执行
0 0 12 ? * WEN 每周三12点执行
0 15 10 ? * MON-FRI 每月的周一到周五10点 15分执行
0 15 10 ? * MON,FRI 每月的周一和周五10点 15分执行

2.4.3 消息队列实现最终一致

本方案是将分布式事务拆分成多个本地事务来完成,并且由消息队列异步协调完成,如下图:
下边以下单减少库存为例来说明:
学成在线19,分布式事务,corn定期时间表达式_第1张图片
1、订单服务和库存服务完成检查和预留资源。
2、订单服务在本地事务中完成添加订单表记录和添加“减少库存任务消息”。
3、由定时任务根据消息表的记录发送给MQ通知库存服务执行减库存操作。
4、库存服务执行减少库存,并且记录执行消息状态(为避免重复执行消息,在执行减库存之前查询是否执行过此
消息)。
5、库存服务向MQ发送完成减少库存的消息。
6、订单服务接收到完成库存减少的消息后删除原来添加的“减少库存任务消息”。
实现最终事务一致要求:预留资源成功理论上要求正式执行成功,如果执行失败会进行重试,要求业务执行方法实
现幂等。
优点 :
由MQ按异步的方式协调完成事务,性能较高。
不用实现try/confirm/cancel接口,开发成本比TCC低。
缺点:
此方式基于关系数据库本地事务来实现,会出现频繁读写数据库记录,浪费数据库资源,另外对于高并发操作不是
最佳方案。

2.5 自动添加选课方案

2.5.1 搭建环境
根据自动选课需求,为了更好的分析解决方案,这里搭建订单工程及数据库。
2.5.2.1 创建订单工程
导入资料下的xc-service-manage-order工程。
2.5.2.2 创建订单数据库
1、创建订单数据库xc_order(MySQL)
导入xc_order.sql
学成在线19,分布式事务,corn定期时间表达式_第2张图片
学成在线19,分布式事务,corn定期时间表达式_第3张图片
学成在线19,分布式事务,corn定期时间表达式_第4张图片
在任务表中包括了交换机的名称、路由key等信息为了是将任务的处理做成一个通用的功能。
考虑分布式系统并发读取任务处理任务的情况发生项目使用乐观锁的方式解决并发问题。
学成在线19,分布式事务,corn定期时间表达式_第5张图片

学成在线19,分布式事务,corn定期时间表达式_第6张图片

2.5.2 解决方案

本项目综合考虑选择基于消息的分布式事务解决方案,解决方案如下图:
学成在线19,分布式事务,corn定期时间表达式_第7张图片
1、支付成功后,订单服务向本地数据库更新订单状态并向消息表写入“添加选课消息”,通过本地数据库保证订单
状态和添加选课消息的事务。。
2、定时任务扫描消息表,取出“添加选课任务“并发向MQ。
3、学习服务接收到添加选课的消息,先查询本地数据库的历史消息表是否存在消息,存在则说明已经添加选课,
否则向本地数据库添加选课,并向历史消息表添加选课消息。这里选课表和历史消息表在同一个数据库,通过本地
事务保证。
4、学习服务接收到添加选课的消息,通过查询消息表判断如果已经添加选课也向MQ发送“完成添加选课任务的消
息”,否则则添加选课,完成后向MQ发送“完成添加选课任务的消息”,
5、订单服务接收到完成选课的消息后删除订单数据库中消息表的“添加选课消息”,为保证后期对账将消息表的消
息先添加到历史消息表再删除消息,表示此消息已经完成。

具体定时发布消息,和处理添加课程代码:参照Day19,学成源码:
学成在线19,分布式事务,corn定期时间表达式_第8张图片

你可能感兴趣的:(学成在线19,分布式事务,corn定期时间表达式)