• Redis做消息队列使用


    背景:

      很多场景需要实效性不是很高,可以异步操作,java自带的Async异步操作或者新创建线程池进行异步会额外占用内存,或者使用RabbitMQ和RocketMQ也会带来额外操作,在同一系统使用这些MQ也会站占用消耗其他资源,所以选择使用Redis队列来实现

    场景:

      比如发布评论消息,需要进行内容审核校验,需要修改主贴的回复数量,需要添加被回复人的消息通知,如果是除了一级回复添加二三级回复需要更新一级回复的回复数量,等等,一系列的操作全部同步,客户端等待响应,耗费的时间也是相对比较长的,所以将添加回复之后其他操作放入队列完成。

    封装代码如下:

    public interface IQueue<E> {
        /**
         * 入队操作
         * @param e 入队数据
         * @return
         */
        boolean rightPush(E e);
    
        /**
         * 出队
         * @return 一条队列数据
         */
        E leftPop();
    
        /**
         * 队列大小
         * @return
         */
        long size();
    }

      

    public class RedisQueue implements IQueue<String> {
    
        /**
         * redis 模板
         */
        private RedisTemplate<String, String> redisQueueTemplate;
    
        /**
         * redis key
         */
        private String key;
    
        public RedisQueue(String key, RedisTemplate<String, String> redisQueueTemplate) {
            this.key = key;
            this.redisQueueTemplate = redisQueueTemplate;
        }
    
        @Override
        public boolean rightPush(String s) {
            return redisQueueTemplate.opsForList().rightPush(key, s) > 0;
        }
    
        @Override
        public String leftPop() {
            return redisQueueTemplate.opsForList().leftPop(key);
        }
    
        @Override
        public long size() {
            return redisQueueTemplate.opsForList().size(key);
        }
    }

    在添加回复完之后进行放入队列操作,

    实例话接口IQueue,这里使用redisTemplate的list进行右侧入队操作调用rightPush()

    public abstract class  AbstractQueueWorkerService implements InitializingBean {
        private static final Logger LOG = LoggerFactory.getLogger(AbstractQueueWorkerService.class);
    
        protected volatile boolean monitorStarted = false;
    
        protected volatile boolean monitorShutDowned = false;
        //默认休眠时间 500毫秒
        private static final int DEFAULT_SLEEP_TIME = 500;
    
        private ExecutorService executorService;
    
        private static final int DEFAULT_THREAD_NUM = 1;
        /**
         * 线程数量(默认1)
         */
        private int threadNum = DEFAULT_THREAD_NUM;
    
        private int threadSheepTime = DEFAULT_SLEEP_TIME;
    
        /**
         * 线程名称
         */
        protected String threadName;
    
        /**
         * 需要监控的队列
         */
        protected IQueue<String> monitorQueue;
    
        public void setQueueTaskConf(String threadName , int threadNum, IQueue<String> monitorQueue,int threadSheepTime ){
            this.threadName = threadName;
            this.threadNum = threadNum;
            this.monitorQueue = monitorQueue;
            this.threadSheepTime = threadSheepTime;
        }
    
        public void setThreadNum(int threadNum) {
            this.threadNum = threadNum;
        }
    
        public void setThreadName(String threadName) {
            this.threadName = threadName;
        }
    
        public void setThreadSheepTime(int threadSheepTime) {
            this.threadSheepTime = threadSheepTime;
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
    
            executorService = Executors.newFixedThreadPool(threadNum);
    
            for (int i = 0; i < threadNum; i++) {
                final int num = i;
                executorService.execute(() -> {
                    Thread.currentThread().setName(threadName +"["+num+"]");
                    while (!monitorShutDowned) {
                        String value = null;
                        try {
                            value = beforeExecute();
                            if (StringUtils.isNotEmpty(value)) {
                                if(LOG.isDebugEnabled()){
                                    LOG.debug("Monitor Thread[" + Thread.currentThread().getName() + "], pop from queue,value = {}", value);
                                }
                                boolean success = execute(value);
                                // 失败重试
                                if (!success) {
                                    success = retry(value);
                                    if (!success) {
                                        LOG.warn("Monitor Thread[" + Thread.currentThread().getName() + "] execute Failed,value = {}", value);
                                        failProcess(value);
                                    }
                                } else {
                                    if(LOG.isDebugEnabled()){
                                        LOG.debug("Monitor Thread[" + Thread.currentThread().getName() + "]:execute successfully!values = {}", value);
                                    }
                                }
                            } else {
                                if(LOG.isDebugEnabled()){
                                    LOG.debug("Monitor Thread[" + Thread.currentThread().getName() + "]:monitorThreadRunning = {}", monitorStarted);
                                }
                                Thread.sleep(threadSheepTime);
                            }
                        } catch (Exception e) {
                            LOG.error("Monitor Thread[" + Thread.currentThread().getName() + "] execute Failed,value = " + value, e);
                        }
                    }
                    LOG.info("Monitor Thread[" + Thread.currentThread().getName() + "] Completed...");
                });
            }
            LOG.info("thread pool is started...");
        }
    
        /**
         * 操作队列取数据
         *
         * @return 队列数据
         */
        public String beforeExecute() {
            return monitorQueue.leftPop();
        }
    
        /**
         * 执行业务逻辑
         */
        public abstract boolean execute(String value);
    
        /**
         * 重试
         *
         * @param value 队列内容
         * @return true:成功,false:失败
         */
        protected boolean retry(String value) {
            LOG.info("job retry, value: {}",value);
            return execute(value);
        }
    
        /**
         * 失败处理
         * @param value
         */
        protected void failProcess(String value){
    
        }
    
        protected void shutdown() {
            executorService.shutdown();
            monitorShutDowned = true;
            LOG.info("thread pool is shutdown...");
        }
    }

    封装类继承InitializingBean,或者实现ApplicationContext,或者使用方法注解@PostConstruct待上下文加载完成进行消息的消费,出队列操作

    实现execute方法可使用redisTemplate的list.leftPop()进行左侧取出队列

    @Slf4j
    public class MonitorReplyQueueService extends AbstractQueueWorkerService {
    
        @Autowired
        private ReplyService replyService;
    
        @Override
        public boolean execute(String value) {
            log.info("MonitorReplyQueueService execute value: {}", value);
            replyService.handleReplyQueue(value);
            return true;
        }
    
        public MonitorReplyQueueService(IQueue<String> redisQueue, String threadName, int threadNum,
                                          int threadSleepTime) {
            setQueueTaskConf(threadName, threadNum, redisQueue, threadSleepTime);
        }
    
    }

    这里可以自己取出队列数据大小来进行线程池的动态扩容,可以保证消息快速消费完。

  • 相关阅读:
    解决Hexo博客模板hexo-theme-next的翻页按钮不正常显示问题
    通过filebeat、logstash、rsyslog采集nginx日志的几种方式
    浅析SIEM、态势感知平台、安全运营中心
    解决layui表单ajax提交回调函数不起作用问题的两种方式
    LVM简介及CentOS7 LVM操作实战
    JAVA设计模式总结之23种设计模式
    JAVA设计模式总结之六大设计原则
    SpringMVC学习笔记_02
    SpringMVC学习笔记_01
    Mybatis 学习总结
  • 原文地址:https://www.cnblogs.com/LiuFqiang/p/16366813.html
Copyright © 2020-2023  润新知