1.项目依赖,pom.xml配置
<!--activemq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!--activemq连接池-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.14.5</version>
</dependency>
2.yml文件配置:
spring:
activemq:
user: admin
password: XXXXXX
broker-url: tcp://XXXXXXXXXXXXX:61616
pool:
enabled: true
max-connections: 10
3.对应的启动应用需要加注解@EnableJms
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jms.annotation.EnableJms;
@SpringBootApplication
@EnableJms
public class LenderApplication {
private final static Logger logger = LoggerFactory.getLogger(LenderApplication.class);
public static void main(String[] args) {
SpringApplication.run(LenderApplication.class, args);
logger.info("LenderApplication is success!");
}
}
4.对应的配置bean的创建
package com.stylefeng.guns.config.mq;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.RedeliveryPolicy;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import javax.jms.Destination;
@Configuration
public class ActiveMQConfig {
@Value("${loanTopic}")
private String loanTopic;
@Value("${repayTopic}")
private String repayTopic;
@Value("${spring.activemq.user}")
private String usrName;
@Value("${spring.activemq.password}")
private String password;
@Value("${spring.activemq.broker-url}")
private String brokerUrl;
@Bean
public Destination topic() {
return new ActiveMQTopic(loanTopic);
}
/*@Bean
public Queue queue(){
return new ActiveMQQueue(queueName);
}*/
@Bean
public Destination topic2() {
return new ActiveMQTopic(repayTopic);
}
@Bean
public RedeliveryPolicy redeliveryPolicy() {
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
//是否在每次尝试重新发送失败后,增长这个等待时间
redeliveryPolicy.setUseExponentialBackOff(true);
//重发次数,默认为6次 这里设置为10次
redeliveryPolicy.setMaximumRedeliveries(2);
//重发时间间隔,默认为1秒
redeliveryPolicy.setInitialRedeliveryDelay(1);
//第一次失败后重新发送之前等待500毫秒,第二次失败再等待500 * 2毫秒,这里的2就是value
redeliveryPolicy.setBackOffMultiplier(2);
//是否避免消息碰撞
redeliveryPolicy.setUseCollisionAvoidance(false);
//设置重发最大拖延时间-1 表示没有拖延只有UseExponentialBackOff(true)为true时生效
redeliveryPolicy.setMaximumRedeliveryDelay(-1);
return redeliveryPolicy;
}
@Bean
public ActiveMQConnectionFactory connectionFactory(RedeliveryPolicy redeliveryPolicy) {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(usrName, password, brokerUrl);
activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy);
activeMQConnectionFactory.setTrustAllPackages(true);
return activeMQConnectionFactory;
}
@Bean
public JmsTemplate jmsTemplate(ActiveMQConnectionFactory connectionFactory, Destination topic) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDeliveryMode(2);//进行持久化配置 1表示非持久化,2表示持久化
jmsTemplate.setConnectionFactory(connectionFactory);
// jmsTemplate.setDefaultDestination(queue); //此处可不设置默认,在发送消息时也可设置队列
jmsTemplate.setDefaultDestination(topic);
jmsTemplate.setSessionAcknowledgeMode(4);//客户端签收模式
// jmsTemplate.setDefaultDestinationName(loanTopic);
return jmsTemplate;
}
@Bean
public JmsTemplate jmsTemplate2(ActiveMQConnectionFactory connectionFactory, Destination topic2) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDeliveryMode(2);//进行持久化配置 1表示非持久化,2表示持久化
jmsTemplate.setConnectionFactory(connectionFactory);
// jmsTemplate.setDefaultDestination(queue); //此处可不设置默认,在发送消息时也可设置队列
jmsTemplate.setDefaultDestination(topic2);
jmsTemplate.setSessionAcknowledgeMode(4);//客户端签收模式
// jmsTemplate.setDefaultDestinationName(repayTopic);
return jmsTemplate;
}
//定义一个消息监听器连接工厂,这里定义的是点对点模式的监听器连接工厂
// @Bean(name = "jmsQueueListener")
public DefaultJmsListenerContainerFactory jmsQueueListenerContainerFactory(ActiveMQConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
//设置连接数
factory.setConcurrency("1-10");
//重连间隔时间
factory.setRecoveryInterval(1000L);
factory.setSessionAcknowledgeMode(4);
factory.setPubSubDomain(false);
return factory;
}
@Bean(name = "jmsTopicListener")
public JmsListenerContainerFactory<?> jmsTopicListenerContainerFactory(ActiveMQConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setSessionTransacted(true);
factory.setAutoStartup(true);
factory.setConnectionFactory(connectionFactory);
//设置连接数
factory.setConcurrency("1-10");
//重连间隔时间
factory.setRecoveryInterval(1000L);
//客户端签收模式
factory.setSessionAcknowledgeMode(4);
//使用监听模式
factory.setPubSubDomain(true);
//使用持久化
factory.setSubscriptionDurable(true);
//客户端ID,根据业务名称定义
factory.setClientId("lenderLoadClient");
return factory;
}
@Bean(name = "jmsTopicListener2")
public JmsListenerContainerFactory<?> jmsTopicListenerContainerFactory2(ActiveMQConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setSessionTransacted(true);
factory.setAutoStartup(true);
factory.setConnectionFactory(connectionFactory);
//设置连接数
factory.setConcurrency("1-10");
//重连间隔时间
factory.setRecoveryInterval(1000L);
factory.setSessionAcknowledgeMode(4);
factory.setPubSubDomain(true);
factory.setSubscriptionDurable(true);
factory.setClientId("lenderRepayClient");
return factory;
}
}
ps:这里面使用持久化的监听模式,因为业务需要创建了两个监听工厂!
activemq安全性配置需要
http://activemq.apache.org/objectmessage.html
activeMQConnectionFactory.setTrustAllPackages(true);
5.消费者消息监听
package com.stylefeng.guns.modular.message;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.stylefeng.guns.modular.orderinfo.service.IOrderInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
@EnableJms
@Component
@Slf4j
public class LoanMessageListener {
@Autowired
private IOrderInfoService orderInfoService;
@JmsListener(destination = "loan.success.topic", containerFactory = "jmsTopicListener")
public void onMessage(final TextMessage message, Session session) throws JMSException {
try {
log.info("=======接收到借款消息=={}", message);
if (message instanceof TextMessage) {
log.info("=======接收到借款消息內容=={}", message.getText());
String result = message.getText();
JSONObject jsonObject = JSON.parseObject(result);
Long borrowId = jsonObject.getLongValue("borrowId");
orderInfoService.updateOrderInfo(borrowId);
log.info("===========borrowId={}", borrowId);
message.acknowledge();
}
} catch (Exception e) {
session.recover();
}
}
}
7:一些说明总结
ps:使用了持久化订阅(消费在离线之后这点时间生产者产成的消息仍然可以在重新上线之后监听到)(防止消息丢失)
订阅模式消息能被多个监听的消息者获取到所有的消息:1:N(同时生产的所有消息都能被消费)(保证消息被消费者全部消费)
消费的重复消费问题:客户端去重
1.第一种,数据库保持数据的唯一性,如果往数据库里面新增一条数据可以根据id主键保证唯一性(单体应用)
2.分布式系统要消息的生产需要用到分布式ID,然后消费者可以把这个监听到的消费过的消息ID存入redis里面,再次监听消费时先查看redis里面是否存在消费过的消息ID。存在就不重复消费。