消費者實現應用內分佈式事務

在《生產者實現應用內分佈式事務管理》中我們已經實現了消息生產與關係型數據庫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註解事務同步也生效。貌似這個沒有改動也不會不生效。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章