為了維護消息的有效性,當消費消息時候處理失敗時候,不進行消費,需要我們根據業務區返回ACK,本項目我使用Redis和ack機制雙重保險,保障消息一定能夠正確的消費
首先,接著上部分內容,使用Topic,機制(不明白的,可以回顧上部分內容)
上部分內容,我們使用SpringBoot注解,去實現,但是控制權不完全賬務,當進行大規模項目時候,不太建議使用
@RabbitListener(queues = TopicRabbitConfig.USER_QUEUE)
@RabbitHandler
public void processUser(String message) {
threadPool.execute(new Runnable() {
@Override
public void run() {
logger.info("用戶側流水:{}",message);
}
});
}
根據源碼分析,當然這里不分析源碼,有興趣的可以多失敗幾次就ok明白了
在配置類中定義監聽器,監聽這個序列(
AcknowledgeMode.MANUAL
是必須的哦)
/**
* 接受消息的監聽,這個監聽客戶交易流水的消息
* 針對消費者配置
* @return
*/
@Bean
public SimpleMessageListenerContainer messageContainer1(ConnectionFactory connectionFactory, TransactionConsumeImpl transactionConsume) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queueMessage());
container.setExposeListenerChannel(true);
container.setMaxConcurrentConsumers(1);
container.setConcurrentConsumers(1);
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); //設置確認模式手工確認
container.setMessageListener(transactionConsume);
return container;
}
這個 TransactionConsumeImpl
要繼承ChannelAwareMessageListener
,主要說的手動返回ACK就是channel。調用
@Component
public class TransactionConsumeImpl implements ChannelAwareMessageListener {
private static final Logger logger = LoggerFactory.getLogger(TransactionConsumeImpl.class);
private static final Gson gson = new Gson();
@Autowired
JedisShardInfo jedisShardInfo;
@Autowired
ExecutorService threadPool;
@Autowired
BoluomeFlowService boluomeFlowService;
@Override
public void onMessage(Message message, Channel channel) throws Exception {
String boby = new String(message.getBody(), "utf-8");//轉換消息,我們是使用json數據格式
threadPool.execute(new Runnable() { //多線程處理
@Override
public void run() {
Jedis jedis = jedisShardInfo.createResource();
jedis.sadd(TopicRabbitConfig.TRANSACTION_QUEUE, boby);//添加到key為當前消息類型的集合里面,防止丟失消息
BoluomeFlow flow = gson.fromJson(boby, BoluomeFlow.class);
String json = gson.toJson(flow);
if (boluomeFlowService.insert(flow)) { //當添加成功時候返回成功
logger.info("客戶交易流水添加1條記錄:{}", json);
jedis.srem(TopicRabbitConfig.TRANSACTION_QUEUE, boby);//從當前消息類型集合中移除已經消費過的消息
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);//手工返回ACK,通知此消息已經爭取消費
} catch (IOException ie) {
logger.error("消費成功回調成功,io操作異常");
}
} else {
logger.info("客戶交易流水添加失敗記錄:{}", json);
}
}
});
}
}