一、背景
1.什么是消息中间件?就是客户端与服务器端进行异步通讯。
2.生产发送几万个消息到消息中间件,消息中间件不会宕机掉的,因为这些数据会在队列中存储。
二、ActiveMQ持久化机制
1.消息中间件的持久化机制,默认消息中间件是没有持久化的,消息中间件在高可用的场景下,如果消息中间件出现宕机的时候,在生产者一方开启持久化机制,就会把数据存储在磁盘中。
2.贴测试代码
2.1.项目结构图
2.2.Producer.java(生产者代码,肯定是在生产者代码中设置持久化)
public class Producer {
public static void main(String[] args) throws JMSException {
// 获取mq连接工程
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
// 创建连接
Connection createConnection = connectionFactory.createConnection();
// 启动连接
createConnection.start();
// 创建会话工厂
Session session = createConnection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("cmx_queue");
MessageProducer producer = session.createProducer(destination);
// DeliveryMode.PERSISTENT 设置持久化
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
for (int i = 1; i <= 5; i++) {
System.out.println("我是生产者: " + i);
sendMsg(session, producer, "我是生产者: " + i);
}
System.out.println("生产者 发送消息完毕!!!");
}
public static void sendMsg(Session session, MessageProducer producer, String i) throws JMSException {
TextMessage textMessage = session.createTextMessage("hello activemq " + i);
producer.send(textMessage);
}
}
注意:当生产者发送消息到mq中后,停掉生产者,没有关闭mq的后台,也没有设置持久化的操作的时候,数据还是存在mq的内存中的,当设置好持久化的时候之后,停掉mq的时候这时候设置的持久化才会起到作用,把数据缓存起来。
2.3.consumer.java
public class Consumer {
public static void main(String[] args) throws JMSException {
// 获取mq连接工程
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
// 创建连接
Connection createConnection = connectionFactory.createConnection();
// 启动连接
createConnection.start();
// 创建会话工厂
Session session = createConnection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("cmx_queue");
MessageConsumer createConsumer = session.createConsumer(destination);
while (true) {
//监听消息
TextMessage textMessage =(TextMessage) createConsumer.receive();
if(textMessage!=null){
String text= textMessage.getText();
System.out.println("消费者 获取到消息: text:"+text);
}else{
break;
}
}
System.out.println("消费者消费消息完毕!!!");
}
}
2.4.测试结果(持久化的数据位置),启动顺序:启动mq-->启动生产者-->关闭生产者-->关闭mq-->再次启动mq,结果:消息还是在,这就是在生产者做了持久化操作。
解决的问题就是为了解决mq宕机的时候数据丢失的问题。
四、JMS可靠消息
1.activemq的话
1.1.自动签收,生产者把消息给队列之后,只要存在消费者队列中的数据直接签收到消费者,不管消费者消费成功与否,这种方式不太好,一般不使用。
上面的代码就是自动签收的写法,不详细贴代码了。
1.2.事物消息,生产者完成发送消息后,必须提交事务给队列,不然在队列中是没有生产者生产的消息的。
消费者获取事物消息,如果消费没有提交事务,默认表示没有进行消费,默认自动重试机制。
1.2.1.Producer.java
public class Producer {
public static void main(String[] args) throws JMSException {
// 获取mq连接工程
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
// 创建连接
Connection createConnection = connectionFactory.createConnection();
// 启动连接
createConnection.start();
// 创建会话工厂 Boolean.FALSE, Session.AUTO_ACKNOWLEDGE 开启事物自动签收
Session session = createConnection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("cmx_queue");
MessageProducer producer = session.createProducer(destination);
// DeliveryMode.PERSISTENT 设置持久化
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
for (int i = 1; i <= 5; i++) {
System.out.println("我是生产者: " + i);
sendMsg(session, producer, "我是生产者: " + i);
//提交事务
session.commit();
}
System.out.println("生产者 发送消息完毕!!!");
}
public static void sendMsg(Session session, MessageProducer producer, String i) throws JMSException {
TextMessage textMessage = session.createTextMessage("hello activemq " + i);
producer.send(textMessage);
}
}
mq(不提交事务是0):
1.2.2.Consumer.java
public class Consumer {
public static void main(String[] args) throws JMSException {
// 获取mq连接工程
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
// 创建连接
Connection createConnection = connectionFactory.createConnection();
// 启动连接
createConnection.start();
// 创建会话工厂 Boolean.FALSE, Session.AUTO_ACKNOWLEDGE 开启事物自动签收
Session session = createConnection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("cmx_queue");
MessageConsumer createConsumer = session.createConsumer(destination);
while (true) {
//监听消息
TextMessage textMessage =(TextMessage) createConsumer.receive();
if(textMessage!=null){
String text= textMessage.getText();
System.out.println("消费者 获取到消息: text:"+text);
session.commit();
}else{
break;
}
}
System.out.println("消费者消费消息完毕!!!");
}
}
mq(不提交事务还是5):
1.3.消费者没有手动签收消息,默认表示没有进行消费。代码例子如下:
1.3.1.Producer.java
public class Producer {
public static void main(String[] args) throws JMSException {
// 获取mq连接工程
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
// 创建连接
Connection createConnection = connectionFactory.createConnection();
// 启动连接
createConnection.start();
// 创建会话工厂 Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE 不开启事物手动签收
Session session = createConnection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("cmx_queue");
MessageProducer producer = session.createProducer(destination);
// DeliveryMode.PERSISTENT 设置持久化
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
for (int i = 1; i <= 5; i++) {
System.out.println("我是生产者: " + i);
sendMsg(session, producer, "我是生产者: " + i);
}
System.out.println("生产者 发送消息完毕!!!");
}
public static void sendMsg(Session session, MessageProducer producer, String i) throws JMSException {
TextMessage textMessage = session.createTextMessage("hello activemq " + i);
producer.send(textMessage);
}
}
1.3.2.Consumer.java
public class Consumer {
public static void main(String[] args) throws JMSException {
// 获取mq连接工程
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
// 创建连接
Connection createConnection = connectionFactory.createConnection();
// 启动连接
createConnection.start();
// 创建会话工厂 Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE 不开启事物手动签收
Session session = createConnection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("cmx_queue");
MessageConsumer createConsumer = session.createConsumer(destination);
while (true) {
//监听消息
TextMessage textMessage =(TextMessage) createConsumer.receive();
if(textMessage!=null){
String text= textMessage.getText();
System.out.println("消费者 获取到消息: text:"+text);
//手动签收消息
textMessage.acknowledge();
}else{
break;
}
}
System.out.println("消费者消费消息完毕!!!");
}
}
1.3.3.测试结果(如果不在Consumer.java中设置textMessage.acknowledge(),能消费到数据但是下图的红框中的数据还是没变化,只有加上那句话之后,才为0,这样就完成了手动签收)
五、springboot整合activemq
1.项目结构图
2.springboot-producer项目代码
2.1.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ysfj</groupId>
<artifactId>springboot-producer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-producer</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring boot web支持:mvc,aop... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2.application.yml
spring:
activemq:
broker-url: tcp://127.0.0.1:61616
user: admin
password: admin
queue: springboot-cmx-queue
server:
port: 8081
2.3.QueueConfig.java
@Configuration
public class QueueConfig {
@Value("${queue}")
private String queue;
@Bean
public Queue logQueue() {
return new ActiveMQQueue(queue);
}
@Bean
public JmsTemplate jmsTemplate(ActiveMQConnectionFactory activeMQConnectionFactory, Queue queue) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDeliveryMode(2);// 进行持久化配置 1表示非持久化,2表示持久化</span>
jmsTemplate.setConnectionFactory(activeMQConnectionFactory);
jmsTemplate.setDefaultDestination(queue); // 此处可不设置默认,在发送消息时也可设置队列
jmsTemplate.setSessionAcknowledgeMode(4);// 客户端签收模式</span>
return jmsTemplate;
}
// 定义一个消息监听器连接工厂,这里定义的是点对点模式的监听器连接工厂
@Bean(name = "jmsQueueListener")
public DefaultJmsListenerContainerFactory jmsQueueListenerContainerFactory(
ActiveMQConnectionFactory activeMQConnectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(activeMQConnectionFactory);
// 设置连接数
factory.setConcurrency("1-10");
// 重连间隔时间
factory.setRecoveryInterval(1000L);
factory.setSessionAcknowledgeMode(4);
return factory;
}
}
2.4.Producer
@SpringBootApplication
@Component
@EnableScheduling
public class Producer {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Queue queue;
@Scheduled(fixedDelay = 3000)
public void send() {
String reuslt = System.currentTimeMillis() + "---测试消息";
System.out.println("reuslt:" + reuslt);
jmsMessagingTemplate.convertAndSend(queue, reuslt);
}
public static void main(String[] args) {
SpringApplication.run(Producer.class, args);
}
}
2.5.启动项目
3.springboot-consumer项目
3.1.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ysfj</groupId>
<artifactId>springboot-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring boot web支持:mvc,aop... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2.application.yml
spring:
activemq:
broker-url: tcp://127.0.0.1:61616
user: admin
password: admin
queue: springboot-cmx-queue
server:
port: 8082
3.3.Consumer.java
@SpringBootApplication
@Component
public class Consumer {
@JmsListener(destination = "${queue}")
public void receive(String msg) {
System.out.println("消费端接收到生产者消息:"+msg);
}
public static void main(String[] args) {
SpringApplication.run(Consumer.class, args);
}
}
3.4.测试结果
消费端接收到生产者消息:1559488051605---测试消息
2019-06-02 23:11:32.321 INFO 4796 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8082 (http)
2019-06-02 23:11:32.327 INFO 4796 --- [ main] com.ysfj.springbootconsumer.Consumer : Started Consumer in 2.159 seconds (JVM running for 2.835)
消费端接收到生产者消息:1559488055100---测试消息
消费端接收到生产者消息:1559488058237---测试消息
消费端接收到生产者消息:1559488061291---测试消息
消费端接收到生产者消息:1559488064417---测试消息
消费端接收到生产者消息:1559488067547---测试消息
消费端接收到生产者消息:1559488070612---测试消息
消费端接收到生产者消息:1559488073752---测试消息
消费端接收到生产者消息:1559488076883---测试消息
消费端接收到生产者消息:1559488080025---测试消息
消费端接收到生产者消息:1559488083164---测试消息
消费端接收到生产者消息:1559488086220---测试消息
消费端接收到生产者消息:1559488089363---测试消息
消费端接收到生产者消息:1559488092511---测试消息
六、结束