消息幂等性处理实现
1. 基于数据库实现幂等性
@Service
@Slf4j
public class MessageProcessor {
@Autowired
private JdbcTemplate jdbcTemplate;
public void processMessageWithDB(String messageId, String content) {
String sql = "SELECT COUNT(*) FROM message_record WHERE message_id = ?";
int count = jdbcTemplate.queryForObject(sql, Integer.class, messageId);
if (count > 0) {
log.info("Message already processed, messageId: {}", messageId);
return;
}
try {
processBusinessLogic(content);
String insertSql = "INSERT INTO message_record (message_id, status, create_time) VALUES (?, ?, ?)";
jdbcTemplate.update(insertSql, messageId, "SUCCESS", new Date());
} catch (Exception e) {
String insertSql = "INSERT INTO message_record (message_id, status, create_time, error_msg) VALUES (?, ?, ?, ?)";
jdbcTemplate.update(insertSql, messageId, "FAILED", new Date(), e.getMessage());
throw e;
}
}
}
2. 基于Redis实现幂等性
@Service
@Slf4j
public class RedisMessageProcessor {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String MESSAGE_CONSUMED_KEY_PREFIX = "mq:consumed:";
private static final long EXPIRE_TIME = 24 * 60 * 60;
public void processMessageWithRedis(String messageId, String content) {
String consumeKey = MESSAGE_CONSUMED_KEY_PREFIX + messageId;
Boolean setSuccess = redisTemplate.opsForValue()
.setIfAbsent(consumeKey, "1", EXPIRE_TIME, TimeUnit.SECONDS);
if (Boolean.FALSE.equals(setSuccess)) {
log.info("Message already processed, messageId: {}", messageId);
return;
}
try {
processBusinessLogic(content);
} catch (Exception e) {
redisTemplate.delete(consumeKey);
throw e;
}
}
private void processBusinessLogic(String content) {
}
}
3. 基于业务唯一标识实现幂等性
@Service
public class BusinessIdempotentProcessor {
@Autowired
private OrderService orderService;
public void processOrder(OrderMessage orderMessage) {
String orderNo = orderMessage.getOrderNo();
if (orderService.isOrderExists(orderNo)) {
log.info("Order already exists, orderNo: {}", orderNo);
return;
}
try {
Order order = new Order();
order.setOrderNo(orderNo);
order.setAmount(orderMessage.getAmount());
order.setStatus(OrderStatus.CREATED);
orderService.createOrder(order);
} catch (DuplicateKeyException e) {
log.info("Order already processed, orderNo: {}", orderNo);
}
}
}
4. 分布式锁实现幂等性
@Service
public class DistributedLockProcessor {
@Autowired
private RedissonClient redissonClient;
public void processMessageWithLock(String messageId, String content) {
String lockKey = "mq:lock:" + messageId;
RLock lock = redissonClient.getLock(lockKey);
try {
boolean locked = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (!locked) {
log.warn("Failed to acquire lock for messageId: {}", messageId);
return;
}
if (isMessageProcessed(messageId)) {
return;
}
processBusinessLogic(content);
markMessageAsProcessed(messageId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Processing interrupted", e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
5. 幂等性处理最佳实践
- 选择合适的幂等性实现方案:
- 对于高并发场景,推荐使用Redis或分布式锁
- 对于对数据一致性要求高的场景,推荐使用数据库唯一索引
- 对于有业务唯一标识的场景,推荐使用业务唯一标识