實現持久訂閱消費者

在很多的業務場景下,如果有多個訂閱者,其不在線則會丟失生產者發出的消息,僅有在線狀態下方可接收到消息,那麼是否有方式可以實現離線訂閱消息的讀取呢,並保證可靠傳遞以及不重複消費呢,本章將通過持久訂閱者實現。

本章概要

1、定義持久訂閱消費者;
2、取消持久訂閱消費者;

定義持久訂閱消費者

1、首先關注一下幾個信息,在沒有定義持久訂閱者時
1.1、沒有任何訂閱者

1.2、持久訂閱記錄表,沒有訂閱記錄

1.3、消息表,沒有任何訂閱消息


2、實現持久化訂閱配置,紅色部分爲本次實現持久訂閱調整內容:
2.1、調整DefaultJmsListenerContainerFactory配置:
@Bean(name = { "jmsListenerContainerFactory4Topic" })
	public DefaultJmsListenerContainerFactory jmsListenerContainerFactory4Topic() {
		DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
		factory.setConnectionFactory(cachingConnectionFactory);
		
		factory.setPubSubDomain(true);
		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);
//		factory.setClientId("topicListener1");//無效,需要設置在ConnectionFactory
		//實現持久訂閱
		cachingConnectionFactory.setClientId("client_01");
		factory.setSubscriptionDurable(true);
		return factory;
	}
2.2、添加訂閱者描述
@JmsListener(destination = "my.queue",containerFactory="jmsListenerContainerFactory4Queue",subscription="sub_01")//ActiveMQ.DLQ
	public void receiveQueue(String text) throws Exception {
		System.out.println("消費者:來源於生產者的消息:"+text);
		this.jmsTopicMessagingTemplate.convertAndSend(this.topic, "生產者辛苦生產的發佈訂閱消息成果");
		System.out.println("生產者2:辛苦生產發佈訂閱消息成果");
		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("出現異常啦");
	}

3、啓動服務再次查看控制檯以及數據庫:
MQ控制檯

持久訂閱記錄表


4、此時我們啓動一個topic生產者,並不啓動消費者:
4.1、此時我們在消息記錄表中能夠看到生產了6個topic消息

4.2、上次監聽至503923

4.3、topic消息控制檯,已經被消費4個


5、此時我們啓動消費者監聽topic:
可以看到我們再次被消費了6個,累計被消費10個

本次消費最後一條記錄503929


總結:
a、通過實現可持久化訂閱者終於在數據庫表中存儲了兩種隊列信息,解開了之前爲何不存儲topic類型消息的原因;
b、如果存在多個持久化消費者,消息能夠可靠的被每一個消費者上線後消費,且不會重複消費,非常適合做廣播場景;

異常點:
1、此時在消息表中我們已經被消費的topic消息仍然存在,但過幾個小時後會自動刪除。

取消持久訂閱消費者

1、通過一段單元測試代碼實現:
@Test
public void testUnSub() throws JMSException{
jmsTopicTemplate.getConnectionFactory().createConnection().createSession(false, 1).unsubscribe("sub_01");
}
執行後沒有任何持久訂閱者


特別注意,sub_01爲之前@JmsListener(destination = "my.topic",containerFactory="jmsListenerContainerFactory4Topic",subscription="sub_01")屬性,我們需要取消監聽消費方可取消訂閱成功,否則將提示Durable consumer is in use異常。

附錄

1、啓動服務遇到如下異常:
com.atomikos.icatch.HeurHazardException: Heuristic Exception
at com.atomikos.datasource.xa.XAResourceTransaction.commit(XAResourceTransaction.java:707)
at com.atomikos.icatch.imp.CommitMessage.send(CommitMessage.java:72)
at com.atomikos.icatch.imp.PropagationMessage.submit(PropagationMessage.java:83)
at com.atomikos.icatch.imp.Propagator$PropagatorThread.run(Propagator.java:79)
at com.atomikos.icatch.imp.Propagator.submitPropagationMessage(Propagator.java:58)
at com.atomikos.icatch.imp.HeurHazardStateHandler.onTimeout(HeurHazardStateHandler.java:131)
at com.atomikos.icatch.imp.CoordinatorImp.alarm(CoordinatorImp.java:933)
at com.atomikos.timing.PooledAlarmTimer.notifyListeners(PooledAlarmTimer.java:112)
at com.atomikos.timing.PooledAlarmTimer.run(PooledAlarmTimer.java:99)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
則刪除工程下tmlog.lck and tmlog6.log文件即可。


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