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 2.x搭建多级缓存案例,结合Caffeine和Redis,可以提供更高效的缓存机制。下面是一个简单的示例: 1. 首先,确保在项目的pom.xml文件中添加以下依赖: ```xml <!-- Caffeine --> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.9.0</version> </dependency> <!-- Spring Boot Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 在application.properties文件中配置Redis连接信息: ```properties spring.redis.host=your_redis_host spring.redis.port=your_redis_port ``` 3. 创建一个缓存配置类,例如CacheConfig: ```java import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.github.benmanes.caffeine.cache.Caffeine; import java.util.concurrent.TimeUnit; @Configuration @EnableCaching public class CacheConfig extends CachingConfigurerSupport { @Bean @Override public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager("myCache"); cacheManager.setCaffeine(caffeineCacheBuilder()); return cacheManager; } Caffeine<Object, Object> caffeineCacheBuilder() { return Caffeine.newBuilder() .initialCapacity(100) .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .recordStats(); } } ``` 4. 创建一个Service类,例如DemoService,使用@Cacheable注解进行缓存: ```java import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class DemoService { @Cacheable(cacheNames = "myCache") public String getData(String key) { // 从数据库或其他数据源获取数据 return "Data for key: " + key; } } ``` 5. 在Controller中使用DemoService类: ```java import org.springframework.beans.factory.annotation.Autowired;

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值