一、背景

1.什么是消息中间件?就是客户端与服务器端进行异步通讯。

2.生产发送几万个消息到消息中间件,消息中间件不会宕机掉的,因为这些数据会在队列中存储。

二、ActiveMQ持久化机制

1.消息中间件的持久化机制,默认消息中间件是没有持久化的,消息中间件在高可用的场景下,如果消息中间件出现宕机的时候,在生产者一方开启持久化机制,就会把数据存储在磁盘中。

2.贴测试代码

2.1.项目结构图

activemq断开后重连消费端 activemq长连接_mq的基础

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,结果:消息还是在,这就是在生产者做了持久化操作。

activemq断开后重连消费端 activemq长连接_activemq断开后重连消费端_02

解决的问题就是为了解决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):

activemq断开后重连消费端 activemq长连接_持久化_03

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):

activemq断开后重连消费端 activemq长连接_mq的基础_04

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,这样就完成了手动签收)

activemq断开后重连消费端 activemq长连接_System_05

五、springboot整合activemq

1.项目结构图

activemq断开后重连消费端 activemq长连接_activemq断开后重连消费端_06

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.启动项目

activemq断开后重连消费端 activemq长连接_spring_07

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---测试消息

六、结束