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

代码有问题,请留言

相关推荐
_Evan_Yao18 小时前
当AI能写SQL时,数据库表设计反而成了最后一道护城河
数据库·人工智能·后端·sql
skiy19 小时前
Spring WebFlux:响应式编程
java·后端·spring
zb2006412019 小时前
springboot整合libreoffice(两种方式,使用本地和远程的libreoffice);docker中同时部署应用和libreoffice
spring boot·后端·docker
weixin_7042660519 小时前
SpringCloud Feign 声明式服务调用
后端·spring·spring cloud
浪客川19 小时前
【百例RUST - 011】简单键值对
开发语言·后端·rust
喜欢流萤吖~19 小时前
SpringBoot 性能优化实战
spring boot·后端·性能优化
常利兵19 小时前
Spring Boot 搭建邮件发送系统:开启你的邮件自动化之旅
spring boot·后端·自动化
彭于晏Yan19 小时前
Spring Boot 集成邮件服务实现发送邮件功能
java·spring boot·后端
浮尘笔记19 小时前
Java Snowy 框架生产环境安全部署全流程(服务器篇)
java·运维·服务器·开发语言·后端