Java框架-如何利用Redisson实现延时队列

在分布式系统中,延时队列是一种常见的需求。它允许我们在指定的时间后执行某个操作,或者在指定的时间后发送一条消息。Redisson是一个在Redis上实现的Java客户端,它提供了丰富的分布式对象和服务,包括延时队列。

和之前讲过的事件实现一样,我们首先定义事件

java 复制代码
public class RedissonEvent<T extends BaseEvent> implements Serializable {

    /**
     * DATA
     */
    private T data;
    /**
     * TAG
     */
    private String tag;

    private String className;

    public RedissonEvent() {
    }

    public RedissonEvent(T data, String tag, String className) {
        this.data = data;
        this.tag = tag;
        this.className = className;
    }

    public RedissonEvent(T data, String tag) {
        this.data = data;
        this.tag = tag;
        this.className = data.getClass().getName();
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getClassName() {
        return className;
    }
}

发送者:

java 复制代码
@Slf4j
public class RedissonEventPublisher implements EventPublisher {

    private final RedissonClient redissonClient;


    public RedissonEventPublisher(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }


    /*
     **
     * @Date: 2024/1/15 18:09
     * @MethodName: sendMessage
     * @Description: 发送消息
     * @param: payload 消息体
     * @param: msgTag 消息的tag
     * @Return: boolean
     **/
    @Override
    public <T extends BaseEvent> boolean sendMessage(T message, String msgTag) {
        return sendMessage(message, msgTag, 0, TimeUnit.SECONDS);
    }

    @Override
    public <T extends BaseEvent> boolean sendMessage(T payload, String msgTag, long delayedTimes, TimeUnit timeUnit) {
        sendMessageByRedisson(payload, msgTag, delayedTimes, timeUnit);
        return true;
    }

    private <T extends BaseEvent> void sendMessageByRedisson(T message, String msgTag, long delayedTimes, TimeUnit timeUnit) {
        RedissonEvent<T> redissonEvent = new RedissonEvent<>(message, msgTag);
        log.info("往消息队列中添加Redission延迟消息:{}", message);
        RBlockingQueue<RedissonEvent<T>> blockingQueue = redissonClient.getBlockingQueue(RedissonMessageListener.queue + ":" + msgTag);
        RDelayedQueue<RedissonEvent<T>> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
        delayedQueue.offer(redissonEvent, delayedTimes, timeUnit);
    }

}

消费者:

java 复制代码
@Slf4j
public class RedissonMessageListener {

    public static String queue = "dragon:jjb-saas-common:redis:queue";

    private final RedissonClient redissonClient;

    public RedissonMessageListener(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public void init(String tag) {
        RBlockingQueue<RedissonEvent<?>> blockingQueue = redissonClient.getBlockingQueue(RedissonMessageListener.queue + ":" + tag);
        RDelayedQueue<RedissonEvent<?>>  delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
        ThreadUtil.execAsync(() -> {
            while (true) {
                try {
                    RedissonEvent<?> redissonEvent = blockingQueue.poll();
                    if (null != redissonEvent) {
                        onMessage(redissonEvent);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public void onMessage(RedissonEvent redissonEvent) {

        assert redissonEvent != null;
        ConsumerType msgProcessorType = ConsumerCache.getConsumer(redissonEvent.getTag());
        if (msgProcessorType == null) {
            log.warn("No message processor present for tag: " + redissonEvent.getTag());
            return;
        }
        BaseEvent baseMessage = redissonEvent.getData();
        TraceUtils.putSid(baseMessage.getSid());
        TraceUtils.putTid(baseMessage.getTid());
        AuthContext.set(baseMessage.getLoginUser());
        MessageConsumer<BaseEvent> messageConsumer = ApplicationContextHelper.getBean(msgProcessorType.getConsumerClass());

        try {
            //判断消息是否需要幂等消费
            if (baseMessage.isIdempotentEnabled()) {
                //计算key
                String key = DigestUtils.md5Hex(redissonEvent.getTag() + "#" + baseMessage.getMsgSerialNum()
                        + "#" + baseMessage + "#" + messageConsumer.getClass().getName());
                Boolean consumed = ApplicationContextHelper.getBean(RedisService.class).setNx(key, 1, 24, TimeUnit.HOURS);

                //判断消息是否已经被当前服务消费过,如果消费过,则忽略当前消息
                if (Boolean.FALSE.equals(consumed)) {
                    log.warn("repeat consume message:{}", baseMessage);
                    return;
                }
                try {
                    messageConsumer.consume(baseMessage);
                } catch (Throwable e) {
                    //执行错误,删除key,下次可以重复消费
                    ApplicationContextHelper.getBean(RedisService.class).delete(key);
                    throw e;
                }
            } else {
                messageConsumer.consume(baseMessage);
            }
        } catch (Throwable e) {
            throw e;
        } finally {
            TraceUtils.removeTid();
            TraceUtils.removeSid();
            AuthContext.remove();
        }
    }
}

代码有问题,请留言

相关推荐
crack_comet11 分钟前
Spring Boot 3.5.11 分离打包(无参数启动+Jar瘦身)完整配置文档
java·spring boot·后端·maven·intellij-idea·jar
echome88811 分钟前
Go 语言并发编程:Channel 与 Goroutine 的完美结合
开发语言·后端·golang
weixin_4080996715 分钟前
身份证正反面合并+识别OCR接口调用
java·人工智能·后端·python·ocr·api·身份证ocr
橘子编程25 分钟前
Django全栈开发终极指南
后端·python·django·npm·html·pandas·html5
Java成神之路-26 分钟前
Spring 注解开发进阶实战:Bean 生命周期、 依赖注入及Properties配置(Spring系列4)
java·后端·spring
牛奔27 分钟前
升级Go 版本,导致兼容性依赖编译错误排查并解决
开发语言·后端·golang
小邓的技术笔记30 分钟前
聊聊 ASP.NET Core 中间件和过滤器的区别
后端·中间件·asp.net
运维行者_30 分钟前
网络监控告警设置指南:如何配置智能告警规避“告警风暴”?
linux·运维·服务器·网络·后端
知识汲取者33 分钟前
初识 RuoYi-Vue
java·spring boot·后端·开源软件
独自破碎E1 小时前
Spring Boot + Vue 前后端联调踩坑记录
vue.js·spring boot·后端