- Java消息服务指的是两个应用程序之间进行异步通信的API,它为标准消息协议和消息服务提供了一组通用接口,包括创建、发送、读取消息等,用于支持JAVA应用程序开发。在J2EE中,当两个应用程序使用JMS进行通信时,它们之间并不是直接相连的,而是通过一个共同的消息收发服务连接起来,可以达到解耦的效果,我们将会在接下来的教程中详细介绍。
- 点对点消息传送模型
- 应用程序由消息队列,发送者,接受者组成,每一个消息发送给一个特殊的消息队列,该队列保存了所有发送给它的消息(除了被接收者消费掉的和过期的消息)。
- 点对点消息模型有以下的特性
- 每个消息只有一个接收者
- 消息发送者和接收者没有时间依赖性
- 当消息发送者发送消息的时候,无论接收者程序在不在运行,都能接收消息
- 当接收者收到消息的时候,会发送确认收到通知
- 发布/订阅消息传递模型
- 在发布/订阅消息模型中,发布者发布一个消息,该消息通过topic传递给所有的客户端。在这种模型中,发布者和订阅者彼此不知道对方,是匿名的且可以动态发布和订阅topic。topic主要用于保存和传递消息,且会一直保存消息直到消息被传递给客户端。
- 发布/订阅消息模型特性如下
- 一个消息可以传送给多个订阅者
- 发布者和接收者有时间依赖性,只有当客户端创建订阅后才能接收消息,而订阅者需一直保持活跃状态以接收消息
- 为了缓和这种严格的相关性,JMS允许订阅者创建一个可持久化的订阅,这样即使订阅者没有被激活运行,它也能接收到发布者的消息
- 接收消息
- 在JMS中,消息的接收可以可以用两种方式
- 同步:使用同步接收消息的方式,消息订阅者可以调用receive()方法,在receive()中,消息未到达或在到达时间之前,方法会一直阻塞,直到消息可用
- 异步:使用异步接收消息的方式,消息订阅者需注册一个消息监听器,类似于事件监听器,只要消息到达,JMS服务提供者会通过调用调用监听器的onMessage()递送消息
- 在JMS中,消息的接收可以可以用两种方式
- JMS编程接口
- 开发P2p消息传送模型实例
- 下载ActiveMQ, http://archive.apache.org/dist/activemq/5.14.3/
- 解压,找到bin目录下的activemq.bat文件,双击启动,启动完成后,在浏览器中输入http://localhost:8161访问
- 初始化后登录的用户名和密码都为admin
- 配置tomcat服务器,在conf目录下的context.xml文件中添加如下内容
-
<Resource name="queue/connectionFactory"
auth="Container"
type="org.apache.activemq.ActiveMQConnectionFactory"
description="JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="tcp://localhost:61616"
brokerName="LocalActiveMQBroker" />
<Resource name="queue/queue0"
auth="Container"
type="org.apache.activemq.command.ActiveMQQueue"
description="My Queue"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
physicalName="TomcatQueue" />
- 创建一个maven动态web项目
- 导入依赖
-
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.14.3</version>
</dependency>
- 创建消息发送者servlet
-
package com.hand.jms.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.jms.DeliveryMode;
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.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Send
*/
@WebServlet
public class Send extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Send() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
PrintWriter out = response.getWriter();
try {
//创建初始化上下文
InitialContext context = new InitialContext();
//获取queue对象
Queue queue = (Queue) context.lookup("java:comp/env/queue/queue0");
//获取队列连接工厂
QueueConnectionFactory conFactory = (QueueConnectionFactory) context.lookup("java:comp/env/queue/connectionFactory");
//创建一个队列连接
QueueConnection queConn = (QueueConnection) conFactory.createConnection();
//创建一个队列会话
QueueSession queSession = queConn.createQueueSession(false, Session.DUPS_OK_ACKNOWLEDGE);
//创建一个会话的发送者(设置传递模式)
QueueSender sender = queSession.createSender(queue);
sender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//创建消息
TextMessage message = queSession.createTextMessage("Hello World");
//发送消息
sender.send(message);
//反馈结果
out.write("Message Sent: "+message.getText());
//关闭队列连接
queConn.close();
} catch (NamingException e) {
e.printStackTrace();
} catch (JMSException e) {
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- 创建消息接收者servlet
-
package com.hand.jms.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Receive
*/
public class Receive extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Receive() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
PrintWriter out = response.getWriter();
try {
//获取初始化上下文
InitialContext context = new InitialContext();
//获取队列
Queue queue = (Queue) context.lookup("java:comp/env/queue/queue0");
//获取队列连接工厂
QueueConnectionFactory conFactory = (QueueConnectionFactory) context.lookup("java:comp/env/queue/connectionFactory");
//获取队列连接
QueueConnection queConn = conFactory.createQueueConnection();
//创建会话
QueueSession queSession = queConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
//创建接收者
QueueReceiver receiver = queSession.createReceiver(queue);
//开始连接
queConn.start();
//获取消息
TextMessage message = (TextMessage) receiver.receive();
//反馈
out.write("Message Received: "+message.getText());
//关闭连接
queConn.close();
} catch (JMSException e) {
e.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- 启动tomcat服务器,在浏览器中输入超链接:http://localhost:8080/JMS_Test/Send, 发送消息
- 打开http://localhost:8161/admin/queues.jsp,查看消息
- 在浏览器中输入http://localhost:8080/JMS_Test/Receive,接收消息
- 打开http://localhost:8161/admin/queues.jsp,查看消息,刚才发送的消息已经不见了
- 此时一个消息的发送和接收已经完毕了,实现了P2P(点到点)的消息发送
- 开发 发布/订阅 消息传送模型实例
- 配置tomcat服务器 conf/context.xml文件
-
<Resource name="topic/connectionFactory" auth="Container"
type="org.apache.activemq.ActiveMQConnectionFactory" description="JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="failover:(tcp://localhost:61616)?initialReconnectDelay=100&maxReconnectAttempts=5"
brokerName="LocalActiveMQBroker" useEmbeddedBroker="false" />
<Resource name="topic/topic0"
auth="Container"
type="org.apache.activemq.command.ActiveMQTopic" description="My Topic" factory="org.apache.activemq.jndi.JNDIReferenceFactory"
physicalName="TestTopic" />
- 创建发布者servlet
-
package com.hand.jms.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.jms.DeliveryMode;
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.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Publisher
*/
public class Publisher extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Publisher() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
PrintWriter out = response.getWriter();
try {
//创建初始化上下文
InitialContext context = new InitialContext();
//获取Topic
Topic topic = (Topic) context.lookup("java:comp/env/topic/topic0");
//获取topic连接工厂
TopicConnectionFactory conFactory = (TopicConnectionFactory) context.lookup("java:comp/env/topic/connectionFactory");
//创建topic连接
TopicConnection conn = conFactory.createTopicConnection();
//创建topic会话
TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
//创建消息发布者
TopicPublisher publisher = session.createPublisher(topic);
publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//创建需要发布的消息
TextMessage message = session.createTextMessage();
message.setText("Hello World");
//发布消息
publisher.publish(message);
//关闭连接
out.write("Message published: "+message.getText());
conn.close();
} catch (NamingException e) {
e.printStackTrace();
} catch (JMSException e) {
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- 创建订阅者servlet
-
package com.hand.jms.servlet;
import java.io.IOException;
import java.io.PrintWriter;
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.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Subscriber
*/
public class Subscriber extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Subscriber() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
PrintWriter out = response.getWriter();
try {
//初始化上下文
InitialContext context = new InitialContext();
//获取topic
Topic topic = (Topic) context.lookup("java:comp/env/topic/topic0");
//获取topic连接工厂
TopicConnectionFactory connFactory = (TopicConnectionFactory) context.lookup("java:comp/env/topic/connectionFactory");
//创建连接
TopicConnection connection = connFactory.createTopicConnection();
//创建会话
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
//创建订阅
TopicSubscriber subscriber = session.createSubscriber(topic);
//开启连接
connection.start();
//接收消息
TextMessage message = (TextMessage) subscriber.receive();
//反馈信息
out.write("Message received: "+message.getText());
//关闭连接
connection.close();
} catch (NamingException e) {
e.printStackTrace();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- 浏览器中输入http://localhost:8080/JMS_Test/Subscriber,查看订阅的消息,此时消息没有被发布,订阅者处于阻塞状态
- 浏览器中输入http://localhost:8080/JMS_Test/Publisher, 发布消息,此时不论有多少个订阅者在等待消息,都会立即收到消息
- 到此位置,一个 发布/订阅 消息发送模式的简单实例开发完毕