007_JMS中的持久订阅

1. 持久订阅时, 客户端需要首先向JMS提供者注册一个表面自己身份的id(clientId)。这样当咱们这个客户端处于离线时, JMS提供者会为这个客户端保存所有发送到主题的消息。当客户端再次连接到JMS提供者时, JMS提供者根据这个客户端id, 把消息发送给它。

2. 创建持久订阅必须设置一个客户端id, 不然会报如下错误

3. 设置客户端id

3.1. 设置客户端id要紧跟在创建连接之后

// 1. 创建一个连接工厂
TopicConnectionFactory cf = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL);
// 2. 创建连接
TopicConnection conn = cf.createTopicConnection();
// 3. 设置客户端id
conn.setClientID(clientId);

3.2. 如果设置客户端id没有紧跟在创建连接之后回报如下错误

4. 多个客户端设置clientID, clientID不能重复。如果已有一个活动的被clientID标识的客户端, 再出现一个重复clientID标识的客户端连接, 会报如下错误

5. 持久订阅的实现机制

5.1. 生产者发送消息给提供者, 如果此时提供者发现没有任何的消费者(包括在线/离线), 那么就会认为该消息无用, 不需要存储, 会直接删除。

5.2. 如果有在线的消费者, 那么提供者会将消息直接传送给在线的消费者, 因为这个时候连接是通的, 消息有传输的通道。

5.3. 如果有离线的消费者, 那么提供者会把属于该消费者的消息存储下来, 等消费者在线的时候, 再将保存的离线消息推送给它。对于持久订阅者, 提供者会在该消费者第一次登录在线的时候, 将它的身份信息记录下来。记录身份的关键就是clientID和主题名称。当持久订阅者又重新在线的时候, 提供者会根据当前连接的clientID和主题名称, 去查询属于它的离线消息, 并进行推送。

6. 例子

6.1. 新建一个名为JMSDurableSubscriber的Java项目, 同时拷入相关jar包

6.2. 编写MyProducer.java

package com.jmsapp.persistent;

import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.StreamMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class MyProducer {
	// 默认连接用户名
	private static final String dftUsr = ActiveMQConnection.DEFAULT_USER;
	// 默认用户密码
	private static final String dftPwd = ActiveMQConnection.DEFAULT_PASSWORD;
	// 默认连接地址
	private static final String dftUrl = ActiveMQConnection.DEFAULT_BROKER_URL;
	// 队列名称
	private static final String topicName = "persistentSubscriber";
		
	public static void main(String[] args) {
		// 1. 创建一个连接工厂
		TopicConnectionFactory cf = new ActiveMQConnectionFactory(dftUsr, dftPwd, dftUrl);
		// 连接对象
		TopicConnection conn = null;
		// 会话对象
		TopicSession session = null;
		
		try {
			// 2. 创建连接
			conn = cf.createTopicConnection();
			// 3. 启动连接
			conn.start();
			// 4. 创建会话
			session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
			// 5. 创建消息目的地。如果是点对点, 那么它的实现是Queue; 如果是订阅模式, 那它的实现是Topic。这里我们创建一个名为persistentSubscriber的主题。
			Topic topic = session.createTopic(topicName);
			// 6. 消息生产者
			TopicPublisher publisher = session.createPublisher(topic);
			// 7. 创建文本消息和发送消息
			StreamMessage message = session.createStreamMessage();
			message.writeString("JMS中的持久订阅");
			publisher.publish(message);
		} catch (JMSException e) {
			e.printStackTrace();
		} finally {
			try {
				if (session != null) {
					session.close();
				}
			} catch (JMSException e1) {
				e1.printStackTrace();
			} finally {
				if (conn != null) {
					try {
						conn.close();
					} catch (JMSException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

6.3. 编写MyConsumer.java

package com.jmsapp.persistent;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.StreamMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class MyConsumer {
	// 默认连接用户名
	private static final String dftUsr = ActiveMQConnection.DEFAULT_USER;
	// 默认用户密码
	private static final String dftPwd = ActiveMQConnection.DEFAULT_PASSWORD;
	// 默认连接地址
	private static final String dftUrl = ActiveMQConnection.DEFAULT_BROKER_URL;
	// 队列名称
	private static final String topicName = "persistentSubscriber";
	// 客户端id
	private static final String clientId = "rjbd";
		
	public static void main(String[] args) {
		// 1. 创建一个连接工厂
		TopicConnectionFactory cf = new ActiveMQConnectionFactory(dftUsr, dftPwd, dftUrl);
		// 连接对象
		TopicConnection conn = null;
		// 会话对象
		TopicSession session = null;
		
		try {
			// 2. 创建连接
			conn = cf.createTopicConnection();
			// 3. 设置客户端id
			conn.setClientID(clientId);
			// 4. 创建会话
			session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
			// 5. 创建消息目的地。如果是点对点, 那么它的实现是Queue; 如果是订阅模式, 那它的实现是Topic。这里我们创建一个名为persistentSubscriber的主题。
			Topic topic = session.createTopic(topicName);
			// 6. 消息消费者
			TopicSubscriber subscriber = session.createDurableSubscriber(topic, clientId);
			// 7. 接收消息
			subscriber.setMessageListener(new MessageListener() {
				@Override
				public void onMessage(Message msg) {
					try {
						StreamMessage message = (StreamMessage) msg;
						System.out.println("接收: " + message.readString());
					} catch (JMSException e) {
						e.printStackTrace();
					}
				}
			});
			// 8. 启动连接, 准备开始接收消息
			conn.start();
		} catch (JMSException e) {
			e.printStackTrace();
		}
	}
}

6.4. 运行MyConsumer.java, 接收端是一直处于运行状态的

6.5. 终止运行MyConsumer.java

6.6. 运行MyProducer.java

6.7. 再次运行MyConsumer.java, 接收到消息(非持久化订阅, 这样操作就接收不到消息)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值