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();
        }
    }
}

代码有问题,请留言

相关推荐
小码哥_常5 小时前
从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代
后端
lolo大魔王5 小时前
Go语言的异常处理
开发语言·后端·golang
IT_陈寒7 小时前
Python多进程共享变量那个坑,我差点没爬出来
前端·人工智能·后端
码事漫谈7 小时前
2026软考高级·系统架构设计师备考指南
后端
AI茶水间管理员9 小时前
如何让LLM稳定输出 JSON 格式结果?
前端·人工智能·后端
其实是白羊9 小时前
我用 Vibe Coding 搓了一个 IDEA 插件,复制URI 再也不用手动拼了
后端·intellij idea
用户8356290780519 小时前
Python 操作 Word 文档节与页面设置
后端·python
酒後少女的夢9 小时前
设计模式教程
后端·架构
凌览10 小时前
别再手搓 Skill 了,用这个工具 5 分钟搞定
前端·后端