使用Redis防止重复发送RabbitMQ消息

问题

今天遇到一个问题,发送MQ消息的时候需要保证不会重复发送,注意不是可靠到达(可靠到达可以通过消息确认机制和回调接口保证),这里保证的是不会生产多条一样的消息。

方法

综合讨论下来决定使用Redis缓存来解决,因为相比于将记录插入数据库Redis更为高效和便捷。

检验是否已经发送

在发送消息之前根据相关信息组合成keyRedis中查找,找到后检测值是否为存在并且是否为设定的值,若存在且与设定的值一样,则返回false,说明该消息已经发送过了。

java 复制代码
    public boolean isSend(String messageType, Long bizId, int hashCode) {
        // 根据消息类型、业务id和哈希值组合成key
        String key = this.genKey(messageType, bizId, hashCode);
        Long value = super.get(key);

        if (value != null && value.equals(DEFAULT_VALUE)) {
            return false;
        }

        return true;
    }
   
   /**get方法*/
    public V get(K key) {
        if (key == null) {
            return null;
        } else {
            try {
                // 在key前添加前缀和名字,并将原来的key进行json序列化
                String realKey = this.genRealKey(key);
                String content = (String)this.redisTemplate.opsForValue().get(realKey);
                // 若get到的值不为null则进行json反序列化
                return content == null ? null : this.valueSerializer.deserialize(content);
            } catch (Exception e) {
                CACHE.error("", key.toString(), "", "0", e);
                return null;
            }
        }
    }

以上就是检验消息是否重复的方法,需要注意的是JSON序列化,因为Redis默认使用的是JDK序列化,这种序列化后的内容不仅多而且不易于阅读,因此将其改为Json序列化。

发送后添加缓存

在发送消息的时候会先在Redisput一个以相关信息组合为keyvalue为默认值的记录,过期时间为5min

java 复制代码
    public void sendMessage(String messageType, Long bizId, int hashCode) {
        super.put(genKey(messageType, bizId, hashCode), DEFAULT_VALUE);
    }
    
    /**put方法*/
    public void put(K key, V value) {
        try {
            if (key != null && null != value) {
                // 进行json序列化
                String content = this.valueSerializer.serialize(value);
                this.redisTemplate.opsForValue().set(this.genRealKey(key), content, this.expires, this.timeUnit);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

发送消息方法

最后的发送消息方法大致代码如下:

java 复制代码
    public void sendMQMessage(Long bizId, String messageTemplateCode, String msg, int msgHashCode, String exchange, String routingKey) {
        //加入缓存
        boolean send = true;
        //String messageType = MessageTypeUtil.getMessageType(messageTemplateCode);
        if (bizId != null) {
            // 检测是否已经发送
            send = sendMessageCache.isSend(messageTemplateCode, bizId, msgHashCode);
        }

        //发送mq消息
        if (send) {
            if (bizId != null) {
                // 加入缓存
                sendMessageCache.sendMessage(messageTemplateCode, bizId, msgHashCode);
            }
            // 发送消息
            messageSender.send(exchange, routingKey, msg);
        }
    }
相关推荐
李高钢3 分钟前
Python FastAPI 框架入门:从零搭建你的第一个高性能 API 服务
数据库·python·fastapi
shehuiyuelaiyuehao2 小时前
1计算机是如何工作的
java·计算机网络
yuzhiboyouye2 小时前
那xml对应的sql语法,列举一下
xml·数据库·sql
. . . . .2 小时前
乐观锁 vs 悲观锁
数据库·sql
letisgo52 小时前
JAVA 高级进阶07篇《@Transactional失效的8个场景:代理机制到传播行为》
java·面试·transactional·spring事务·aop动态代理
水域安全老周2 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
Leo.yuan2 小时前
2026年多数据库实时同步的四种架构方案及工具推荐
数据库·架构
三8443 小时前
Java 模板注入(FreeMarker) · 02 · 模板引擎与 FreeMarker 语法
java·开发语言·web安全·freemarker
hasty3 小时前
Origin 校验不是身份认证:MySQL MCP Server 漏洞给 AI 平台的警告
数据库·mysql·安全
暮雨哀尘3 小时前
Java 基础练习(一):创建类并调用对象
java·开发语言