SpringBoot结合ActiveMQ(同时支持Queue和Topic)
在网上找了很多文章,一大堆,可直接运行的不多,特别是对于配置同时支持Queue和Topic两种方式的,也没有一个可以直接运行。最后不得以,慢慢琢磨,成功之后,就在这里分享一下经验。
在项目实际使用过程中,持久化队列用处不多,有持久化需求的,也基本是持久化到数据库的。最后也分享一个目前项目中关于记账服务使用activemq来异步记账的案例。
在SpringBoot中使用ActiveMQ
//MQ configuration class
@Configuration
public class MqConfig {
@Bean
public Queue queue(){
return new ActiveMQQueue("mvp.queue");
}
@Bean
public Topic topic(){
return new ActiveMQTopic("mvp.topic");
}
@Bean
public ActiveMQConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory("admin", "admin", "tcp://localhost:61616");
}
@Bean
public JmsListenerContainerFactory<?> jmsListenerContainerTopic(ActiveMQConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
bean.setPubSubDomain(true);
bean.setConnectionFactory(connectionFactory);
return bean;
}
@Bean
public JmsListenerContainerFactory<?> jmsListenerContainerQueue(ActiveMQConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
bean.setConnectionFactory(connectionFactory);
return bean;
}
@Bean
public JmsMessagingTemplate jmsMessagingTemplate(ActiveMQConnectionFactory connectionFactory){
return new JmsMessagingTemplate(connectionFactory);
}
}
@Component
@EnableScheduling
public class producer {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Queue queue;
@Autowired
private Topic topic;
private static int count= 0;
@Scheduled(fixedDelay=3000)
public void send(){
this.jmsMessagingTemplate.convertAndSend(this.queue,"hi.activeMQ,index="+count);
this.jmsMessagingTemplate.convertAndSend(this.topic,"hi,activeMQ( topic ),index="+count++);
}
}
@Component
public class consumerqueue {
@JmsListener(destination = "mvp.topic",containerFactory="jmsListenerContainerTopic")
public void receiveTopic(String text){
System.out.println("Topic Consumer1:"+text);
}
@JmsListener(destination = "mvp.topic",containerFactory="jmsListenerContainerTopic")
public void receiveTopic2(String text){
System.out.println("Topic Consumer2:"+text);
}
@JmsListener(destination = "mvp.queue",containerFactory="jmsListenerContainerQueue")
public void reviceQueue(String text){
System.out.println("Queue Consumer:"+text);
}
}
应用启动后的输出
Queue Consumer:hi.activeMQ,index=0
Topic Consumer1:hi,activeMQ( topic ),index=0
Topic Consumer2:hi,activeMQ( topic ),index=0
Queue Consumer:hi.activeMQ,index=1
Topic Consumer1:hi,activeMQ( topic ),index=1
Topic Consumer2:hi,activeMQ( topic ),index=1
案例1-记账模块MQ的使用
在跨境系统的记账模块中,记账分为两部分,这两部分内容基本一致,分为同步记账和异步记账。在同步记账完成后,会发送MQ消息去异步记账,而记账模块也会起一个任务去查询哪些因为记账服务中断没有执行记账的记录。
数据表T_BALANCE_DEAL的CHARGE_STATUS有4种状态:0-未记账,未发MQ,3-未记账,已发MQ ,1-记账成功,2-记账失败。定时任务会扫描状态为0-未记账未发MQ 和 三小时前的3-未记账已发MQ的记录,扫到后,重新发送MQ消息到队列。
记账模块有一个监听器来监听MQ记账队列,如果有消息,就执行记账。这样设计就比较清晰,记账入口有2个,一个是其他服务发送的记账请求,一个是定时任务发送的记账请求。
系统比较老旧,还是老式的xml配置。关键配置如下:
<!--记账定时任务扫描-->
<bean id="mdbtask-chargeupTaskSendMsg" class="com.ttf.ma.task.impl.ChargeupTaskSendMsgImpl"
parent="mdbtask-abstractChargeupTask">
<property name="jmsSender" ref="jmsSender" />
</bean>
//查询没有发mq交易信息
List<BalanceDealDto> balanceDealDtos = this.queryBalanceChargeupWithEnum(ChargeUpStatusEnum.CHARGEUP_NO_SEND_MQ);
//查询3个小时没有记账交易信息进行记账
List<BalanceDealDto> lists =this.queryBalanceChargeupWithEnum(ChargeUpStatusEnum.CHARGEUP_SEND_MQ);
<!--MQ监听-->
<bean id="mdbtask-chargeUpMessageListener" class="com.ttf.ma.mdb.impl.CommonChargeupListenerImpl"
parent="mdbtask-abstractChargeupListener">
<property name="balanceDealService" ref="mdbtask-balanceDealService" />
<property name="peServiceFacade" ref="mdbtask-peServiceFacade" />
</bean>
案列2-MQ外部消息推送
跨境系统向外部发送消息提供了三种方式HTTP,SMS,EMAIL.第一种通常用于接口回调,后面两种用于商户通知,其中会涉及消息模板的使用,这个可以另起一篇文章来介绍。