在《生產者實現應用內分佈式事務管理》中我們已經實現了消息生產與關係型數據庫MySQL的應用內分佈式事務,結合上一章《消費者JmsListener應用源碼淺析》中對源碼的分析,本章將實現消費者實現應用內分佈式事務。
本章概要
1、基礎工程目錄;
2、新增事務相關代碼案例;
3、事務驗證;
實踐
1、首先看下本工程的目錄
紅色部分爲在《優化生產者連接工廠(帶有session緩存)》章節中已經實現部分,本章節不在重複說明,本次主要實現
JmsListenerContainerConfiguration、Consumer
2、在《消費者JmsListener應用源碼淺析》章節中已經說明,需要XA連接工廠實現JTA事務管理,故我們務必注意:
/**
* 注入MQ連接工廠
*/
@Autowired
// @Qualifier(value="jmsConnectionFactory")
@Qualifier(value="xaJmsConnectionFactory")
private ConnectionFactory connectionFactory;
/**
* 創建帶有緩衝session的連接工廠
* @return
*/
@Bean(name="cachingConnectionFactory")
@Primary
public CachingConnectionFactory getConnectionFactory(){
CachingConnectionFactory cachingConnectionFactory=new CachingConnectionFactory();
//目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory
cachingConnectionFactory.setTargetConnectionFactory(connectionFactory);
//Session緩存數量,這裏屬性也可以直接在這裏配置
cachingConnectionFactory.setSessionCacheSize(10);
return cachingConnectionFactory;
}
3、在JTATransactionConfig.java中已經實現了JtaTransactionManager註冊:
@Configuration
@EnableTransactionManagement
public class JTATransactionConfig {
......
@Bean(name = "transactionManagerJTA")
@DependsOn({ "userTransaction", "atomikosTransactionManager"})
public PlatformTransactionManager transactionManager() throws Throwable {
UserTransaction userTransaction = userTransaction();
AtomikosJtaPlatform.transaction = userTransaction;
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}
4、本次的消息消費依舊採用@JmsListener註解異步消費,故我們的實現將基於自定義DefaultJmsListenerContainerFactory的bean實例:
@Configuration
public class JmsListenerContainerConfiguration {
@Autowired
private CachingConnectionFactory cachingConnectionFactory;
private final ObjectProvider<DestinationResolver> destinationResolver;
private final JtaTransactionManager transactionManager;
private final ObjectProvider<MessageConverter> messageConverter;
private final JmsProperties jmsProperties;
JmsListenerContainerConfiguration(ObjectProvider<DestinationResolver> destinationResolver,
JtaTransactionManager transactionManager, ObjectProvider<MessageConverter> messageConverter,
JmsProperties jmsProperties) {
this.destinationResolver = destinationResolver;
this.transactionManager = transactionManager;
this.messageConverter = messageConverter;
this.jmsProperties = jmsProperties;
}
@Bean(name = { "jmsListenerContainerFactory" })
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(cachingConnectionFactory);
factory.setPubSubDomain(jmsProperties.isPubSubDomain());
if (this.transactionManager != null) {
factory.setTransactionManager(transactionManager);
} else {
factory.setSessionTransacted(Boolean.valueOf(true));
}
JmsProperties.Listener listener = jmsProperties.getListener();
factory.setAutoStartup(listener.isAutoStartup());
if (listener.getAcknowledgeMode() != null) {
factory.setSessionAcknowledgeMode(Integer.valueOf(listener.getAcknowledgeMode().getMode()));
}
String concurrency = listener.formatConcurrency();
if (concurrency != null)
factory.setConcurrency(concurrency);
return factory;
}
}
5、實現一個消費者:
@Component
public class Consumer {
@Autowired
@Qualifier(value="jmsQueueMessagingTemplate")
private JmsMessagingTemplate jmsQueueMessagingTemplate;
@Autowired
@Qualifier("queue")
private Queue queue;
@Autowired
private UserRepository userRepository;
@JmsListener(destination = "my.consuqueue",containerFactory="jmsListenerContainerFactory")//ActiveMQ.DLQ
public void receiveQueue(String text) throws Exception {
System.out.println("消費者:來源於生產者2的消息:"+text);
this.jmsQueueMessagingTemplate.convertAndSend(this.queue, "生產者辛苦生產的點對點消息成果");
System.out.println("生產者:辛苦生產的點對點消息成果");
List<User> list=new ArrayList<User>(10);
list.add(new User("111","111"));
list.add(new User("12211","222"));
list.add(new User("333","333"));
for(User user:list){
userRepository.save(user);
}
throw new Exception("出現異常啦");
}
}
預期:存在異常的情況下,my.consuqueue隊列中消息不會被消費,my.queue隊列在broker中不會生產新的消息,並且數據庫中不會新增用戶數據;無異常情況則my.consuqueue隊列被全部消費,並且生產同等數量的my.queue隊列消息,MySQL數據庫中新增3被數量的用戶;
5.1、首先查看目前的消息數據情況:
用戶:
持久化的queue消息
5.2、驗證存在異常執行後的結果:
用戶
持久化的queue消息
5.3、無異常情況下 ,考慮到目前my.consuqueue僅有一個數據,爲了更好的驗證效果,我們本次監聽ActiveMQ.DLQ隊列:
持久化的消息,新增3個queue消息
用戶,新增9個用戶數據
總結,消費者應用內分佈式事務驗證成功。同時簡單的跑了一下《生產者實現應用內分佈式事務管理》的單元測試,@Transactional註解事務同步也生效。貌似這個沒有改動也不會不生效。