java开源消息服务(OpenJMS&ActiveMQ)-OpenJMS学习笔记三(调用示例)

OpenJMS消息

JMS 有五种消息类型。三种结构化或半结构化的消息类型(MapMessage、ObjectMessage 和StreamMessage)以及两种非结构化的或自由格式的消息类型(TextMessage 和 BytesMessage)。

OpenJMS消息通讯模式有两种:

点到点(point-to-point)(PTP)模型

PTP(Point-to-Point)模型是基于队列的,发送方发消息到队列,接收方从队列接收消息,队列的存在使得消息的异步传输成为可能。

消息的发布和订阅

JMS 的发布/订阅模型定义了如何向一个内容节点发布和订阅消息,内容节点也叫主题(topic),主题是为发布者(publisher)和订阅者(subscribe) 提供传输的中介。发布/订阅模型使发布者和订阅者之间不需要直接通讯(如RMI)就可保证消息的传送,有效解决系统间耦合问题(当然有这个需要才行),还有就是提供了一对一、一对多的通讯方式,比较灵活。

  先介绍JMS里2个概念,持久订阅模式和非持久订阅模式,其实也是发布/订阅模型在可靠性上提供的2种方式:

  非持久订阅模式:只有当客户端处于激活状态,也就是和JMS 服务器保持连接的状态下,才能接收到发送到某个Topic的消息,而当客户端处于离线状态时,则这个时间段发到Topic的消息将会永远接收不到。

  持久订阅模式:客户端向JMS 注册一个识别自己身份的ID,当这个客户端处于离线时,JMS 服务器会为这个ID 保存所有发送到主题的消息,当客户再次连接到JMS 服务器时,会根据自己的ID 得到所有当自己处于离线时发送到主题的消息,即消息永远能接收到。


下面我们就通过代码来熟悉PTP和发布/订阅模式

PTP模式

发送者代码

import java.util.Hashtable;

import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class QueueSend {
	public static void main(String[] args) throws NamingException, JMSException {
//		Hashtable<String, String> properties = new Hashtable<String, String>();
//		
//		properties.put(Context.INITIAL_CONTEXT_FACTORY,"org.exolab.jms.jndi.InitialContextFactory");
//		
//		properties.put(Context.PROVIDER_URL,"rmi://192.168.107.67:1099/");
//		
//		Context context = new InitialContext(properties);
		Context context = new InitialContext();
		QueueConnectionFactory queueConnFactory = (QueueConnectionFactory)context.lookup("JmsQueueConnectionFactory");
		
		QueueConnection queueConn = queueConnFactory.createQueueConnection();
		
		QueueSession queueSession = queueConn.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
		
		
		//Queue queue = (Queue)context.lookup("queue1");
		Queue queue =queueSession.createQueue("test");
		
		QueueSender queueSender = queueSession.createSender(queue);
		
		
		
		TextMessage message =  queueSession.createTextMessage();
		
		message.setText("hello, this is open JMS");
		
		
		queueSender.send(message);
		
		
		context.close();
		
		
		queueConn.close();
		
		
		
	}
}




消息接受者代码

import java.util.Hashtable;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class QueueRecive {
	public static void main(String[] args) throws NamingException, JMSException {
//		Hashtable properties = new Hashtable();
//		
//		properties.put(Context.INITIAL_CONTEXT_FACTORY,"org.exolab.jms.jndi.InitialContextFactory");
//		
//		properties.put(Context.PROVIDER_URL,"rmi://192.168.107.67:1099/");
//		
//		Context context = new InitialContext(properties);
		Context context = new InitialContext();
		QueueConnectionFactory queueConnFactory = (QueueConnectionFactory)context.lookup("JmsQueueConnectionFactory");
		
		QueueConnection queueConn = queueConnFactory.createQueueConnection();
		
		queueConn.start();
		
		QueueSession queueSession = queueConn.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
		
		
		//Queue queue = (Queue)context.lookup("test");
		Queue queue = (Queue)queueSession.createQueue("test");
		
		QueueReceiver queueReceiver = queueSession.createReceiver(queue);
		
		
		
	
		
		Message message = queueReceiver.receive();
		
		System.out.println(message.getJMSType());
		
		if(message instanceof TextMessage){
			System.out.println(((TextMessage)message).getText());
		}
		
		context.close();
		
		
		queueConn.close();
		
		
		
	}
}



说明

我们在获取QueueConnectionFactory时是使用JNDI方式,需要说明两点:

  • 获取方式:我们通过设置property或者通过设置jndi.properties文件来初始化Context

如果使用jndi.properties,大家可以将%OPENJMS_HOME%/config目录下得jndi文件拷贝到自己程序的src下面,并将provider.url路径改掉。

我们JNDI service accepting connection on:****后面的url就是我们JNDI获取的PROVIDER_URL。比如我这变有两个分别是tcp和rmi开头的。大家随便选择一个使用。我在后面会具体描述有什么不一样。置于启动时,其他的Server acceptiing,Admin service我们也在后面研究。

置于代码下面发送和接受信息的代码都是标准的JMS操作。我们这边就不描述了。如果不清楚,大家自己去找资料。


发布订阅模式

发布信息

import java.util.Date;
import java.util.Hashtable;

import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class TopicPublish {

	/**
	 * @param args
	 * @throws NamingException
	 * @throws JMSException 
	 */
	public static void main(String[] args) throws NamingException, JMSException {
		// TODO Auto-generated method stub
		Hashtable<String, String> properties = new Hashtable<String, String>();

		properties.put(Context.INITIAL_CONTEXT_FACTORY,
				"org.exolab.jms.jndi.InitialContextFactory");

		properties.put(Context.PROVIDER_URL, "rmi://192.168.107.67:1099/");

		Context context = new InitialContext(properties);

		TopicConnectionFactory factory = (TopicConnectionFactory) context
				.lookup("JmsTopicConnectionFactory");

		TopicConnection topicConn = factory.createTopicConnection();
		topicConn.start();

		TopicSession session = topicConn.createTopicSession(false,
				Session.AUTO_ACKNOWLEDGE);

		Topic topic1 = (Topic) context.lookup("topic1");

		TopicPublisher publisher = session.createPublisher(topic1);
		for(int i = 0 ;i<2;i++){
			TextMessage message = session.createTextMessage("test topic:"+i+":"+new Date());
			publisher.publish(message);
		}
	

		context.close();

		topicConn.close();

	}

}


订阅者代码(阻塞-同步模式)

import java.util.Hashtable;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class TopicSubscribeSync {

	/**
	 * @param args
	 * @throws NamingException
	 * @throws JMSException 
	 */
	public static void main(String[] args) throws NamingException, JMSException {
		// TODO Auto-generated method stub
		System.out.println("订阅开始");
		Hashtable<String, String> properties = new Hashtable<String, String>();

		properties.put(Context.INITIAL_CONTEXT_FACTORY,
				"org.exolab.jms.jndi.InitialContextFactory");

		properties.put(Context.PROVIDER_URL, "rmi://192.168.107.67:1099/");

		Context context = new InitialContext(properties);

		TopicConnectionFactory factory = (TopicConnectionFactory) context
				.lookup("JmsTopicConnectionFactory");

		TopicConnection topicConn = factory.createTopicConnection();
		topicConn.setClientID("testDurable");
		topicConn.start();

		TopicSession session = topicConn.createTopicSession(false,
				Session.AUTO_ACKNOWLEDGE);

		Topic topic1 = (Topic) context.lookup("topic1");

		//TopicSubscriber subscriber = session.createSubscriber(topic1);
		TopicSubscriber subscriber = session.createDurableSubscriber(topic1,"sub1");
		Message message  = null;
		while((message = subscriber.receive())!= null){
			if(message instanceof TextMessage){
				System.out.println(((TextMessage)message).getText());
			}
		}
		subscriber.close();
		context.close();
		topicConn.close();
		System.out.println("订阅结束");
	}

}


订阅者代码(非阻塞-异步模式)

import java.util.Hashtable;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class TopicSubscribeAsync implements MessageListener{

	/**
	 * @param args
	 * @throws NamingException
	 * @throws JMSException 
	 */
	public static void main(String[] args) throws NamingException, JMSException {
		// TODO Auto-generated method stub
		new TopicSubscribeAsync().subscribe();
		
		
		
	}
	public void subscribe() throws NamingException, JMSException{
		System.out.println("异步订阅开始");
		Hashtable<String, String> properties = new Hashtable<String, String>();

		properties.put(Context.INITIAL_CONTEXT_FACTORY,
				"org.exolab.jms.jndi.InitialContextFactory");

		properties.put(Context.PROVIDER_URL, "rmi://192.168.107.67:1099/");

		Context context = new InitialContext(properties);

		TopicConnectionFactory factory = (TopicConnectionFactory) context
				.lookup("JmsTopicConnectionFactory");

		TopicConnection topicConn = factory.createTopicConnection();
		topicConn.start();

		TopicSession session = topicConn.createTopicSession(false,
				Session.AUTO_ACKNOWLEDGE);

		Topic topic1 = (Topic) context.lookup("topic1");

		TopicSubscriber subscriber = session.createSubscriber(topic1);
		
		subscriber.setMessageListener(this);

//		context.close();
//
//		topicConn.close();
		System.out.println("订阅结束");
	}
	@Override
	public void onMessage(Message message) {
		// TODO Auto-generated method stub
		System.out.println("receive a message");
		if(message instanceof TextMessage){
			try {
				System.out.println(((TextMessage)message).getText());
			} catch (JMSException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}


说明1

OpenJMS消息订阅者有两种方式:1.持久订阅 2.非持久订阅。概念上面已经描述。具体的代码时间就是上面的两个订阅代码。

第一个订阅代码,我通过createDurableSubscriber方法来指定了主题为:topic1,订户为:sub1。所以我们这个订阅者是一个持久订阅

第二个订阅代码,我通过createSubscriber方法指定了主题为:topci1。所以这个订阅者是非持久订阅。

说明2

消息接受处理分异步和同步两种方式。同步的话,直接subscriber.receive()代码,阻塞等待消息。异步的话,通过注册subscriber.setMessageListener(this);设置message监听器来处理消息。

说明3

我们在PTP模式中时,获取queue时,是用代码
//Queue queue = (Queue)context.lookup("queue1");
Queue queue =queueSession.createQueue("test");上面queue1是openjms默认创建的一个队列,后面这个,是我们临时指定的对列名。
在这种模式下,临时创建没有问题。但是在发布/订阅模式下,就有问题了。
本示例中topic1和sub1都是系统默认的配置(这些配置我们会再讲述openjms配置部分说明),所以是没有问题的,问题在哪呢,如果我们没有配置默认的订阅者信息,这获取订阅信息的代码就会出问题了。topic1我们可以createTopic来创建,但是sub1如何指定?
我们有两种方式,一个通过admin接口创建一个持久订阅者信息(后面章节讲),另外一个是订阅者先创建一个连接,设置clientid等信息进行持久订阅。
比如:
topicConn.setClientID("1");
		topicConn.start();

		TopicSession session = topicConn.createTopicSession(false,
				Session.AUTO_ACKNOWLEDGE);

		Topic topic1 = (Topic) session.createTopic("test");

		//TopicSubscriber subscriber = session.createSubscriber(topic1);
		TopicSubscriber subscriber = session.createDurableSubscriber(topic1,"1");


这种方式需要订阅者先建立一次连接进行注册。

本例中使用的方式以及设置clientID方式,都是遵循JMS API的,我比较奇怪的是,在实际代码中,如果强行关闭接受者程序,再次启动时候会出现 异常。
异常 Durable subscriber already exists with name: sub1或者Duplicate clientID: testDurable。
我还没研究到OpenJMS这块处理逻辑,具体为什么还不知道。

说明4

大家也可以直接看%OPENJMS_HOME%/examples\basic目录下得示例。
请参阅这两个官方文档: http://openjms.sourceforge.net/usersguide/using.html
关于程序中使用的jar包,可以在%OPENJMS_HOME%lib下获取,关于什么环境需要什么jar包,请参阅官方文档:






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值