JMS: Java 消息服务(Java Message Service),是 Java 平台上有关面向 MOM 的技术规范,旨在通过提供标准的产生、发送、接收和处理消息的 API 简化企业应用的开发,类似于 JDBC 和关系型数据库通信方式的抽象。
- Provider:纯 Java 语言编写的 JMS 接口实现(比如 ActiveMQ 就是)
- Domains:消息传递方式,包括点对点(P2P)、发布/订阅(Pub/Sub)两种
- Connection factory:客户端使用连接工厂来创建与 JMS provider 的连接
- Destination:消息被寻址、发送以及接收的对象
消息生产者和消息消费者又有两种关系: P2P 和 Pub/Sub
P2P (点对点)消息域使用 queue 作为 Destination,消息可以被同步或异步的发送和接收,每个消息只会给一个 Consumer 传送一次。
Consumer 可以使用 MessageConsumer.receive() 同步地接收消息,也可以通过使用MessageConsumer.setMessageListener() 注册一个 MessageListener 实现异步接收。
多个 Consumer 可以注册到同一个 queue 上,但一个消息只能被一个 Consumer 所接收,然后由该 Consumer 来确认消息。并且在这种情况下,Provider 对所有注册的 Consumer 以轮询的方式发送消息。
Pub/Sub(发布/订阅,Publish/Subscribe)消息域使用 topic 作为 Destination,发布者向 topic 发送消息,订阅者注册接收来自 topic 的消息。发送到 topic 的任何消息都将自动传递给所有订阅者。接收方式(同步和异步)与 P2P 域相同。
除非显式指定,否则 topic 不会为订阅者保留消息。当然,这可以通过持久化(Durable)订阅来实现消息的保存。这种情况下,当订阅者与 Provider 断开时,Provider 会为它存储消息。当持久化订阅者重新连接时,将会受到所有的断连期间未消费的消息。
JMS创建程序的通用步骤:
- 获取连接工厂
- 使用连接工厂创建连接
- 启动连接
- 从连接创建会话
- 获取 Destination
- 创建 Producer,或
- 创建 Producer
- 创建 message
- 创建 Consumer,或发送或接收message发送或接收 message
- 创建 Consumer
- 注册消息监听器(可选)
- 发送或接收 message
- 关闭资源(connection, session, producer, consumer 等)
ActiveMQ步骤:
官网:http://activemq.apache.org/
本地解压后: 进入bin,根据个人系统,选择响应文件夹中的activemq.bat启动
即可访问:ActiveMQ 服务启动地址:http://127.0.0.1:8161/admin/ 用户名/密码 admin/admin
队列:ActiveMQ 在 queue 中存储 Message 时,采用先进先出顺序(FIFO)存储。同一时间一个消息被分派给单个消费者,且只有当 Message 被消费并确认时,它才能从存储中删除。
队列对应点对点模式实现:
将官网下载下来的 activemq-all-5.15.9.jar 导入工程
步骤一:创建消息生产者:
package com.tao.activemq; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; /** * 消息生产者 * @author * */ public class JMSProducer { private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; //默认的连接用户名 private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; //默认的连接密码 private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;//默认的连接地址 private static final int SENDNUM = 10; //发送的消息数量 public static void main(String[] args) { ConnectionFactory connectionFactory; //连接工厂 Connection connection = null; //连接 Session session; //会话接受或者发送消息的线程 Destination destination; //消息的目的地 MessageProducer messageProducer; //消息生产者 //实例化连接工厂 connectionFactory = new ActiveMQConnectionFactory(JMSProducer.USERNAME, JMSProducer.PASSWORD, JMSProducer.BROKEURL); try { //通过连接工厂获取连接 connection = connectionFactory.createConnection(); //启动连接 connection.start(); //session实例化 connection.createSession(arg0, arg1) arg0:是否加入事务 arg1:消息确认方式 session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE); //创建消息队列 destination = session.createQueue("This is my First Queue"); //创建消息生产者 messageProducer = session.createProducer(destination); sendMessage(session, messageProducer); //提交消息 session.commit(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(connection!=null) { try { connection.close(); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * 发送消息 * @param session * @param messageProducer * @throws Exception */ private static void sendMessage(Session session,MessageProducer messageProducer) throws Exception{ for(int i=1; i<=JMSProducer.SENDNUM;i++) { TextMessage textMessage = session.createTextMessage("ActiveMQ 发送的消息:"+i); System.out.println("发送消息:"+ "ActiveMQ 发送的消息:"+i); messageProducer.send(textMessage); } } }
创建消息消费者:调用 messageConsumer.receive(1000) 接受消息
package com.tao.activemq; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageConsumer; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; /** * 点对点消费者 * @author * */ public class JMDConsumer { private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; //默认的连接用户名 private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; //默认的连接密码 private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;//默认的连接地址 public static void main(String[] args) { ConnectionFactory connectionFactory; //连接工厂 Connection connection = null; //连接 Session session; //会话接受或者发送消息的线程 Destination destination; //消息的目的地 MessageConsumer messageConsumer; //消息消费者 //实例化连接工厂 connectionFactory = new ActiveMQConnectionFactory(JMDConsumer.USERNAME, JMDConsumer.PASSWORD, JMDConsumer.BROKEURL); try { //通过连接工厂获取连接 connection = connectionFactory.createConnection(); //启动连接 connection.start(); //session实例化 消费无需加事务 session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); //点对点消费 消费方创建的消息队列名需和生产方一致 destination = session.createQueue("This is my First Queue"); //创建消费者 messageConsumer = session.createConsumer(destination); while(true) { TextMessage textMessage = (TextMessage) messageConsumer.receive(1000); if(textMessage!=null) { System.out.println("收到的消息:" + textMessage.getText()); }else { break; } } } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
点对点监听进行消费,消息生产者生产消息,消费者进行监听消费:
1.先创建一个监听类:
package com.tao.activemq; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; /** * 消息监听 * @author * */ public class Listener implements MessageListener{ @Override public void onMessage(Message message) { try { System.out.println("收到的消息:"+ ((TextMessage)message).getText()); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
通过消费方监听来消费消息:
package com.tao.activemq; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.MessageConsumer; import javax.jms.Session; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; /** * 消息队列 消费者监听 * @author * */ public class ConsumerListener { private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; //默认的连接用户名 private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; //默认的连接密码 private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;//默认的连接地址 public static void main(String[] args) { ConnectionFactory connectionFactory; //连接工厂 Connection connection = null; //连接 Session session; //会话接受或者发送消息的线程 Destination destination; //消息的目的地 MessageConsumer messageConsumer; //消息消费者 //实例化连接工厂 connectionFactory = new ActiveMQConnectionFactory(ConsumerListener.USERNAME, ConsumerListener.PASSWORD, ConsumerListener.BROKEURL); try { //通过连接工厂获取连接 connection = connectionFactory.createConnection(); //启动连接 connection.start(); //session实例化 消费无需加事务 session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); //点对点消费 消费方创建的消息队列名需和生产方一致 destination = session.createQueue("This is my First Queue"); //创建消费者实例 messageConsumer = session.createConsumer(destination); //通过消费者监听来消费消息 messageConsumer.setMessageListener(new Listener()); }catch (Exception e) { // TODO: handle exception } } }
点对点先运行生产者生产消息,在运行消费者 进行消息消费。
发布订阅模式:
消息发布者我们保持原有不动:
package com.tao.pub.activemq; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; /** * 消息发布者 * @author * */ public class JMSProducer { private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; //默认的连接用户名 private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; //默认的连接密码 private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;//默认的连接地址 private static final int SENDNUM = 10; //发送的消息数量 public static void main(String[] args) { ConnectionFactory connectionFactory; //连接工厂 Connection connection = null; //连接 Session session; //会话接受或者发送消息的线程 Destination destination; //消息的目的地 MessageProducer messageProducer; //消息生产者 //实例化连接工厂 connectionFactory = new ActiveMQConnectionFactory(JMSProducer.USERNAME, JMSProducer.PASSWORD, JMSProducer.BROKEURL); try { //通过连接工厂获取连接 connection = connectionFactory.createConnection(); //启动连接 connection.start(); //session实例化 connection.createSession(arg0, arg1) arg0:是否加入事务 arg1:消息确认方式 session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE); //发布订阅模式 发布主题不是队列 destination = session.createTopic("This is my First Topic"); //创建消息生产者 messageProducer = session.createProducer(destination); sendMessage(session, messageProducer); //提交消息 session.commit(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(connection!=null) { try { connection.close(); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * 发送消息 * @param session * @param messageProducer * @throws Exception */ private static void sendMessage(Session session,MessageProducer messageProducer) throws Exception{ for(int i=1; i<=JMSProducer.SENDNUM;i++) { TextMessage textMessage = session.createTextMessage("ActiveMQ 发送的消息:"+i); System.out.println("发送消息:"+ "ActiveMQ 发布的消息:"+i); messageProducer.send(textMessage); } } }
建立多个订阅者进行订阅消费:
监听者一:
package com.tao.pub.activemq; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; /** * 消息订阅者一 监听 * @author * */ public class Listener implements MessageListener{ @Override public void onMessage(Message message) { try { System.out.println("订阅者一收到的消息:"+ ((TextMessage)message).getText()); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.tao.pub.activemq; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.MessageConsumer; import javax.jms.Session; import javax.jms.Topic; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; /** * 消息订阅者一 * @author * */ public class ConsumerListener { private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; //默认的连接用户名 private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; //默认的连接密码 private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;//默认的连接地址 public static void main(String[] args) { ConnectionFactory connectionFactory; //连接工厂 Connection connection = null; //连接 Session session; //会话接受或者发送消息的线程 Destination destination; //消息的目的地 MessageConsumer messageConsumer; //消息消费者 //实例化连接工厂 connectionFactory = new ActiveMQConnectionFactory(ConsumerListener.USERNAME, ConsumerListener.PASSWORD, ConsumerListener.BROKEURL); try { //通过连接工厂获取连接 connection = connectionFactory.createConnection(); //启动连接 connection.start(); //session实例化 消费无需加事务 session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); //发布订阅模式 主题名一致即可 destination = session.createTopic("This is my First Topic"); //创建消费者实例 messageConsumer = session.createConsumer(destination); //通过消费者监听来消费消息 messageConsumer.setMessageListener(new Listener()); }catch (Exception e) { // TODO: handle exception } } }
监听者二:
package com.tao.pub.activemq; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; /** * 消息订阅者一 监听 * @author * */ public class Listener2 implements MessageListener{ @Override public void onMessage(Message message) { try { System.out.println("订阅者二收到的消息:"+ ((TextMessage)message).getText()); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.tao.pub.activemq; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.MessageConsumer; import javax.jms.Session; import javax.jms.Topic; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; /** * 消息订阅者二 * @author zhangtao * */ public class ConsumerListener2 { private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; //默认的连接用户名 private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; //默认的连接密码 private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;//默认的连接地址 public static void main(String[] args) { ConnectionFactory connectionFactory; //连接工厂 Connection connection = null; //连接 Session session; //会话接受或者发送消息的线程 Destination destination; //消息的目的地 MessageConsumer messageConsumer; //消息消费者 //实例化连接工厂 connectionFactory = new ActiveMQConnectionFactory(ConsumerListener2.USERNAME, ConsumerListener2.PASSWORD, ConsumerListener2.BROKEURL); try { //通过连接工厂获取连接 connection = connectionFactory.createConnection(); //启动连接 connection.start(); //session实例化 消费无需加事务 session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); //发布订阅模式 主题名一致即可 destination = session.createTopic("This is my First Topic"); //创建消费者实例 messageConsumer = session.createConsumer(destination); //通过消费者监听来消费消息 messageConsumer.setMessageListener(new Listener2()); }catch (Exception e) { // TODO: handle exception } } }
注意:发布订阅模式,需要先订阅 才能消费。(像订报纸一样)