AWS SQS发送消息后如何确认消息因重复被服务端忽略
SQS FIFO队列重复去重状态获取方案
QueueMessagingTemplate自带的MessagePostProcessor无法拿到去重判定状态是正常机制:PostProcessor的执行时机是本地消息构造完成、请求发往服务端之前,仅能修改待发送的本地消息属性,完全接触不到AWS SQS返回的服务端响应,自然读不到去重相关标识。
SQS服务端的去重响应规则
- 无论消息是正常入队,还是因重复
message-deduplication-id被去重丢弃,SQS的SendMessage接口都会返回HTTP 200成功状态码,不会抛出异常,无法通过异常捕获识别去重状态 - 两种场景的唯一区分标识是响应中的
SequenceNumber字段:- 新消息首次成功入队时,SQS会为该消息分配全新的唯一序列号返回
- 重复消息被去重忽略时,SQS不会生成新序列号,直接返回第一次携带相同deduplication ID入队的原始消息对应的序列号
可行实现方式
1. 直接调用SQS原生客户端获取响应判断
放弃使用convertAndSend这类无返回值的封装方法,直接注入AWS SDK提供的SQS客户端构造发送请求,拿到响应结果后对比序列号判断状态,示例代码(AWS SDK v2):
// 本地缓存需要做TTL过期,匹配SQS默认5分钟的去重窗口时长 private final Cache<String, String> dedupSeqCache = CacheBuilder.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .build(); public SendResult sendMsg(Object msgObject, String messageGroup, String dedupId) throws JsonProcessingException { SendMessageRequest request = SendMessageRequest.builder() .queueUrl("你的队列URL") .messageBody(new ObjectMapper().writeValueAsString(msgObject)) .messageGroupId(messageGroup) .messageDeduplicationId(dedupId) .build(); SendMessageResponse response = sqsClient.sendMessage(request); String returnedSeq = response.sequenceNumber(); String cachedSeq = dedupSeqCache.getIfPresent(dedupId); if (cachedSeq != null && cachedSeq.equals(returnedSeq)) { // 命中重复去重逻辑:消息已被SQS忽略,未实际入队 return SendResult.DEDUP_IGNORED; } // 首次发送成功,缓存对应序列号 dedupSeqCache.put(dedupId, returnedSeq); return SendResult.ENQUEUED; }
注意:SQS默认的去重判定窗口为5分钟,相同deduplication ID超过窗口后发送会被识别为新消息、返回新的序列号,本地缓存必须配置相同窗口的过期时间,避免误判。
2. 扩展Spring Messaging模板适配返回值
如果需要继续沿用Spring的消息发送封装,不要用无返回值的convertAndSend方法,可自定义QueueMessagingTemplate子类,重写底层的doSend方法,在拿到SQS客户端返回的发送结果后,将序列号、去重判定结果写入消息Header,发送完成后即可从返回的Message对象中读取对应状态,核心判断逻辑和原生客户端方案一致。
内容的提问来源于stack exchange,提问作者Do Will
相关产品推荐
相关产品推荐

