zoukankan      html  css  js  c++  java
  • 一次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
  • 相关阅读:
    vue.js报错:Module build failed: Error: No parser and no file path given, couldn't infer a parser.
    安装node
    java四大特性理解(封装继承多态抽象)
    在成功道路上,你要百败百战
    职场观察:高薪需要什么?
    jBPM4工作流应用开发指南
    WebService技术简介
    如何获得Android设备名称(ADB命令详细介绍)
    how-to-fix-fs-re-evaluating-native-module-sources-is-not-supported-graceful
    QQ文件没有读取权限,60017导致QQ无法登陆的终极解决办法
  • 原文地址:https://www.cnblogs.com/s6-b/p/12079694.html
Copyright © 2011-2022 走看看