JMS的简单应用:
点对点模型(Point-to-Point)
点对点消息传送模型允许JMS客户端通过队列(queue)这个虚拟通道来同步和异步发送、接收消息。在点对点模型中,消息生产者称为发送者(Sender),而消息消费者则称为接收者(receiver)。传统上,点对点模型是一个基于拉取(Pull)或基于轮询(polling)的消息传送模型,这种模型从队列中请求消息,而不是自动地将消息推送到客户端。点对点消息传送模型的一个突出特点就是:发送到队列的消息被一个而且仅仅一个接收者所接收,即使可能有多个接收者在一个队列中侦听同一消息时,也是如此。
点对点消息传送模型既支持异步“即发即弃(fire and forget)”消息传送方式,又支持同步请求/应答消息传送方式。点对点消息传送模型比发布/订阅模型具有更强的耦合性,发送者通常会知道消息将被如何使用,而且也会知道谁将接收该消息。举例来说,发送者可能会向一个队列发送一个证券交易订单并等待响应,响应中应包含一个交易确认码。这样一来,消息发送者就会知道消息接收者将要处理交易订单。另一个例子就是一个生成长时间运行报告的异步请求。发送者发出报告请求,而当该报告准备就绪时,就会给发送者发送一条通知消息。在这种情况下,发送者就会知道消息接收者将要处理该消息并创建报告。
点对点模型支持负载均衡,它允许多个接收者侦听同一队列,并以此来分配负载。如图1-4所示,JMS提供者负责管理队列,确保每条消息被组内下一个可用的接收者消费一次,而且仅仅一次。JMS规范没有规定在多个接收者中间分发消息的规则,尽管某些JMS厂商已经选择实现此规则来提升负载均衡能力。点对点模型还具有其他优点,比如说,队列浏览器允许客户端在消费其消息之前查看队列内容——在发布/订阅模型中,并没有这种浏览器的概念。
在JBoss下面JMS的简单开发点对点的JMS应用:
部署关于JMS的配置过程如下:
备注:
JMS配置文件必须在JBoss的在%JBoss_HOME%\server\default\deploy中,同时文件的命名必须以xxx-service.xml中。
jboss-jms-service.xml配置信息如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: mail-service.xml 62350 2007-04-15 16:50:12Z dimitris@jboss.org $ -->
<server>
<!-- ==================================================================== -->
<!-- Mail Connection Factory -->
<!-- ==================================================================== -->
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=longgangbai">
<attribute name="JNDIName">queue/longgangbai</attribute>
<depends optional-attribute-name="DestinationManager" >jboss.mq:service=DestinationManager</depends>
</mbean>
</server>
在客户端:
备注下面为:JMS1.1 TopicConnectionFactory 和QueueConnectionFactory合并为ConnectionFactory
package com.easyway.jboss.jms.ptp.service;
import java.util.Properties;
import javax.jms.BytesMessage;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.StreamMessage;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
/**
* 队列消息的发送者
* 采用JMS点对点,一对一的(PTP消息传递模型)
* @author longgangbai
*
*/
public class QueueSender {
/**
*
* @param args
*/
public static void main(String[] args) {
QueueConnection conn=null;
QueueSession session=null;
try {
//得到一个JNDI初始化上下文
Properties props=new Properties();
//设置
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
props.setProperty(Context.PROVIDER_URL, "localhost:1099");
props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
InitialContext ctx=new InitialContext(props);
//根据上下文查找一个连接工厂TopicConnectionFactory/QueueConnectionFactory (有了两种连接工厂,
//根绝topic、queue来是哟偶那个相应的类型),该连接工厂是有JMS提供的,不需要我们自己创建,每一个
//厂商为他绑定一个全局的JNDI,我们功过JNDI便可获取它。
QueueConnectionFactory factory=(QueueConnectionFactory)ctx.lookup("ConnectionFactory");
//从连接工厂 中得到一个连接(Connect 的类型有两种,TopicConnection、QueueConnection)
conn=factory.createQueueConnection();
//通过连接来建立一个会话(Session)
//建立一个不需要事物的并且能自动确认消息已接收的会话
session=conn.createQueueSession(false,QueueSession.AUTO_ACKNOWLEDGE);
//查找目的地(目的地的类型有两种:Topic、Queue)
Destination destination =(Queue)ctx.lookup("queue/longgangbai");
//根绝会话以及目的地建立消息生产者MessageProducter(QueueSender 和TopicPublisher都扩展自MessageProducer接口)
MessageProducer producer=session.createProducer(destination);
TextMessage msg=session.createTextMessage("hi ,longgangbai ,this is jboss jms MDB message");
//发送文本消息
producer.send(msg);
//发送对象消息
producer.send(session.createObjectMessage(new SMS("wangnabaobao","this is my girl friend")));
MapMessage mapmsg=session.createMapMessage();
mapmsg.setObject("name", "baobao");
producer.send(mapmsg);
BytesMessage bmsg=session.createBytesMessage();
bmsg.writeBytes("I am a good boy !".getBytes());
producer.send(bmsg);
StreamMessage smsg=session.createStreamMessage();
smsg.writeString("阿里巴巴,http://www.alibaba.com");
producer.send(smsg);
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
session.close();
conn.close();
} catch (JMSException e2) {
e2.printStackTrace();
}
}
}
}
运行JBoss在:
在jboss.mq.destination中的选择相关的消息服务信息:
选择listMesages:
查询
接受消息的代码如下:
package com.easyway.jboss.jms.ptp.service;
import java.io.ByteArrayOutputStream;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.BytesMessage;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.StreamMessage;
import javax.jms.TextMessage;
/**
* Queue消息的接受方的MDB
* @author longgangbai
*
*
* 在JBoss 的%JAVA_HOME%\server\default\deploy目录下添加相关配置
* @author longgangbai
*/
@MessageDriven(activationConfig={
@ActivationConfigProperty(propertyName = "destinationType", //消息的目标的类型: 取值可以为javax.jms.Queue或者javax.jms.Topic
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", //消息的目标的地址,取值是目标地址的JDNI名称:
propertyValue = "queue/longgangbai"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", //消息的确认模式,却只可以为:Auto-acknowledge或者Dups_ok_acknowledge
//消息确认:是指JMS客户端通知JMS provider确认消息已经到达的一种机制,在EJB中,收到消息后发送确认是EJB容器的职责,
//确认消息就是JMS Provider ,EJB容器已经收到并处理了消息,如没有确认,JMS provider 就不知道容器是否收到了,消息,进而重发消息
propertyValue = "Auto-acknowledgeMode")
})
public class DisplayMessage implements MessageListener{
@Override
public void onMessage(Message msg) {
try {
if(msg instanceof TextMessage){
TextMessage tmsg=(TextMessage)msg;
String content=tmsg.getText();
System.out.println(content);
}else if(msg instanceof ObjectMessage){
ObjectMessage tmsg=(ObjectMessage)msg;
SMS sms=(SMS)tmsg.getObject();
String content=sms.getUsername()+":"+sms.getMessage();
System.out.println(content);
}else if(msg instanceof MapMessage){
MapMessage mmsg=(MapMessage)msg;
String content=mmsg.getString("name");
System.out.println(content);
}else if(msg instanceof BytesMessage){
BytesMessage bytesmsg=(BytesMessage)msg;
ByteArrayOutputStream byteStream=new ByteArrayOutputStream();
byte[] buffer=new byte[256];
int length=0;
while((length=bytesmsg.readBytes(buffer))!=-1){
byteStream.write(buffer,0,length);
}
String context=new String(byteStream.toByteArray());
System.out.println(context);
}else if(msg instanceof ObjectMessage){
ObjectMessage tmsg=(ObjectMessage)msg;
SMS sms=(SMS)tmsg.getObject();
String content=sms.getUsername()+":"+sms.getMessage();
System.out.println(content);
}else if(msg instanceof StreamMessage){
StreamMessage tmsg=(StreamMessage)msg;
String content=tmsg.readString();
System.out.println(content);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.easyway.jboss.jms.ptp.service;
import java.util.Properties;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.TopicSession;
import javax.naming.Context;
import javax.naming.InitialContext;
/**
* 队列消息的接受者
* 采用JMS点对点的模式
* @author longgangbai
*
*/
public class QueueReceive {
/**
*
* @param args
*/
public static void main(String[] args) {
QueueConnection conn=null;
QueueSession session=null;
try {
//得到一个JNDI初始化上下文
Properties props=new Properties();
//设置
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
props.setProperty(Context.PROVIDER_URL, "localhost:1099");
props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
InitialContext ctx=new InitialContext(props);
//根据上下文查找一个连接工厂TopicConnectionFactory/QueueConnectionFactory (有了两种连接工厂,
//根绝topic、queue来是哟偶那个相应的类型),该连接工厂是有JMS提供的,不需要我们自己创建,每一个
//厂商为他绑定一个全局的JNDI,我们功过JNDI便可获取它。
QueueConnectionFactory factory=(QueueConnectionFactory)ctx.lookup("ConnectionFactory");
//从连接工厂 中得到一个连接(Connect 的类型有两种,TopicConnection、QueueConnection)
conn=factory.createQueueConnection();
//通过连接来建立一个会话(Session)
//建立一个不需要事物的并且能自动确认消息已接收的会话
session=conn.createQueueSession(false,TopicSession.AUTO_ACKNOWLEDGE);
//查找目的地(目的地的类型有两种:Topic、Queue)
Destination destination =(Queue)ctx.lookup("queue/longgangbai");
//根绝会话以及目的地建立消息生产者MessageProducter(QueueSender 和TopicPublisher都扩展自MessageProducer接口)
MessageConsumer consumer=session.createConsumer(destination);
DisplayMessage ml = new DisplayMessage();
consumer.setMessageListener(ml);
System.out.println("开始处理休息..");
conn.start();
System.out.println("消息处理完毕...");
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
session.close();
conn.close();
} catch (JMSException e2) {
e2.printStackTrace();
}
}
}
}