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

代码有问题,请留言

相关推荐
程序猿DD1 小时前
OctaFuse Gateway 2.16.0:新增 ElevenLabs 的支持、厂商原生端点重构、后台管理优化
后端
掘金者阿豪1 小时前
列存储适不适合订单报表?从一条慢 SQL 聊聊金仓 KES 的使用边界
后端
武子康2 小时前
150毫秒和2400毫秒:同一 Agent 任务的两种延迟
人工智能·后端·agent
海边捡石子2 小时前
FreeSWITCH 入门:把电话系统接入语音客服机器人
人工智能·后端
武子康2 小时前
Agent 偏好记忆怎样确认写入:范围、版本与撤销的一个最小例
人工智能·后端·agent
Bazingga3 小时前
Java16 record 完整拆解——语法、语义、限制、反射
java·后端
对象存储与RustFS3 小时前
网盘限速逼你开会员?用 RustFS 自己搭一个不限速的“私人网盘
服务器·后端·docker
Thneonl3 小时前
你写的 NetworkPolicy 可能一条都没生效
后端·架构
王中阳Go3 小时前
质保期第 90 天我翻了一遍日志目录:交付后真正会坏的 3 处
人工智能·后端·agent
卷无止境3 小时前
一人公司的全栈选型指南:让 AI Agent 写得快,也让生意跑得稳
后端·python