ActiveMQ常见的两种应用模式以及API说明

1 PTP处理模式(Queue)

1.1 ptp基本理解

  • 消息生产者生产消息发送到 queue 中, 然后消息消费者从 queue 中取出并且消费消息。
  • 消息被消费以后, queue 中不再有存储, 所以消息消费者不可能消费到已经被消费的消息。
  • Queue 支持存在多个消费者, 但是对一个消息而言, 只会有一个消费者可以消费、 其它的则不能消费此消息了。
  • 当消费者不存在时, 消息会一直保存, 直到有消费消费
    在这里插入图片描述

1.2 pom依赖

<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-core -->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-core</artifactId>
			<version>5.7.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/javax.jms/jms -->
		<dependency>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
			<version>1.1</version>
		</dependency>

1.3 ptp的消费者

public class ConsumerPTP {
	
	public String consumer() {
		//连接工厂
		ConnectionFactory factory = null;
		//连接
		Connection connection = null;
		//目的地
		Destination destination = null;
		//会话
		Session session = null;
		//消息发送者
		MessageConsumer consumer = null;
		//消息对象
		Message message = null;
		String resultCode = null;
		try {
			//创建连接工厂
			//创建工厂三个参数:用户名,密码,连接地址
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.126.133:61616");
			//通过工厂创建连接对象
			//创建连接的方法有重载,其中有createConnection(String username,String password)
			//可以在创建连接时,只传递地址 ,不传递用户信息
			connection = factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接,但是消息的消费者必须启动连接
			connection.start();
			//两个参数:boolean transacted 是否支持事物, int acknowledgeMode如何确认消息
			
			session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			//创建目的地。参数名称是目的地是唯一标记
			destination=session.createQueue("first-activemq");
			//通过会话,创建消息的发送者producer
			//创建的消息发送者,发送的消息一定到指定的目的地中
			consumer = session.createConsumer(destination);
			//获取队列中的消息。receive方法是一个主动获取消息的方法。执行一次拉取一个消息
			message = consumer.receive();
			resultCode = ((TextMessage)message).getText();
			System.out.println("========消息已经发送========");
		}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) {
		ConsumerPTP ptp = new ConsumerPTP();
		System.out.println("========="+ptp.consumer()+"=========");
	}
}

1.4 ptp的提供者

public class ProviderPTP {
	/**
	 * 把消息发送到activemq中,具体消息内容为参数信息
	 * 开发jms相关代码过程中,都在javax.jms包下的类型
	 * @param datas - 消息内容
	 */
	public void provider(String datas) {
		//连接工厂
		ConnectionFactory factory = null;
		//连接
		Connection connection = null;
		//目的地
		Destination destination = null;
		//会话
		Session session = null;
		//消息发送者
		MessageProducer producer = null;
		//消息对象
		Message message = null;
		try {
			//创建连接工厂
			//创建工厂三个参数:用户名,密码,连接地址
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.126.133:61616");
			//通过工厂创建连接对象
			//创建连接的方法有重载,其中有createConnection(String username,String password)
			//可以在创建连接时,只传递地址 ,不传递用户信息
			connection = factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接,但是消息的消费者必须启动连接
			connection.start();
			//连个参数:boolean transacted 是否支持事物, int acknowledgeMode如何确认消息
			/**
			 * transacted:是否支持事物,
			 * true:支持事物,那么第二个参数默认无效建议是Session.SESSION_TRANSACTED
			 * false:不支持事物,常用参数。那么第二个参数必须传,且有效
			 * acknowledgeMode:如何确认消息的处理
			 * AUTO_ACKNOWLEDGE--自动确认消息,消息的消费者处理消息后,自动确认,商业开发不推荐
			 * CLIENT_ACKNOWLEDGE--客户端手动确认,消息的消费者处理后,必须手工确认
			 * DUPS_OK_ACKNOWLEDGE--有副本的客户端手动确认,一个消息可以多次处理,可以降低session消耗,可以容忍重复消息时使用(不推荐)
			 */
			session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			//创建目的地。参数名称是目的地是唯一标记
			destination=session.createQueue("first-activemq");
			//通过会话,创建消息的发送者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) {
		ProviderPTP ptp = new ProviderPTP();
		ptp.provider("测试activemq");
	}
}

2 Publish/Subscribe 处理模式(Topic)

2.1 topic基本理解

  • 消息生产者(发布) 将消息发布到 topic 中, 同时有多个消息消费者(订阅) 消费该消息。
  • 和点对点方式不同, 发布到 topic 的消息会被所有订阅者消费。
  • 当生产者发布消息, 不管是否有消费者。 都不会保存消息
  • 一定要先有消息的消费者, 后有消息的生产者。
    在这里插入图片描述

2.2 pom依赖

<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-core -->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-core</artifactId>
			<version>5.7.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/javax.jms/jms -->
		<dependency>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
			<version>1.1</version>
		</dependency>

2.3 topic的消费者

public class ConsumerTopic {
	public String consumer() {
		//连接工厂
		ConnectionFactory factory = null;
		//连接
		Connection connection = null;
		//目的地
		Destination destination = null;
		//会话
		Session session = null;
		//消息发送者
		MessageConsumer consumer = null;
		//消息对象
		Message message = null;
		String resultCode = null;
		try {
			//创建连接工厂
			//创建工厂三个参数:用户名,密码,连接地址
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.126.133:61616");
			//通过工厂创建连接对象
			//创建连接的方法有重载,其中有createConnection(String username,String password)
			//可以在创建连接时,只传递地址 ,不传递用户信息
			connection = factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接,但是消息的消费者必须启动连接
			connection.start();
			//两个参数:boolean transacted 是否支持事物, int acknowledgeMode如何确认消息
			
			session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			//创建目的地。参数名称是目的地是唯一标记
			destination=session.createTopic("test-topicmq");
			//通过会话,创建消息的发送者producer
			//创建的消息发送者,发送的消息一定到指定的目的地中
			consumer = session.createConsumer(destination);
			//获取队列中的消息。receive方法是一个主动获取消息的方法。执行一次拉取一个消息
			message = consumer.receive();
			resultCode = ((TextMessage)message).getText();
			System.out.println("========消息已经发送========");
		}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) {
		ConsumerTopic ptp = new ConsumerTopic();
		System.out.println("========="+ptp.consumer()+"=========");
	}
}

2.4 topic的提供者

public class ProviderTopic {

	public static void main(String[] args) {
		ProviderTopic ptp = new ProviderTopic();
		ptp.provider("测试topic activemq");
	}

	public void provider(String datas) {
		// 连接工厂
		ConnectionFactory factory = null;
		// 连接
		Connection connection = null;
		// 目的地
		Destination destination = null;
		// 会话
		Session session = null;
		// 消息发送者
		MessageProducer producer = null;
		// 消息对象
		Message message = null;
		try {
			//创建连接工厂
			//创建工厂三个参数:用户名,密码,连接地址
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.126.133:61616");
			//通过工厂创建连接对象
			//创建连接的方法有重载,其中有createConnection(String username,String password)
			//可以在创建连接时,只传递地址 ,不传递用户信息
			connection = factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接,但是消息的消费者必须启动连接
			connection.start();
			//连个参数:boolean transacted 是否支持事物, int acknowledgeMode如何确认消息
			session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			//创建目的地。参数名称是目的地是唯一标记
			//destination=session.createQueue("first-activemq");
			session.createTopic("test-topicmq");
			//通过会话,创建消息的发送者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();
				}
			}
		}
	}
}

3 PTP 和 PUB/SUB 简单对比

Topic Queue
概要 Publish Subscribe messaging
发布订阅消息
Point-to-Point
点对点
有无状态 topic 数据默认不落地, 是无状态的 Queue 数据默认会在mq服务器上以文件形式保存,
比如Active MQ 一 般 保 存 在$AMQ_HOME\data\kahadb 下面。
也可以配置成 DB 存储
完整性保障 并不保证publisher发布的每条数据, Subscriber 都能接受到 Queue 保证每条数据能被 receiver 接收。 消息不超时
消息是否会丢失 一般来说 publisher 发布消息到某一个topic时,
只有正在监听该 topic地址的sub 能够接收到消息;
如果没有 sub 在监听, 该topic 就丢失了
Sender发 送 消 息 到 目 标Queuereceiver 可以异步接收这个 Queue 上的消息。
Queue 上的消息如果暂时没有 receiver 来取, 也不会丢失。 前提是消息不超时
消息发布接收策略 一对多的消息发布接收策略, 监听同一个 topic 地址的多个 sub 都能收到 publisher发送的消息。
Sub接收完通知 mq 服务器
一对一的消息发布接收策略, 一个 sender 发送的消息, 只能有一个receiver 接收。
receiver接收完后, 通知 mq 服务器已接收, mq 服务器对 queue 里的消息采取删除或其他操作

4 API

4.1 Producer API

4.1.1发送消息

MessageProducer

  • send(Message message);发送消息到默认目的地, 就是创建 Producer 时指定的目的地。
  • send(Destination destination, Message message); 发送消息到指定目的地, Producer不建议绑定目的地。也就是创建 Producer的时候,不绑定目的地。这样写法:session.createProducer(null)
  • send(Message message, int deliveryMode, int priority, long timeToLive);发送消息到默认目的地, 且设置相关参数。
    deliveryMode:持久化方式(DeliveryMode.PERSISTENT|DeliveryMode.NON_PERSISTENT)。
    priority:优先级。 timeToLive:消息有效期(单位毫秒)。
  • send(Destination destination, Message message, int deliveryMode, int priority, longtimeToLive)发送消息到指定目的地, 且设置相关参数。

4.1.2 消息有效期

消息过期后, 默认会将失效消息保存到死信队列(ActiveMQ.DLQ)
不持久化的消息, 在超时后直接丢弃, 不会保存到死信队列中。
死信队列名称可配置, 死信队列中的消息不能恢复
死信队列是在activemq.xml 中配置的。

4.1.3 消息优先级

可以在发送消息时, 指定消息的权重,broker 可以建议权重较高的消息将会优先发送给 Consumer。 在某些场景下, 我们通常希望权重较高的消息优先传送; 不过因为各种原因, priority 并不能决定消息传送的严格顺序(order)。
JMS 标准中约定 priority 可以为0~9 的整数数值, 值越大表示权重越高,默认值为 4
activeMQ 中各个存储器对 priority 的支持并非完全一样。 比如 JDBC 存储器可以支持0~9,因为JDBC 存储器可以基于 priority 对消息进行排序和索引化;但是对于 kahadb/levelDB等这种基于日志文件的存储器而言, priority 支持相对较弱, 只能识别三种优先级(LOW: <4,NORMAL: =4,HIGH: > 4)

4.1.3.1 开启

broker端, 默认是不存储 priority 信息的, 我们需要手动开启, 修改 activemq.xml配置文件, 在 broker标签的子标签 policyEntries中增加下述配置:

<policyEntry queue=">" prioritizedMessages="true"/>

不过对于“非持久化”类型的消息(如果没有被 swap 到临时文件), 它们被保存在内存中,它们不存在从文件Paged in 到内存的过程,因为可以保证优先级较高的消息,总是在prefetch的时候被优先获取, 这也是“非持久化”消息可以担保消息发送顺序的优点。
Broker在收到Producer的消息之后, 将会把消息cache到内存, 如果消息需要持久化,那么同时也会把消息写入文件; 如果通道中 Consumer的消费速度足够快(即积压的消息很少, 尚未超过内存限制, 通过上文能够知道, 每个通道都可以有一定的内存用来 cache消息), 那么消息几乎不需要从存储文件中Paged In, 直接就能从内存的cache中获取即可,这种情况下, priority 可以担保“全局顺序”; 不过, 如果消费者滞后太多,cache已满, 就会触发新接收的消息直接保存在磁盘中, 那么此时,priority 就没有那么有效了。
Queue 中, prefetch 的消息列表默认将会采用轮询的方式(roundRobin, 注意并不是roundRobinDispatch)[备注:因为 Queue不支持任何 DispatchPolicy],依次添加到每个 consumerpending buffer 中, 比如有 m1-m2-m3-m4四条消息, 有 C1-C2两个消费者, 那么 :m1->C1,m2->C2,m3->C1,m4->C2。 这种轮序方式, 会对基于权重的消息发送有些额外的影响, 假如四条消息的权重都不同, 但是(m1,m3)->C1, 事实上m2的权重>m3,对于 C1 而言,它似乎丢失了“顺序性”。

4.1.3.2 强顺序

<policyEntry queue=">" strictOrderDispatch="true"/>

strictOrderDispatch“严格顺序转发”, 这是区别于“轮询”的一种消息转发手段; 不过不要误解它为“全局严格顺序”, 它只不过是将prefetch 的消息依次填满每个consumerpending buffer 。 比 如 上 述 例 子 中 , 如 果C1-C2两 个 消 费 者 的buffer尺 寸 为 3 , 那 么(m1,m2,m3)->C1,(m4)->C2;当 C1填充完毕之后, 才会填充 C2。由此这种策略可以保证 buffer中所有的消息都是“权重临近的”、 有序的。 (需要注意: strictOrderDispatch 并非是解决priority消息顺序的问题而生, 只是在使用 priority时需要关注它)。

4.1.3.3 严格顺序

<policyEntry queue=">" prioritizedMessages="true" useCache="false"
expireMessagesPeriod="0" queuePrefetch="1"/>

useCache=false来关闭内存, 强制将所有的消息都立即写入文件(索引化, 但是会降低消息的转发效率);queuePrefetch=1来约束每个consumer任何时刻只有一个消息正在处理, 那些消息消费之后, 将会从文件中重新获取, 这大大增加了消息文件操作的次数, 不过每次读取肯定都是 priority最高的消息

4.2 Consumer API

4.2.1 消息的确认

Consumer拉取消息后, 如果没有做确认 acknowledge, 此消息不会从 MQ中删除。
如果消息拉去到consumer 后, 未确认, 那么消息被锁定。 如果 consumer关闭的时候仍旧没有确认消息, 则释放消息锁定信息。 消息将发送给其他的consumer处理。
消息一旦处理, 应该必须确认。 类似数据库中的事务管理机制。
例:consumer确认方法(Session.CLIENT_ACKNOWLEDGE客户端手动确认时才需要如下,若是AUTO_ACKNOWLEDGE这不需要了)

message.acknowledge()

4.2.2 消息的过滤

对消息消费者处理的消息数据进行过滤。 这种处理可以明确消费者的角色, 细分消费者的功能。
设置过滤:
Session.createConsumer(Destination destination, String messageSelector);
过滤信息为字符串, 语法类似 SQL92 中的 where子句条件信息。 可以使用诸如AND、OR、 IN、 NOT IN等关键字。 详细内容可以查看javax.jms.Message 的帮助文档。
注意: 消息的生产者在发送消息的的时候, 必须设置可过滤的属性信息, 所有的属性信息设置方法格式为:setXxxxProperty(String name, T value)。 其中方法名中的Xxxx 是类型,如 setObjectProperty/setStringProperty 等。

发布了334 篇原创文章 · 获赞 186 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/u012060033/article/details/104455560
今日推荐