ActiveMQ入门教程(二)之队列模式(Queue)和主题模式(Topic)

ActiveMQ的架构

ActiveMQ主要涉及到5个方面:
1. 传输协议:消息之间的传递,无疑需要协议进行沟通,启动一个ActiveMQ打开了一个监听端口, ActiveMQ提供了广泛的连接模式,其中主要包括SSL、STOMP、XMPP;ActiveMQ默认的使用的协议是openWire,端口号:61616;
2. 消息域:ActiveMQ主要包含Point-to-Point (点对点),Publish/Subscribe Model (发布/订阅者),其中在Publich/Subscribe 模式下又有Nondurable subscription和durable subscription (持久化订阅)2种消息处理方式
3. 消息存储:在消息传递过程中,部分重要的消息可能需要存储到数据库或文件系统中,当中介崩溃时,信息不回丢失
4. Cluster  (集群): 最常见到 集群方式包括network of brokers和Master Slave;
5. Monitor (监控) :ActiveMQ一般由jmx来进行监控

默认配置下的ActiveMQ只适合学习代码而不适用于实际生产环境,ActiveMQ的性能需要通过配置挖掘,其性能提高包括代码级性能、规则性能、存储性能、网络性能以及多节点协同方法(集群方案),所以我们优化ActiveMQ的中心思路也是这样的:

1. 优化ActiveMQ单个节点的性能,包括NIO模型选择和存储选择。

2. 配置ActiveMQ的集群(ActiveMQ的高性能和高可用需要通过集群表现出来)。

ActiveMQ的通信方式

1. 队列模式(Queue)

点对点模式下一条消息将会发送给一个消息消费者,如果当前Queue没有消息消费者,消息将进行存储。

队列模式特点

  1. 客户端包括生产者和消费者
  2. 队列中的消息只能被一个消费者消费
  3. 消费者可以随时消费队列中的消息

创建过程

1.创建连接Connection
2.创建会话Session
3.通过Session来创建其它的(MessageProducer、MessageConsumer、Destination、TextMessage)
4.将生产者 MessageProducer 和消费者 MessageConsumer 都会指向目标 Destination
5.生产者向目标发送TextMessage消息send()
6.消费者设置监听器,监听消息。

代码实现

点对点的消息模型,只需要一个消息生成者和消息消费者,下面我们编写代码。

目录结构:

pom结构

<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>
  <groupId>com.loafer</groupId>
  <artifactId>ActiveMq01</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>ActiveMq01</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-all</artifactId>
      <version>5.11.4</version>
    </dependency>
  </dependencies>
</project>

编写生产者代码

package com.loafer.active;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;

public class TextProducer {
	
	/**
	 * 发送消息到ActiveMQ中,具体的消息内容为参数信息
	 * 开发JMS相关代码中,使用的接口类型都是javax.jms包下的类型
	 * @param datas 消息内容
	 */
	public void sendTextMessage(String datas){
		//连接工厂
		ConnectionFactory factory = null;
		//连接
		Connection connection = null;
		//目的地
		Destination destination = null;
		//会话
		Session session = null;
		//消息发送者
		MessageProducer producer = null;
		//消息对象
		Message message = null;
		
		try{
			//创建连接工厂,连接ActiveMQ服务的连接工厂
			//创建工厂,构造方法有三个参数,分别是用户名,密码,连接地址
			//无参构造,有默认的连接地址。本地连接
			//单参数构造,五无验证模式,没有用户的认证
			//三参数构造,有认证+指定地址。默认端口是61616.从ActiveMQ的conf/activemq.xml配置文件中查看
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.0.32:61616");
			//通过工厂,创建连接对象
			//创建连接的方法有重载,其中有createConnection(String username,String password)
			//可以再创建连接工厂时,只传递连接地址,不传递用户信息
			connection = factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接。消息的消费者必须启动连接
			//producer再发送消息的时候,会检查是否启动了连接,如果未启动,自动启动
			//如果有特殊的配置,建议配置完毕后再启动连接
			connection.start();
			//通过连接对象,创建会话对象
			/*创建会话的时候,必须传递两个参数,分别代表的是否支持事务和如何确认消息处理
			 * transacted:是否支持事务,数据类型是boolean.true-支持  false-不支持
			 * 		true:支持事务,第二个参数对producer来说默认是无效。建议传递的数据是Session.SESSION_TRANSACTED
			 * 		false:不支持事务,常用参数。第二个参数必须传递,且必须有效
			 * acknowledgeMode:如何确认消息的处理。使用确认机制实现的
			 * 		AUTO_ACKNOWLEDGE:自动确认消息。消息的消费者处理消息后,自动确认。常用。商业开发不推荐
			 * 		CLIENT_ACKNOWLEDGE:客户端手动确认。消息的消费者处理后,必须手工确认
			 * 		DUPS_OK_ACKNOWLEDGE:有副本的客户端手动确认
			 * 				一个消息可以多次处理
			 * 				可以降低Session的消耗,再可以容忍重复消息时使用(不推荐使用)
			 */
			session = connection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);
			//创建目的地。参数是目的地名称。是目的地的唯一标记
			destination = session.createQueue("first-mq");
			
			//通过会话对象,创建消息的发送者producer
			//创建的消息发送者,发送的消息一定到指定的目的地中
			//创建producer的时候,可以不提供目的地。在发送消息的时候制定目的地
			producer = session.createProducer(destination);
			//创建文本消息对象,作为具体数据内容的载体
			message = session.createTextMessage(datas);
			//使用producer,发送消息到ActiveMQ中的目的地。如果消息发送失败。抛出异常
			producer.send(message);
			
			System.out.println("消息已经发送成功....");
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(producer != null){//回收消息发送者
				try {
					producer.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null){//回收会话对象
				try {
					session.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null){//回收连接对象
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	
	public static void main(String[] args){
		TextProducer producer = new TextProducer();
		producer.sendTextMessage("我是一个测试的ActiveMQ...");
	}

}

运行生产者结果

  • 编写消费者
package com.loafer.active;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

public class TextConsumer {
	
	public String receiveTextMessage(){
		String resultCode = "";
		ConnectionFactory factory = null;
		Connection connection = null;
		Session session = null;
		Destination destination = null;
		//消息的消费者,用于接收消息的对象
		MessageConsumer consumer = null;
		TextMessage message = null;
		
		try{
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.0.32:61616");
			
			connection = factory.createConnection();
			//消息的消费者必须启动连接,否支无法处理消息
			connection.start();
			
			session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
			
			destination = session.createQueue("first-mq");
			//创建消息消费者对象,在制定的目的地中获取消息
			consumer = session.createConsumer(destination);
			//获取队列中的消息
			message = (TextMessage) consumer.receive();
			//处理文本消息
			resultCode = message.getText();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(consumer != null){//回收消息消费者
				try {
					consumer.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null){//回收会话对象
				try {
					session.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null){//回收连接对象
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
		return resultCode;
	}
	
	
	public static void main(String[] args){
		TextConsumer consumer = new TextConsumer();
		String messageString = consumer.receiveTextMessage();
		System.out.print("接收的消息内容是:" + messageString);
	}

}

运行消费者结果:

 

代码下载:https://github.com/loafer7423/ActiveMq/tree/master/ActiveMq01

消息队列监听demo

目录结构:

生产者代码

package com.loafer.producer;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;

import com.loafer.consumer.ConsumerListener;

public class ProducerSend {
	
	public void sendMessage(Object obj){
		ConnectionFactory factory = null;
		Connection connection = null;
		Session session = null;
		Destination destination = null;
		MessageProducer producer = null;
		Message message = null;
		try{
			factory = new ActiveMQConnectionFactory("guest","guest","tcp://192.168.0.32:61616");
			connection = factory.createConnection();
			connection.start();
			session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
			destination = session.createQueue("test-listener");
			producer = session.createProducer(destination);
			connection.start();
			for(int i=0;i<100;i++){
				message = session.createObjectMessage("我是第"+i+"个消息,消息内容是:"+obj);
				producer.send(message);
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(producer != null){//回收消息生产者
				try {
					producer.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null){//回收会话对象
				try {
					session.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null){//回收连接对象
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}


	public static void main(String[] args){
		ProducerSend producer = new ProducerSend();
		producer.sendMessage("this is message!!!");
				
	}
	

}

消费者代码:

package com.loafer.consumer;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;

import com.sun.xml.internal.org.jvnet.fastinfoset.VocabularyApplicationData;

/**
 * 使用监听器的方式,实现消息的处理【消费】
 * @author 王东
 *
 */
public class ConsumerListener {
	
	/**
	 * 处理消息
	 */
	public void consumMessage(){
		ConnectionFactory factory = null;
		Connection connection = null;
		Session session = null;
		Destination destination = null;
		MessageConsumer consumer = null;
		try{
			factory = new ActiveMQConnectionFactory("guest","guest","tcp://192.168.0.32:61616");
			connection = factory.createConnection();
			connection.start();
			session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
			destination = session.createQueue("test-listener");
			consumer = session.createConsumer(destination);
			//注册监听器。注册成功后,队列中的消息变化会自动触发监听器代码。接收消息并处理
			consumer.setMessageListener(new MessageListener(){
				/*
				 *监听器一旦注册,永久有效。
				 *永久-consumer线程不关闭
				 *处理消息的方式:只要有消息未处理,自动调用onMessage方法,处理消息
				 *监听器可以注册若干。注册多个监听器,相当于集群
				 *ActiveMQ自动的循环调用多个监听器,处理队列中的消息,实现并行处理
				 *
				 * 处理消息的方法。就是监听方法
				 * 监听的事件是:消息,消息未处理
				 * 要处理的具体内容:消息处理
				 * @param message-未处理的消息
				 * 
				 */
				public void onMessage(Message message) {
					try{
						ObjectMessage om = (ObjectMessage) message;
						Object data = om.getObject();
						System.out.println(data);
					}catch(Exception e){
						e.printStackTrace();
					}
				}
				
			});
			//阻塞当前代码。保证listener代码未结束。如果代码结束了,监听器自动关闭
			System.in.read();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(consumer != null){//回收消息消费者
				try {
					consumer.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null){//回收会话对象
				try {
					session.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null){//回收连接对象
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}


	public static void main(String[] args){
		ConsumerListener listener = new ConsumerListener();
		listener.consumMessage();
	}
	
}

依次运行生产者和消费者,最后消费者打印的结果:

代码下载:https://github.com/loafer7423/ActiveMq/tree/master/ActiveMq02

主题模式(Topic)

主题模式特点

  1. 客户端包括发布者和订阅者
  2. 主题中的消息被所有订阅者消费
  3. 消费者不能消费订阅之前就发送到主题中的消息

创建过程

1.创建连接Connection
2.创建会话Session
3.通过Session来创建其它的(MessageProducer、MessageConsumer、Destination、TextMessage)
4.将生产者 MessageProducer 和消费者 MessageConsumer 都会指向目标 Destination
5.生产者向目标发送TextMessage消息send()
6.消费者设置监听器,监听消息。

代码实现

目录结构:

pom.xml代码:

<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>
  <groupId>com.loafer</groupId>
  <artifactId>ActiveMq03</artifactId>
  <version>0.0.1-SNAPSHOT</version>
   <name>ActiveMq03</name>
  
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-all</artifactId>
      <version>5.11.4</version>
    </dependency>
  </dependencies>
</project>

生产者代码:

package com.loafer;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;


/**
 * 发送消息到ActiveMQ中,具体的消息内容为参数信息
 * 开发JMS相关代码中,使用的接口类型都是javax.jms包下的类型
 * @param datas 消息内容
 */
public class TopicProducer {
	
	/**
	 * 发送消息到ActiveMQ中,具体的消息内容为参数信息
	 * 开发JMS相关代码中,使用的接口类型都是javax.jms包下的类型
	 * @param datas 消息内容
	 */
	public void sendTextMessage(String datas){
		//连接工厂
		ConnectionFactory factory = null;
		//连接
		Connection connection = null;
		//目的地
		Destination destination = null;
		//会话
		Session session = null;
		//消息发送者
		MessageProducer producer = null;
		//消息对象
		Message message = null;
		
		try{
			//创建连接工厂,连接ActiveMQ服务的连接工厂
			//创建工厂,构造方法有三个参数,分别是用户名,密码,连接地址
			//无参构造,有默认的连接地址。本地连接
			//单参数构造,五无验证模式,没有用户的认证
			//三参数构造,有认证+指定地址。默认端口是61616.从ActiveMQ的conf/activemq.xml配置文件中查看
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.0.32:61616");
			//通过工厂,创建连接对象
			//创建连接的方法有重载,其中有createConnection(String username,String password)
			//可以再创建连接工厂时,只传递连接地址,不传递用户信息
			connection = factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接。消息的消费者必须启动连接
			//producer再发送消息的时候,会检查是否启动了连接,如果未启动,自动启动
			//如果有特殊的配置,建议配置完毕后再启动连接
			connection.start();
			//通过连接对象,创建会话对象
			/*创建会话的时候,必须传递两个参数,分别代表的是否支持事务和如何确认消息处理
			 * transacted:是否支持事务,数据类型是boolean.true-支持  false-不支持
			 * 		true:支持事务,第二个参数对producer来说默认是无效。建议传递的数据是Session.SESSION_TRANSACTED
			 * 		false:不支持事务,常用参数。第二个参数必须传递,且必须有效
			 * acknowledgeMode:如何确认消息的处理。使用确认机制实现的
			 * 		AUTO_ACKNOWLEDGE:自动确认消息。消息的消费者处理消息后,自动确认。常用。商业开发不推荐
			 * 		CLIENT_ACKNOWLEDGE:客户端手动确认。消息的消费者处理后,必须手工确认
			 * 		DUPS_OK_ACKNOWLEDGE:有副本的客户端手动确认
			 * 				一个消息可以多次处理
			 * 				可以降低Session的消耗,再可以容忍重复消息时使用(不推荐使用)
			 */
			session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
			//创建主题目的地。参数是目的地名称。是目的地的唯一标记
			
			destination = session.createTopic("topicDemo");
			//通过会话对象,创建消息的发送者producer
			//创建的消息发送者,发送的消息一定到指定的目的地中
			//创建producer的时候,可以不提供目的地。在发送消息的时候制定目的地
			producer = session.createProducer(destination);
			//创建文本消息对象,作为具体数据内容的载体
			message = session.createTextMessage(datas);
			//使用producer,发送消息到ActiveMQ中的目的地。如果消息发送失败。抛出异常
			producer.send(message);
			
			System.out.println("消息已经发送成功....");
			
			
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(producer != null){//回收消息发送者
				try {
					producer.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null){//回收会话对象
				try {
					session.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null){//回收连接对象
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	
	public static void main(String[] args){
		System.out.println("生产者程序开始启动.....");
		TopicProducer producer = new TopicProducer();
		producer.sendTextMessage("我是一个测试的ActiveMQ...");
		System.out.println("生产者程序已经关闭.....");
	}


}

消费者A代码:

package com.loafer;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;


public class TopicConsumer {
	public String receiveTextMessage(){
		String resultCode = "";
		ConnectionFactory factory = null;
		Connection connection = null;
		Session session = null;
		Destination destination = null;
		//消息的消费者,用于接收消息的对象
		MessageConsumer consumer = null;
		TextMessage message = null;
		
		try{
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.0.32:61616");
			
			connection = factory.createConnection();
			//消息的消费者必须启动连接,否支无法处理消息
			connection.start();
			
			session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
			
			destination = session.createTopic("topicDemo");
			
			//创建消息消费者对象,在制定的目的地中获取消息
			consumer = session.createConsumer(destination);
			
		    message = (TextMessage) consumer.receive();
		    resultCode = message.getText();
			
			
			/**消费者消息监听开始*/
//			consumer.setMessageListener(new MessageListener() {
//				
//				public void onMessage(Message message) {
//					try {
//	                    System.out.println("接收消息  = [" + ((TextMessage) message).getText() + "]");
//	                } catch (JMSException e) {
//	                    e.printStackTrace();
//	                }
//				}
//			});
			//阻塞当前代码。保证listener代码未结束。如果代码结束了,监听器自动关闭
//			System.in.read();
			/**消费者消息监听结束*/
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(consumer != null){//回收消息消费者
				try {
					consumer.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null){//回收会话对象
				try {
					session.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null){//回收连接对象
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
		return resultCode;
	}
	
	
	public static void main(String[] args){
		System.out.println("消费者程序开始启动.....");
		TopicConsumer consumer = new TopicConsumer();
		String messageString = consumer.receiveTextMessage();
		System.out.println("接收的消息内容是:" + messageString);
		System.out.println("消费者程序已经关闭.....");
	}
}

消费者B代码:

package com.loafer;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;


public class TopicConsumerB {
	public String receiveTextMessage(){
		String resultCode = "";
		ConnectionFactory factory = null;
		Connection connection = null;
		Session session = null;
		Destination destination = null;
		//消息的消费者,用于接收消息的对象
		MessageConsumer consumer = null;
		TextMessage message = null;
		
		try{
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.0.32:61616");
			
			connection = factory.createConnection();
			//消息的消费者必须启动连接,否支无法处理消息
			connection.start();
			
			session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
			
			destination = session.createTopic("topicDemo");
			
			//创建消息消费者对象,在制定的目的地中获取消息
			consumer = session.createConsumer(destination);
			
		    message = (TextMessage) consumer.receive();
		    resultCode = message.getText();
			
			
			/**消费者消息监听开始*/
//			consumer.setMessageListener(new MessageListener() {
//				
//				public void onMessage(Message message) {
//					try {
//	                    System.out.println("接收消息  = [" + ((TextMessage) message).getText() + "]");
//	                } catch (JMSException e) {
//	                    e.printStackTrace();
//	                }
//				}
//			});
			//阻塞当前代码。保证listener代码未结束。如果代码结束了,监听器自动关闭
//			System.in.read();
			/**消费者消息监听结束*/
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(consumer != null){//回收消息消费者
				try {
					consumer.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null){//回收会话对象
				try {
					session.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null){//回收连接对象
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
		return resultCode;
	}
	
	
	public static void main(String[] args){
		System.out.println("消费者程序开始启动.....");
		TopicConsumerB consumer = new TopicConsumerB();
		String messageString = consumer.receiveTextMessage();
		System.out.println("接收的消息内容是:" + messageString);
		System.out.println("消费者程序已经关闭.....");
	}
}

运行查看

1.先运行生产者

控制台:

会发现有1条消息被发布

2. 消息发布后再开启一个消费者

运行TopicConsumer.java后会发现发布的1条消息并没有被消费者接收,因为在主题模式中: 只有提前进行订阅的消费者才能成功消费消息。而队列模式中消费者不需要提前订阅也可以消费消息。如下图:

3.先开启两个消费者,后运行生产者

开启消费者A

开启消费者B

启动生产者

会发现生产者发送的1个消息,两个消费者都全部接收。

总结:

  1. 先启动生产者,发布1条消息,然后再启动消费者,这时消费者是不能消费到消息的,因为主题模式中: 只有提前进行订阅的消费者才能成功消费消息。而队列模式消费者不需要提前订阅也可以消费消息
  2. 先启动一个消费者,然后再启动生产者发布1条消息,这时消费者成功消费了ActiveMQ服务器中的消息。
  3. 先启动两个消费者,然后启动生产者发布1条消息,这时两个消费者都可以消费ActiveMQ服务器中的每一条消息。这就是主题模式的特点: 每个订阅者都可以消费主题模式中的每一条消息。而队列模式中,只能平均消费消息,被别的消费者消费的消息不能重复被其他的消费者消费
  4. 点对点:Queue,不可重复消费;发布/订阅:Topic,可以重复消费

队列模式和主题模式的区别

  1. 是否需要提前订阅
    队列模式:消费者不需要提前订阅也可以消费消息
    主题模式:只有提前进行订阅的消费者才能成功消费消息
  2. 多个消费者如何分配消息
    队列模式:只能平均消费消息,被别的消费者消费的消息不能重复被其他的消费者消费
    主题模式:每个订阅者都可以消费主题模式中的每一条消息

Jms规范里的两种message传输方式Topic和Queue,两者的对比如下表():

 TopicQueue
概要Publish  Subscribe messaging 发布订阅消息Point-to-Point  点对点
有无状态topic数据默认不落地,是无状态的。

Queue数据默认会在mq服务器上以文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data下面。也可以配置成DB存储。

完整性保障并不保证publisher发布的每条数据,Subscriber都能接受到。Queue保证每条数据都能被receiver接收。
消息是否会丢失一般来说publisher发布消息到某一个topic时,只有正在监听该topic地址的sub能够接收到消息;如果没有sub在监听,该topic就丢失了。Sender发送消息到目标Queue,receiver可以异步接收这个Queue上的消息。Queue上的消息如果暂时没有receiver来取,也不会丢失。
消息发布接收策略一对多的消息发布接收策略,监听同一个topic地址的多个sub都能收到publisher发送的消息。Sub接收完通知mq服务器一对一的消息发布接收策略,一个sender发送的消息,只能有一个receiver接收。receiver接收完后,通知mq服务器已接收,mq服务器对queue里的消息采取删除或其他操作。

代码下载:https://github.com/loafer7423/ActiveMq/tree/master/ActiveMq03

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值