SpringBoot 2.X 基于 Redis 实现延时消息队列功能


1 摘要

延时消息队列的功能除了使用 RabbitMQ 这类专业的消息队列工具实现外,如果应对小规模简单的业务,也可以 Redis 实现延时队列的功能。本文将介绍 Springboot 基于 Redis 实现延时队列功能。

2 核心 Maven 依赖

        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>${springboot.version}</version>
        </dependency>

这里作为演示的 springboot 版本为:

<springboot.version>2.0.6.RELEASE</springboot.version>

辅助工具

        <!-- hutool,集成java 工具包 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>

版本信息为:

<hutool.version>5.3.9</hutool.version>

2 核心代码

2.1 配置文件
./demo-web/src/main/resources/application-dev.yml
spring:
  # redis
  redis:
    database: 1
    host: 172.16.140.10
    port: 7749
    password: 66666
    timeout: 10000ms
    jedis:
      pool:
        max-active: 200
        max-idle: 500
        min-idle: 50
        max-wait: 100s
2.2 延时队列工具类
./demo-base-web/src/main/java/com/ljq/demo/springboot/baseweb/util/RedisDelayQueueUtil.java
package com.ljq.demo.springboot.baseweb.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Set;

/**
 * @Description: Redis 延时队列工具类
 * @Author: junqiang.lu
 * @Date: 2021/10/14
 */
@Slf4j
@Component
public class RedisDelayQueueUtil {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 队列名称
     */
    public static final String QUEUE_NAME_ORDER = "orderDelayQueue";

    /**
     * 延时时长
     */
    public static final long QUEUE_DELAY_TIME_ORDER = 30000;

    /**
     * 设置订单延时任务
     *
     * @param orderMsg 订单消息
     * @param delayTime 延时时间,距离当前时间的时间间隔(单位:毫秒)
     */
    public void setOrderDelayTask(Object orderMsg, long delayTime) {
        long expireTime = System.currentTimeMillis() + delayTime;
        boolean addFlag = redisTemplate.opsForZSet().add(QUEUE_NAME_ORDER, JSONUtil.toJsonStr(orderMsg), expireTime);
        if (addFlag) {
            // TODO 记录订单状态

            log.info("订单延时消息创建成功,{},过期时间: {}", orderMsg, expireTime);
        }
    }

    /**
     * 消费订单延时队列
     */
    @PostConstruct
    public void consumeOrderQueue() {
        log.info("订单延时队列扫描已启动.....");
        ThreadUtil.newSingleExecutor().execute(() -> {
            while (true) {
                Set<String> set = redisTemplate.opsForZSet().rangeByScore(QUEUE_NAME_ORDER, 0,
                        System.currentTimeMillis(), 0L, 1L);
                // 如果没有需要消费的消息,则间隔一段时间再扫描
                if (CollUtil.isEmpty(set)) {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                String orderMsgStr = set.iterator().next();
                // TODO 将 orderMsgStr 转化为 orderMsg 对象
//                JSONUtil.toBean(orderMsgStr, OrderMsgObject.class);

                boolean deleteFlag = redisTemplate.opsForZSet().remove(QUEUE_NAME_ORDER, orderMsgStr) > 0;
                if (deleteFlag) {
                    // TODO 消费订单消息

                    log.info("订单延时消息已成功消费,{}", orderMsgStr);
                }
            }
        });

    }



}
3.3 使用示例(Controller)
./demo-web/src/main/java/com/ljq/demo/springboot/web/controller/OrderController.java
package com.ljq.demo.springboot.web.controller;

import com.ljq.demo.springboot.baseweb.api.ApiResult;
import com.ljq.demo.springboot.baseweb.util.RedisDelayQueueUtil;
import com.ljq.demo.springboot.vo.order.OrderDelayCreateParam;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description: 订单控制层
 * @Author: junqiang.lu
 * @Date: 2021/10/14
 */
@Slf4j
@RestController
@RequestMapping(value = "/api/order")
@Api(value = "订单控制层", tags = "订单控制层")
public class OrderController {

    @Autowired
    private RedisDelayQueueUtil redisDelayQueueUtil;

    /**
     * 创建延时订单
     *
     * @param orderDelayCreateParam
     * @return
     */
    @PostMapping(value = "/delay")
    @ApiOperation(value = "创建延时订单", notes = "创建延时订单")
    public ResponseEntity<ApiResult<Void>> createDelayOrder(@RequestBody @Validated OrderDelayCreateParam
                                                                        orderDelayCreateParam) {
        redisDelayQueueUtil.setOrderDelayTask(orderDelayCreateParam, RedisDelayQueueUtil.QUEUE_DELAY_TIME_ORDER);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        return new ResponseEntity<>(ApiResult.success(), headers, HttpStatus.OK);
    }
}
3.4 其他相关类(请求参数)
./demo-model/src/main/java/com/ljq/demo/springboot/vo/order/OrderDelayCreateParam.java
package com.ljq.demo.springboot.vo.order;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.Pattern;
import java.io.Serializable;

/**
 * @Description: 创建延时订单
 * @Author: junqiang.lu
 * @Date: 2021/10/14
 */
@Data
@ApiModel(value = "创建延时订单", description = "创建延时订单")
public class OrderDelayCreateParam implements Serializable {

    private static final long serialVersionUID = 3692411340443934479L;

    /**
     * 订单编号
     */
    @Pattern(regexp = "^[a-zA-Z0-9]{5,64}$", message = "订单编号格式错误")
    @ApiModelProperty(value = "订单编号", name = "orderNo", required = true)
    private String orderNo;

}

4 测试

启动项目,发送请求参数

请求路径:

http://127.0.0.1:8088/api/order/delay

请求参数(body):

{
    "orderNo": "NO123760"
}

后台日志:

2021-10-18 14:27:25 | INFO  | http-nio-8088-exec-5 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 29| preHandle
2021-10-18 14:27:25 | INFO  | http-nio-8088-exec-5 | com.ljq.demo.springboot.baseweb.log.LogService 30| [LOG-REQUEST]
	requestIP: 127.0.0.1
	contentType:application/json
	requestUrl: http://127.0.0.1:8088/api/order/delay
	requestMethod: POST
	requestParams: {}
	requestBody: OrderDelayCreateParam(orderNo=NO123760)
2021-10-18 14:27:25 | INFO  | http-nio-8088-exec-5 | com.ljq.demo.springboot.web.acpect.LogAspect 66| [AOP-LOG-START]
	requestMark: 5abf7a2d-d855-4c2a-b063-f12d6865a2dc
	requestIP: 127.0.0.1
	contentType:application/json
	requestUrl: http://127.0.0.1:8088/api/order/delay
	requestMethod: POST
	requestParams: {"orderNo":"NO123760"}
	targetClassAndMethod: com.ljq.demo.springboot.web.controller.OrderController#createDelayOrder
2021-10-18 14:27:25 | INFO  | http-nio-8088-exec-5 | c.l.d.springboot.baseweb.util.RedisDelayQueueUtil 48| 订单延时消息创建成功,OrderDelayCreateParam(orderNo=NO123760),过期时间: 1634538475824
2021-10-18 14:27:25 | INFO  | http-nio-8088-exec-5 | com.ljq.demo.springboot.web.acpect.LogAspect 72| [AOP-LOG-END]
	requestMark: 5abf7a2d-d855-4c2a-b063-f12d6865a2dc
	requestUrl: http://127.0.0.1:8088/api/order/delay
	response: <200 OK,ApiResult(code=200, msg=成功, data=null, extraData=null, timestamp=1634538445839),{Content-Type=[application/json;charset=UTF-8]}>
2021-10-18 14:27:25 | INFO  | http-nio-8088-exec-5 | com.ljq.demo.springboot.baseweb.log.LogService 44| [LOG-RESPONSE]
	requestIp: 127.0.0.1
	requestUrl: http://127.0.0.1:8088/api/order/delay
	response: ApiResult(code=200, msg=成功, data=null, extraData=null, timestamp=1634538445839)
2021-10-18 14:27:25 | INFO  | http-nio-8088-exec-5 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 38| postHandle
2021-10-18 14:27:25 | INFO  | http-nio-8088-exec-5 | c.ljq.demo.springboot.web.acpect.SimpleInterceptor 44| afterCompletion
2021-10-18 14:27:56 | INFO  | pool-6-thread-1 | c.l.d.springboot.baseweb.util.RedisDelayQueueUtil 79| 订单延时消息已成功消费,{"orderNo":"NO123760"}

redis-delayed-message-queue

从日志可以看出消息被延时消费

5 推荐参考资料

Redis Zset实现延时队列

基于REDIS实现延时任务

Redis延时队列,这次彻底给你整明白了

6 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot可以使用Redis实现延时队列,可以通过以下步骤实现: 1. 在pom.xml中添加Redis和Jedis的依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> ``` 2. 创建一个RedisTemplate对象,用于操作Redis: ``` @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } ``` 3. 创建一个队列类,用于存储延时任务: ``` public class DelayedTaskQueue { private RedisTemplate<String, Object> redisTemplate; public DelayedTaskQueue(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } public void add(String key, Object value, long delay) { redisTemplate.opsForZSet().add(key, value, System.currentTimeMillis() + delay); } public void remove(String key, Object value) { redisTemplate.opsForZSet().remove(key, value); } public Set<Object> range(String key, long start, long end) { return redisTemplate.opsForZSet().range(key, start, end); } } ``` 4. 创建一个延时任务处理类: ``` @Component public class DelayedTaskProcessor { @Autowired private RedisTemplate<String, Object> redisTemplate; @Scheduled(fixedRate = 1000) public void process() { DelayedTaskQueue queue = new DelayedTaskQueue(redisTemplate); Set<Object> tasks = queue.range("delayed_queue", 0, System.currentTimeMillis()); for (Object task : tasks) { // 处理延时任务 queue.remove("delayed_queue", task); } } } ``` 5. 在需要添加延时任务的地方,调用DelayedTaskQueue的add方法: ``` @Autowired private RedisTemplate<String, Object> redisTemplate; public void addDelayedTask(Object task, long delay) { DelayedTaskQueue queue = new DelayedTaskQueue(redisTemplate); queue.add("delayed_queue", task, delay); } ``` 这样,就可以使用Redis实现延时队列了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值