你的浏览器不支持canvas

做你害怕做的事情,然后你会发现,不过如此。

Spring Boot使用redis实现简单队列

时间: 作者: 黄运鑫

本文章属原创文章,未经作者许可,禁止转载,复制,下载,以及用作商业用途。原作者保留所有解释权。


应用场景

  • 一个考试项目,每场考试有上千人在线答题,在考试结束时提交考试结果保存到数据库。因为保存的考试数据比较多,入库慢导致前端请求超时和服务崩溃。
  • 考虑使用队列解决,队列作用是异步解耦削峰
  • 具体思路是将保存考试分解成两个步骤:
    • 第一步:前端提交考试成绩保存到redis
    • 第二步:后台异步从redis获取考试成绩并入库
  • 好处是前端提交考试后执行第一步操作即可完成提交,提高了请求的响应时间;后台异步将reids中的数据入库,减少了服务器和数据库的压力。

实现

  • redislist有四个操作lpushrpushlpoprpop
    • lpush:从列表左边插入数据
    • rpush:从列表右边插入数据
    • lpop:从列表左边取出数据
    • rpop:从列表右边取出数据
  • 可以看出,如果让队列先进先出,需要使用lpushrpop,或rpushlpop
  • 实现代码:
/**
 * @author hyx
 * 考试队列
 */
@Service
public class ExamQueueService implements InitializingBean {
    //队列名
    public final String QUEUE_NAME = "examRecord";
    
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 入队
     */
    public Long enqueue(Object record) {
        JSONObject object = new JSONObject();
        object.put("record", record);
        return redisTemplate.opsForList().leftPush(this.QUEUE_NAME, object);
    }

    /**
     * 出队
     */
    public Object dequeue() {
        Object o = redisTemplate.opsForList().rightPop(this.QUEUE_NAME);
        return o;
    }

    /**
     * 消费队列(此方法在Spring容器初始化的时候执行)
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void afterPropertiesSet() {
        //启用一个线程来消费队列,将数据从队列中取出来并入库
        //数据库性能允许的情况下,可以启用多个线程
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    //从队列中获取一条数据(获取后队列中会自动删除此数据)
                    Object dequeue = this.dequeue();
                    if (dequeue == null) {
                        //如果队列为空,则休眠一秒
                        Thread.sleep(1000L);
                    } else {
                        //数据入库...略
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}
  • 例子中使用了一个线程来消费队列,如果数据库性能允许也可以启用多个线程消费队列。
  • 因为rpop操作是原子性的,所以即使多个线程消费一个队列,也不会出现重复消费的问题。

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。