DelayTaskUtil

@Controller
public class DelayTaskUtil {
    private static final ExecutorService pl = Executors.newSingleThreadExecutor();;
    private static final DelayQueue<DelayData> queue = new DelayQueue();
    private static final int maxQueueNum = 5;
    private static String redisKey = "DELAYQUEUE:"+getIp();
    private static final String redisSingleKey = "SINGLEKEY";
    private static final String redisSingleKeyNeedDel = "SINGLEKEY_NEEDDEL";
    private Logger log = LoggerFactory.getLogger(DelayTaskUtil.class);

    private static RedisTemplate<String,String> redisTemplate;

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisFiledTemplate;

    @Autowired
    private ApplicationContext applicationContext;

    private static IjyscDelayLogDao ijyscDelayLogDao;

    @Autowired
    private IjyscDelayLogDao logDao;

    @Autowired
    private Environment environment;

    public static Thread thread = null;
    public static volatile boolean isSleep = false;

    @PostConstruct
    public void init(){
        redisTemplate = redisFiledTemplate;
        ijyscDelayLogDao = logDao;
        String port = environment.getProperty("server.port");
        redisKey = redisKey +":"+port;
        System.out.println(redisKey);
        redisFiledTemplate.opsForHash().put(redisSingleKey,redisKey,1);
//        execute();
//        test();
//        redisTemplate.opsForZSet().removeRange(redisKey,0,-1);
    }

    @PreDestroy
    public void destory(){
        redisFiledTemplate.opsForHash().put(redisSingleKey,redisKey,0);
    }


//    @EventListener(classes = {DelayEvent.class})
//    public void lister(DelayEvent delayEvent){
//        DelayData delayData = delayEvent.getDelayData();
//        String id = delayData.getId();
//        long time = delayData.getExpire();
//        System.out.print("id="+id+" 开始时间:"+time);
//        long endtime = System.currentTimeMillis();
//        System.out.println("   结束时间:"+endtime+" 相差:"+(endtime-time));
//    }

    public void execute(){
        pl.execute(new Runnable() {
            @Override
            public void run() {
                thread = Thread.currentThread();
                while (true) {
                    isSleep = false;
                    JyscDelayLogVO jyscDelayLogVO = new JyscDelayLogVO();
                    try {
                        if(queue.isEmpty()){//队列为空
                            transfeTableToTask();//表中转换到redis
                        }
                        transfeTaskToQueue();//redis转换到队列中
                        if(queue.isEmpty()){
                            isSleep = true;
                            LockSupport.parkNanos(60*1000000000L);
                            isSleep = false;
                        }else{
                            DelayData take = queue.take();
                            jyscDelayLogVO.setExctime(LocalDateTime.now());
                            jyscDelayLogVO.setMid(take.getId());
                            jyscDelayLogVO.setResult("2");
                            updateLog(jyscDelayLogVO);

                            boolean needDel = removeCurrentDelay(take);//是否已经是作废信息
                            if(!needDel){
                                String json = JsonUtil.writeValueAsString(take);
                                removeDataFromZset(json);
                                applicationContext.publishEvent(new DelayEvent(this,take));
                            }
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                        String mid = jyscDelayLogVO.getMid();
                        if(StringUtils.isNotEmpty(mid)){
                            jyscDelayLogVO.setError(e.getMessage());
                            jyscDelayLogVO.setErrorDetail(ExceptionUtils.getStackTrace(e));
                            updateLog(jyscDelayLogVO);
                        }
                    }
                }
            }
        });
    }

    public static void addTask(String id, long expire){
        DelayData delayData = new DelayData(id,expire);
        addTask(delayData);
    }

    public static void addTask(DelayData delayData){
        String id = delayData.getId();
        long expire = delayData.getExpire();
        removeTask(id);//取消相同的task
        JyscDelayLogVO jyscDelayLogVO = addLog(delayData);
        long todayEndTime = getTodayEndTime();
        if(expire<todayEndTime){ //当天需要发送的立刻加入redis中
            boolean add = true;
            List<JyscDelayLogVO> currentExp = ijyscDelayLogDao.getCurrentExp(todayEndTime);
            if(CollectionUtils.isNotEmpty(currentExp)){
                JyscDelayLogVO log = currentExp.get(0);
                String mexp = log.getMexp();
                if(StringUtils.isNotEmpty(mexp)){
                    long todayStartTime = getTodayStartTime();
                    long dataExp = Long.parseLong(mexp);
                    if((expire>dataExp&&dataExp>=todayStartTime)){
                        add = false;
                    }
                }
            }
            if(add){
                addToZSet(delayData,expire);
                //更新
                JyscDelayLogVO update = new JyscDelayLogVO();
                update.setRediskey(redisKey);
                update.setId(jyscDelayLogVO.getId());
                update.setUpdateDatetime(LocalDateTime.now());
                ijyscDelayLogDao.updDelayRedisKey(update);

                transfeTaskToQueue();//redis转换到队列中
            }

            if(thread!=null&&isSleep){
                LockSupport.unpark(thread);
            }
        }
    }

    /**
     * 获取当天最后时间
     * @return 返回时间戳
     */
    public static long getTodayEndTime(){
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = LocalDate.now().atTime(23, 59, 59).atZone(zone).toInstant();
        return instant.toEpochMilli();
    }

    public static long getTodayStartTime(){
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = LocalDate.now().atTime(0, 0, 1).atZone(zone).toInstant();
        return instant.toEpochMilli();
    }

    /**
     * 返回秒数
     * @param time 指定时间
     * @param seconds 延迟时间
     * @return 返回时间戳
     */
    public static long getMilli(LocalDateTime time,long seconds){
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = time.plusSeconds(seconds).atZone(zone).toInstant();
        return instant.toEpochMilli();
    }

    public static void  transfeTableToTask(){
        cleanRedisSingleKeyNeedDel();//清理需要删除的队列信息

        JyscDelayLogVO jyscDelayLogVO = new JyscDelayLogVO();
        jyscDelayLogVO.setResult("1");
        jyscDelayLogVO.setMexp(Long.toString(getTodayEndTime()));
        List<JyscDelayLogVO> logs = ijyscDelayLogDao.getLog(jyscDelayLogVO);
        if(CollectionUtils.isNotEmpty(logs)){
            JyscDelayLogVO update = new JyscDelayLogVO();
            LocalDateTime now = LocalDateTime.now();
            for (JyscDelayLogVO log : logs) {
                Integer version = log.getVersion();
                update.setVersion(version);
                update.setNewVersion(version+1);
                update.setRediskey(redisKey);
                update.setMid(log.getMid());
                update.setResult("1");
                update.setUpdateDatetime(now);
                int i = ijyscDelayLogDao.updDelayLog(update);
                if(i>0){
                    String message = log.getMessage();
                    String mexp = log.getMexp();
                    addToZSet(message,Long.parseLong(mexp));
                }
            }
        }
    }

    /**
     * 增加日志
     * @param delayData
     */
    public static JyscDelayLogVO  addLog(DelayData delayData){
        JyscDelayLogVO jyscDelayLogVO = new JyscDelayLogVO();
        jyscDelayLogVO.setMid(delayData.getId());
        long expire = delayData.getExpire();
        jyscDelayLogVO.setMexp(Long.toString(expire));
        jyscDelayLogVO.setMessage(JsonUtil.writeValueAsString(delayData));
        jyscDelayLogVO.setCreateDatetime(LocalDateTime.now());
        jyscDelayLogVO.setResult("1");
        jyscDelayLogVO.setName(getName(delayData.getId()));
        jyscDelayLogVO.setDisabled(0);
        ijyscDelayLogDao.addDelayLog(jyscDelayLogVO);
        return jyscDelayLogVO;
    }

    /**
     * 更新日志
     * @param jyscDelayLogVO
     */
    public static  void updateLog(JyscDelayLogVO jyscDelayLogVO){
        jyscDelayLogVO.setUpdateDatetime(LocalDateTime.now());
        ijyscDelayLogDao.updDelayLog(jyscDelayLogVO);
    }

    public static String getName(String mid){
        for (DelayEnum value : DelayEnum.values()) {
            String val = value.getValue();
            if(mid.startsWith(val)){
                return value.getName();
            }
        }
        return "";
    }

    /**
     * 删除信息,跨微服务删除zset和队列中的作废信息
     * @param id
     */
    public static void removeTask(String id){
        if(StringUtils.isEmpty(id)){
            throw new BusinException("600","必须有ID");
        }
        JyscDelayLogVO jyscDelayLogVO = new JyscDelayLogVO();
        jyscDelayLogVO.setMid(id);
        List<JyscDelayLogVO> logs = ijyscDelayLogDao.getLogByMid(jyscDelayLogVO);
        if(CollectionUtils.isNotEmpty(logs)){
            LocalDateTime now = LocalDateTime.now();
            for (JyscDelayLogVO log : logs) {
                String key = log.getRediskey();
                String result = log.getResult();
                String message = log.getMessage();
                if("1".equals(result)&&StringUtils.isNotEmpty(key)){
                    if(redisKey.equals(key)){
                        DelayData delayData = new DelayData(id,0l);
                        queue.remove(delayData);//删除成功
                        redisTemplate.opsForZSet().remove(key,message);
                    }else{
                        redisTemplate.opsForZSet().remove(key,message);
                        redisTemplate.opsForHash().put(redisSingleKeyNeedDel,id,key);//保存需要删除的消息
                    }
                }
                log.setDisabled(1);
                log.setUpdateDatetime(now);
                ijyscDelayLogDao.delDelayLog(log);
            }
        }
    }

    /**
     * 删除当前微任务的队列垃圾信息
     * @param delayData
     */
    public boolean removeCurrentDelay(DelayData delayData){
        Boolean exist = redisTemplate.opsForHash().hasKey(redisSingleKeyNeedDel, delayData.getId());
        if(exist){
            Object value = redisTemplate.opsForHash().get(redisSingleKeyNeedDel, delayData.getId());
            if(redisKey.equals(String.valueOf(value))){
                redisTemplate.opsForHash().delete(redisSingleKeyNeedDel,delayData.getId());
                return true;
            }
        }
        return false;
    }

    /**
     *
     * @param key
     * @param id
     * @return 0 redis空 ,1找到可能在队列中, 2找到不在队列中,-1未找到
     */
    private static int removeRedisZSet(String  key,String id){
        Long size = redisTemplate.opsForZSet().size(key);
        long head = size>maxQueueNum?maxQueueNum:size;
        long tail = size - maxQueueNum;
        if(size==0){
            return 0; //redis的zset为空
        }
        if(size>0){
            Set<String> range = redisTemplate.opsForZSet().range(key, 0, head);
            for (String s : range) {
                if(s.contains(id)){
                    redisTemplate.opsForZSet().remove(key,s);
                    return 1;//前五
                }
            }
            if(tail>0){
                Set<String> other = redisTemplate.opsForZSet().range(key, head, -1);
                for (String s : other) {
                    if(s.contains(id)){
                        redisTemplate.opsForZSet().remove(key,s);
                        return 2;//只在redis中
                    }
                }
            }
        }
        return -1;//未找到
    }

    @RequestMapping("/test")
    public void test(@RequestParam Integer s) {
        long time =System.currentTimeMillis()+s*1000;
        String id = "1"+s;
        addTask(id,time);
    }

    public static void transfeTaskToQueue(){
        cleanRedisSingleKeyNeedDel();//清理需要删除的队列信息

        Set<String> dataFromZset = getDataFromZset(maxQueueNum);
        if(dataFromZset!=null){
            for (String str : dataFromZset) {
                DelayData delayData = JsonUtil.readValue(str,DelayData.class);
                addQueue(delayData);//添加任务队列
            }
        }
        //清理队列
        int num = queue.size() - maxQueueNum;
        if(num > 0){
            Iterator<DelayData> iterator = queue.iterator();
            DelayData maxDelay = null;
            for(int i=num;i>0;i--){
                while(iterator.hasNext()){
                    DelayData next = iterator.next();
                    if(maxDelay==null||maxDelay.getExpire()<next.getExpire()){
                        maxDelay = next;
                    }
                }
                queue.remove(maxDelay);
            }
        }
    }

    /**
     * 清理需要删除的队列信息
     */
    public static void cleanRedisSingleKeyNeedDel(){
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(redisSingleKeyNeedDel);
        if(entries!=null && entries.size()>0){
            for (Map.Entry<Object, Object> objectObjectEntry : entries.entrySet()) {
                String value = String.valueOf(objectObjectEntry.getValue());
                if(redisKey.equals(value)){
                    String key = String.valueOf(objectObjectEntry.getKey());
                    DelayData delayData = new DelayData(key,0l);
                    queue.remove(delayData);
                    redisTemplate.opsForHash().delete(redisSingleKeyNeedDel,key);
                }
            }
        }
    }

    /**
     * 添加任务队列
     * @param delayData
     */
    public static void addQueue(DelayData delayData){
        if(queue.contains(delayData)){
            queue.remove(delayData);
        }
        queue.put(delayData);
    }

    /**
     * 添加zset
     * @param value
     * @param score
     */
    public static void addToZSet(DelayData value, long score) {
        String str = JsonUtil.writeValueAsString(value);
        addToZSet(str,score);
    }

    public static void addToZSet(String str, long score) {
        redisTemplate.opsForZSet().add(redisKey, str, score);
    }

    /**
     * 获取数据
     * @param num >=1
     * @return
     */
    public static Set<String> getDataFromZset(long num){
        long l = redisTemplate.opsForZSet().size(redisKey).longValue();
        if(l==0){
            return null;
        }
        return redisTemplate.opsForZSet().range(redisKey, 0, num-1);
    }

    /**
     * 删除数据
     * @param delayData
     */
    public static void removeDataFromZset(String delayData){
        redisTemplate.opsForZSet().remove(redisKey,delayData);
    }


    public static String getIp(){
        //得到IP,
        InetAddress ia = null;
        try {
            ia = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        String ip=ia.toString().split("/")[1];
//        ip = ip.replaceAll("\\.","_");
        System.out.println(ia);
        System.out.println("本机的IP:"+ip);
        //得到IP,输出PC-201309011313/122.206.73.83

        return ip;
    }
}

  

posted @ 2024-12-02 11:44  Li_ll  Views(2)  Comments(0Edit  收藏  举报