一次redis使用

需求: 某系统在启动或重启时需将未执行任务加载至redis缓存中,周期或定时执行缓存中的任务。当用户增加新任务时 需要把新的周期任务或定时任务添加进redis缓存,当用户删除任务需要同时删除redis缓存中的数据 

   (最先的方案是 只用java内存 不用redis ,但是此种方案存在高延时的风险,即当用户删除任务同时要清空内存中的任务数据时,如果线程在执行大批量任务命令 用户此时删除任务 用户会一直等待直到线程执行完毕

解决方案 使用俩个缓存redis+java内存hashmap<String,model> 首先库中未执行任务先缓存到redis中 然后 java内存hashmap<String,model>读取reids中的数据 线程循环java内存hashmap<String,model>中的数据 即使用户删除某些任务更新的是redis中的数据 不会影响java内存hashmap<String,model>中命令下发 同时给redis加锁(存在用户和java内存hashmap<String,model>并行操作redis情况)

    但是这样的方案有个弊端  本就珍贵的内存中 我们存了双份的数据 一份是redis数据一份是java内存数据  ,所以只用一份内存来处理是最高效的,用ConcurrentHashMap来处理并发问题 

       ConcurrentHashMap的优势在于 不会锁住整个对象 它的锁的粒度是key  这样在对象层次上不存在锁 不会发生线程阻塞

设计结构:

  初始化->redis->java内存->在java内存中处理所有待执行任务

1:初始化加载任务至redis

@PostConstruct
    public void init() {
        try {
            //初始化或重启服务 缓存待执行任务到redis里
            ArrayList<TaskModel> a=taskMapper.getTasks();
            for (int i=0;i<a.size();i++){
                初始化存redis
                redisUtil.set("task"+a.get(i).getTaskId(),a.get(i));
               
            }
            System.err.println("待执行任务加载到缓存中 "+a.size()+" 条");
        } catch (Exception e) {
            //错误输出
            e.printStackTrace();
        }
    }
View Code

2:定时任务将redis中数据专程map 存储在redis中

 3:用户删除redis数据

删除redis缓存
               redisUtil.del("task"+taskId);
View Code

4:概要设计流程图

5:优化成只用java内存来处理 去掉redis层  用ConcurrentHashMap做内存保存数据

任务工具类

public class TaskUtil {
    /**
     * 定时任务的java缓存 key:任务id  value:任务对象
     */
    public static ConcurrentHashMap<String, TaskModel> hashMap=new ConcurrentHashMap<>();
public static ConcurrentHashMap<String, TaskModel> getHashMap() {
        return hashMap;
    }

    public static void setHashMap(ConcurrentHashMap<String, TaskModel> hashMap) {
        TaskUtil.hashMap = hashMap;
    }
}
View Code

初始化加载数据至ConcurrentHashMap中

@PostConstruct
    public void init() {
        try {
            //初始化或重启服务 缓存待执行任务到redis里
            ArrayList<TaskModel> a=taskMapper.getTasks();
            for (int i=0;i<a.size();i++){
                //初始化存java内存
                TaskUtil.getHashMap().put(a.get(i).getTaskId(),a.get(i));
            }
            System.err.println("待执行任务加载到缓存中 "+a.size()+" 条");
        } catch (Exception e) {
            //错误输出
            e.printStackTrace();
        }
    }
View Code

开一个线程在内存中处理任务数据

(之前开发用的是java的Timer类 是一个线程处理一个任务,如果任务有上万了那就要开上万个线程 系统会崩溃 优化方案是只用一个线程就处理所有任务 用的是springboot的@Scheduled注解,此注解默认开的是一个线程)

@Scheduled(cron = "0 0/5 * * * ?")
    @RequestMapping("/taskM")
    public void taskCron() {
        try {
            //线程开始时间
            Date now=new Date();
            //java内存
            ConcurrentHashMap<String, TaskModel> taskMap=TaskUtil.getHashMap();
            //循环java内存中的任务
            for (Map.Entry<String,TaskModel> listEntry: taskMap.entrySet()){
                //任务类型 (0:立即执行,1:周期执行,2:定时执行)
                String taskTimeType=listEntry.getValue().getTaskTimeType();
                //任务开始执行时间
                String startTime=listEntry.getValue().getTaskTimer();

                long btTime= TaskUtil.betweenTime(DateUtil.time4.parse(startTime),now);
                //周期任务
                if (taskTimeType.equals(TaskUtil.TASK_1)){
                    long count=TaskUtil.getTask2Count(btTime,TaskUtil.PERIOD);
                    if (count==0){
                        //执行命令
                       this.doCmd(listEntry,startTime,now);
                    }else if (count<0){//start-now<0
                        //补时到当前周期
                        startTime=TaskUtil.backTime(startTime,now,listEntry.getValue().getTaskTimerPeriod());
                        long btTimeBack= TaskUtil.betweenTime(DateUtil.time4.parse(startTime),now);
                        long countBack=TaskUtil.getTask2Count(btTimeBack,TaskUtil.PERIOD);
                        if (countBack==0){
                            //执行命令
                            this.doCmd(listEntry,startTime,now);
                        }else {
                            //将下一次执行时间更新至内存
                            String takId=listEntry.getValue().getTaskId();//任务id
                            //补时到下一个周期
                            startTime=TaskUtil.backTime2(startTime,now,listEntry.getValue().getTaskTimerPeriod());
                            if (TaskUtil.getHashMap().containsKey(takId)){
                                TaskUtil.getHashMap().get(takId).setTaskTimer(startTime);
                            }
                        }
                    }
                }
                //定时任务
                else if (taskTimeType.equals(TaskUtil.TASK_2)){
                    long count=TaskUtil.getTask2Count(btTime,TaskUtil.PERIOD);
                    //倒计时器为0时 开始执行定时任务 错过执行时间周期则不执行 等待用户重新添加定时任务
                    if (count==0){
                        //执行命令
                        this.doCmd(listEntry,startTime,now);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
View Code

猜你喜欢

转载自www.cnblogs.com/s6-b/p/12079694.html