某项目发布文章加入延迟队列

本文介绍了如何利用Redis的list和zset实现消息队列功能,尤其是作为延迟消费队列,以及如何结合数据库和Feign进行任务调度。作者还展示了如何使用Redis进行任务存储和消费,以及分布式锁在定时刷新数据中的应用。
摘要由CSDN通过智能技术生成

        现在市面上大家使用的消息队列无非就是mq,什么rabbitmq,rocketmq,还有百万级别的kafka,无非就是生产者,消费者,中间一个交换机,一个队列啥的,今天我要替的是一个不怎么常见的工具,redis。

        redis有5种数据结构,字符串,哈希,集合,zset,其中list和zset是今天的主角,list可作为及时消费队列,zeset可作为延迟消费队列,zset从数据库拿数据,list从zset拿数据,同样可实现消息的延迟消费,这里我以最近公司用到的场景为例进行说明

        自媒体文章发布后会将自媒体文章信息映射为一个任务存入redis和数据表中
 

        wmNewsTaskService.addNewsToTask(wmNews.getId(),wmNews.getPublishTime());

	@Override
	@Async
	public void addNewsToTask(Integer id, Date publishTime) {
		log.info("添加任务到延迟服务中----begin");

		Task task = new Task();
		task.setExecuteTime(publishTime.getTime());
		task.setTaskType(TaskTypeEnum.NEWS_SCAN_TIME.getTaskType());
		task.setPriority(TaskTypeEnum.NEWS_SCAN_TIME.getPriority());
		WmNews wmNews = new WmNews();
		wmNews.setId(id);
		task.setParameters(ProtostuffUtil.serialize(wmNews));
		scheduleClient.addTask(task);

		log.info("添加任务到延迟服务中----end");
	}

        这里调用计划模块的功能,通过feign调用,关于feign如何使用,我在往期内容里有房,感兴趣的小伙伴可以去查看,

        将任务加入redis和数据库表
 

    @Override
    public long addTask(Task task) {
        //1.添加任务到数据库中
        boolean success=addTaskToDb(task);

        if (success){
            //2.添加任务到redis
            addTaskToCache(task);
        }

        return task.getTaskId();
    }


================================================
//上述代码用到的方法
//方法1
    /**
     * 添加任务到数据库中
     * @param task
     * @return
     */
    private boolean addTaskToDb(Task task) {
        boolean flag=false;
        try {
            //保存任务表
            Taskinfo taskinfo = new Taskinfo();
            BeanUtils.copyProperties(task,taskinfo);
            taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
            taskinfoMapper.insert(taskinfo);
            //设置taskID
            task.setTaskId(taskinfo.getTaskId());
            //保存任务日志数据
            TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
            BeanUtils.copyProperties(taskinfo,taskinfoLogs);
            taskinfoLogs.setVersion(1);
            taskinfoLogs.setStatus(ScheduleConstants.SCHEDULED);
            taskinfoLogsMapper.insert(taskinfoLogs);
            flag=true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }


//方法2
//此处即使redis的两个队列使用,会设置一个未来时间作为延迟,list中的内容会按照消费频率进行消费,但不一定是马上,zset要等待指定时间
    /**
     * 把任务添加到redis中
     * @param task
     */
    private void addTaskToCache(Task task) {
        String key = task.getTaskType()+"-"+task.getPriority();
        //获取5分钟之后的时间  毫秒值
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE,5);
        long nextScheduleTime = calendar.getTimeInMillis();
        //2.1 如果任务的执行时间小于等于当前时间,存入list
        if (task.getExecuteTime()<=System.currentTimeMillis()){
            cacheService.lLeftPush(ScheduleConstants.TOPIC+key, JSON.toJSONString(task));
        }else if (task.getExecuteTime()<=nextScheduleTime){
        //2.2 如果任务的执行时间大于当前时间 && 小于等于预设时间(未来5分钟) 存入zset中
            cacheService.zAdd(ScheduleConstants.FUTURE+key,JSON.toJSONString(task),task.getExecuteTime());
        }

    }

        消费任务
        同样调用计划模块中的内容,消费速率这里设置的5秒

	@Override
	@Scheduled(fixedRate = 5000)
	@SneakyThrows
	public void scanNewsByTask() {
		log.info("文章审核---消费任务执行---begin---");
		ResponseResult responseResult = scheduleClient.poll(TaskTypeEnum.NEWS_SCAN_TIME.getTaskType(), TaskTypeEnum.NEWS_SCAN_TIME.getPriority());

		if (responseResult.getCode().equals(200)&&responseResult.getData()!=null){
			String json_str = JSON.toJSONString(responseResult.getData());
			Task task = JSON.parseObject(json_str, Task.class);
			byte[] parameters = task.getParameters();
			WmNews wmNews = ProtostuffUtil.deserialize(parameters, WmNews.class);
			System.out.println(wmNews.getId()+"-----------");
			wmNewsAutoScanService.autoScanWmNews(wmNews.getId());
		}

		log.info("文章审核---消费任务执行---end---");
	}

        消费的具体方法
        这里通过删除redis中的内容并更改其在数据库中的状态完成

    /**
     * 按照类型和优先级拉取任务
     * @param type
     * @param priority
     * @return
     */
    @Override
    public Task poll(int type, int priority) {
        Task task=null;
        try {
            String key = type+"-"+priority;
            String task_json = cacheService.lRightPop(ScheduleConstants.TOPIC+key);
            if (StringUtils.isNotBlank(task_json)){
                task = JSON.parseObject(task_json,Task.class);
                //更新数据库信息
                updateDb(task.getTaskId(),ScheduleConstants.EXECUTED);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("poll task exception");
        }
        return task;

    }

        还有这里也是用到了分布式锁
 

    /**
     * 未来数据定时刷新
     */
    @Scheduled(cron = "0 */1 * * * ?")
    public void refresh(){

        System.out.println(System.currentTimeMillis()/1000+"执行了定时任务");

        String token = cacheService.tryLock("FUTURE_TASK_SYNC", 1000 * 30);
        if (StringUtils.isNotBlank(token)){
            log.info("未来数据定时刷新---定时任务");

            // 获取所有未来数据集合的key值
            Set<String> futureKeys = cacheService.scan(ScheduleConstants.FUTURE + "*");// future_*
            for (String futureKey : futureKeys) {  // future_250_250
                //获取当前数据的key  topic
                String topicKey = ScheduleConstants.TOPIC+futureKey.split(ScheduleConstants.FUTURE)[1];
                //获取该组key下当前需要消费的任务数据
                Set<String> tasks = cacheService.zRangeByScore(futureKey, 0, System.currentTimeMillis());
                //同步数据
                if (!tasks.isEmpty()){
                    //将这些任务数据添加到消费者队列中
                    cacheService.refreshWithPipeline(futureKey,topicKey,tasks);
                    System.out.println("成功的将"+futureKey+"刷新到了"+topicKey);
                }
            }

        }

    }

        数据库数据同步到缓存
 

    public void reloadData(){
        clearCache();
        log.info("数据库数据同步到缓存");
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE,5);

        //查看小于未来5分钟的所有任务
        List<Taskinfo> allTasks = taskinfoMapper.selectList(Wrappers.<Taskinfo>lambdaQuery().lt(Taskinfo::getExecuteTime, calendar.getTime()));
        if (allTasks!=null&&allTasks.size()>0){
            for (Taskinfo taskinfo : allTasks) {
                Task task = new Task();
                BeanUtils.copyProperties(taskinfo,task);
                task.setExecuteTime(taskinfo.getExecuteTime().getTime());
                addTaskToCache(task);
            }
        }
    }


==============================================
    private void clearCache(){
        // 删除缓存中未来数据集合和当前消费者队列的所有key
        Set<String> futurekeys = cacheService.scan(ScheduleConstants.FUTURE + "*");// future_
        Set<String> topickeys = cacheService.scan(ScheduleConstants.TOPIC + "*");// topic_
        cacheService.delete(futurekeys);
        cacheService.delete(topickeys);
    }

===============================================
    /**
     * 把任务添加到redis中
     * @param task
     */
    private void addTaskToCache(Task task) {
        String key = task.getTaskType()+"-"+task.getPriority();
        //获取5分钟之后的时间  毫秒值
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE,5);
        long nextScheduleTime = calendar.getTimeInMillis();
        //2.1 如果任务的执行时间小于等于当前时间,存入list
        if (task.getExecuteTime()<=System.currentTimeMillis()){
            cacheService.lLeftPush(ScheduleConstants.TOPIC+key, JSON.toJSONString(task));
        }else if (task.getExecuteTime()<=nextScheduleTime){
        //2.2 如果任务的执行时间大于当前时间 && 小于等于预设时间(未来5分钟) 存入zset中
            cacheService.zAdd(ScheduleConstants.FUTURE+key,JSON.toJSONString(task),task.getExecuteTime());
        }

    }

        记得一键三连,持续更新

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐三葬PLUS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值