上一篇已经讲了JMS的基本概念,这一篇来上手练一练,如果对JMS基本概念还不熟悉,欢迎参靠JMS基本概。
这篇文章所使用的代码已经不是我刚入手时的代码,已经经过我重构过的代码,便于理解,并且加了很多中文注释,希望对大家有所帮助。
在基本概念一篇中已经讲到,JMS有两种消息模型,一种是点对点,另一种的发布/订阅模式。本篇文章就基于这两种消息模型来写例子。
点对点模型
先看一下生产者代码:
- package com.darren.activemq.queue;
- import javax.jms.JMSException;
- import javax.jms.Session;
- import javax.jms.TextMessage;
- import com.darren.activemq.ActivemqContants;
- import com.darren.activemq.ProducerConsumer;
- /**
- * 消息生产类
- *
- * @author Darren
- *
- */
- public class QueueProducer extends ProducerConsumer {
- private int startNumber;
- private int endNumber;
- public QueueProducer(String name) throws JMSException {
- this.name = name;
- // 通过连接工厂获取连接
- this.connection = this.getConnection();
- // 启动连接
- this.connection.start();
- // 创建Session
- this.session = this.connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
- // 创建消息队列
- this.destination = this.session.createQueue("test-queue");
- // 创建消息生产者
- this.messageProducer = this.session.createProducer(destination);
- }
- /**
- * 发送消息
- *
- * @throws JMSException
- */
- public void sendMessage() throws JMSException {
- this.startNumber = this.endNumber;
- this.endNumber = this.startNumber + MESSAGE_COUNT;
- for (int i = this.startNumber; i < this.endNumber; i++) {
- TextMessage message = this.session.createTextMessage("I send the message " + i);
- System.out.println(message.getText());
- this.messageProducer.send(message);
- }
- }
- /**
- * 发送结束标志
- *
- * @param times
- * 发送次数
- * @throws JMSException
- */
- public void sendFinishMessage(int times) throws JMSException {
- for (int i = 0; i < times; i++) {
- TextMessage message = this.session.createTextMessage(ActivemqContants.FINISH_FLAG);
- System.out.println("Send finish flag: " + message.getText());
- this.messageProducer.send(message);
- }
- }
- /**
- * 提交事务
- *
- * @throws JMSException
- */
- public void commit() throws JMSException {
- this.session.commit();
- }
- }
我写了两种消息消费者,其实也就是上一篇讲的同步消费和异步消费。
先来看异步消费者:
- package com.darren.activemq.queue;
- import javax.jms.JMSException;
- import javax.jms.Session;
- import com.darren.activemq.ProducerConsumer;
- import com.darren.activemq.listener.ConsumerListener;
- /**
- * 消息消费者
- *
- * @author Darren
- *
- */
- public class QueueListenerConsumer extends ProducerConsumer {
- public QueueListenerConsumer(String name) throws JMSException {
- this.name = name;
- // 通过连接工厂获取连接
- this.connection = this.getConnection();
- // 启动连接
- this.connection.start();
- // 创建Session
- this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- // 创建连接的消息队列
- this.destination = this.session.createQueue("test-queue");
- // 创建消息消费者
- this.messageConsumer = this.session.createConsumer(destination);
- // 设置消息监听
- this.messageConsumer.setMessageListener(new ConsumerListener("Listener 1:", this));
- }
- }
异步消费者通过设置监听器来达到异步消费消息的目的。其实这个监听器也很简单,我们来看一下。
- package com.darren.activemq.listener;
- import javax.jms.Message;
- import javax.jms.MessageListener;
- import javax.jms.ObjectMessage;
- import javax.jms.TextMessage;
- import com.darren.activemq.ActivemqContants;
- import com.darren.activemq.ProducerConsumer;
- /**
- * 消息监听
- *
- * @author Darren
- *
- */
- public class ConsumerListener implements MessageListener {
- private String name;
- private ProducerConsumer producerConsumer;
- public ConsumerListener(String name, ProducerConsumer producerConsumer) {
- this.name = name;
- this.producerConsumer = producerConsumer;
- }
- @Override
- public void onMessage(Message message) {
- try {
- if (message instanceof TextMessage) {
- TextMessage textMessage = (TextMessage) message;
- System.out.println(name + " 接收到的消息 " + textMessage.getText());
- // 如果接收到结束标志,修改消费者的状态
- if (ActivemqContants.FINISH_FLAG.equals(textMessage.getText())) {
- // 消费者消费完成,关闭连接
- this.producerConsumer.closeConnection();
- }
- } else if (message instanceof ObjectMessage) {
- ObjectMessage objectMessage = (ObjectMessage) message;
- System.out.println(name + " 接收到的消息 " + objectMessage.getObject());
- } else {
- System.out.println("不支持的消息类型!");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
这里只有一个方法。
为了便于理解,我先把父类贴出来,这样就可以基本完整的看了。
- package com.darren.activemq;
- import javax.jms.Connection;
- import javax.jms.ConnectionFactory;
- import javax.jms.Destination;
- import javax.jms.JMSException;
- import javax.jms.MessageConsumer;
- import javax.jms.MessageProducer;
- import javax.jms.Session;
- import org.apache.activemq.ActiveMQConnection;
- import org.apache.activemq.ActiveMQConnectionFactory;
- public abstract class ProducerConsumer {
- protected static final String USERNAME = ActiveMQConnection.DEFAULT_USER;// 默认的连接用户名
- protected static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;// 默认的连接密码
- protected static final String BROKERURL = ActiveMQConnection.DEFAULT_BROKER_URL;// 默认的连接地址
- protected static final int MESSAGE_COUNT = 10;// 发送的消息数量
- protected static ConnectionFactory connectionFactory;// 连接工厂
- protected Connection connection = null; // 连接
- protected Session session;// 会话 接受或者发送消息的线程
- protected Destination destination;// 消息的目的地
- protected MessageProducer messageProducer;// 消息生产者
- protected MessageConsumer messageConsumer;// 消息消费者
- // 状态
- protected volatile boolean isFinished = false;
- protected String name;
- static {
- getConnectionFactory();
- }
- /**
- * 获取连接工厂
- *
- * @return
- */
- protected synchronized static ConnectionFactory getConnectionFactory() {
- if (connectionFactory == null) {
- connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKERURL);
- }
- return connectionFactory;
- }
- /**
- * 获取连接
- *
- * @return
- * @throws JMSException
- */
- protected Connection getConnection() throws JMSException {
- return connectionFactory.createConnection();
- };
- /**
- * 关闭连接
- *
- * @throws JMSException
- */
- public void closeConnection() throws JMSException {
- if (this.connection != null) {
- this.connection.close();
- }
- System.out.println(this.name + " connection close!");
- this.isFinished = true;
- }
- /**
- * 获取状态
- *
- * @return
- */
- public boolean isFinished() {
- return isFinished;
- }
- /**
- * 获取消费者
- *
- * @return
- */
- public MessageConsumer getMessageConsumer() {
- return messageConsumer;
- }
- /**
- * 获取生产者或消费者名称
- *
- * @return
- */
- public String getName() {
- return name;
- }
- }
用到的常量类
- package com.darren.activemq;
- public class ActivemqContants {
- public static final String FINISH_FLAG = "FINISHED";
- }
来个测试类吧,这样就完整了
- package com.darren.activemq.queue;
- import javax.jms.JMSException;
- public class QueueTest {
- public static void main(String[] args) {
- Thread thread = null;
- try {
- // 启动消费者,消费者开始等待
- new QueueListenerConsumer("QueueListenerConsumer");
- //new QueueReceiveConsumer("QueueReceiveConsumer");
- thread = new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- // 启动生产者,生产者定时生产消息
- QueueProducer producer = new QueueProducer("QueueProducer");
- Thread.sleep(2000);
- // 第一次发送
- producer.sendMessage();
- producer.commit();
- Thread.sleep(2000);
- // 第二次发送
- producer.sendMessage();
- producer.commit();
- Thread.sleep(2000);
- // 第三次发送
- producer.sendMessage();
- producer.commit();
- // 发送结束标志
- producer.sendFinishMessage(1);
- producer.commit();
- // 生产者生产完成,关闭连接
- producer.closeConnection();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- thread.start();
- } catch (JMSException e) {
- e.printStackTrace();
- }
- }
- }
现在为止,一个异步消息的完整例子就真的完整了,我是为了便于看到消息的消费过程,所以把生产者放到了一个线程里来不断的生产消息,来通过观察消费者打印的消费信息,了解消费的过程。
再来看看同步消息模式:
同步消费的方式就是轮训的去要有没有消息,有没有消息。为了适应我的测试类,我把同步消息消费也按照异步消费的模式做了相应的改动,下边就是代码:
- package com.darren.activemq.queue;
- import javax.jms.JMSException;
- import javax.jms.Session;
- import com.darren.activemq.ProducerConsumer;
- import com.darren.activemq.listener.UglyConsumerListener;
- /**
- * 消息消费者
- *
- * @author Darren
- *
- */
- public class QueueReceiveConsumer extends ProducerConsumer {
- public QueueReceiveConsumer(String name) throws JMSException {
- this.name = name;
- // 通过连接工厂获取连接
- this.connection = this.getConnection();
- // 启动连接
- this.connection.start();
- // 创建Session
- this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- // 创建连接的消息队列
- this.destination = this.session.createQueue("test-queue");
- // 创建消息消费者
- this.messageConsumer = this.session.createConsumer(destination);
- // 启动一个异步线程接受消息,模拟一个消息监听器
- Thread thread = new Thread(new UglyConsumerListener("UglyListener 1:", this));
- thread.start();
- }
- }
在这里,我选择启动一个线程去设置一个监听器,为什么要启动线程,因为我上边提到了要适应我的测试类,我是先初始化消费者,如果不用线程,那么消费者就会在要消息的时候夯住,因为:
- Message message = messageConsumer.receive(100000);
这个方法是阻塞式方法。会程序在初始化消费者后就会夯住,然后生成者无法启动,无法生成消息,整个程序就死锁了,为了解决这个问题,所以采用模拟异步消费的方式来写同步消费例子。
下边来看我写的丑陋的监听器:
- package com.darren.activemq.listener;
- import javax.jms.JMSException;
- import javax.jms.Message;
- import javax.jms.MessageConsumer;
- import javax.jms.ObjectMessage;
- import javax.jms.TextMessage;
- import com.darren.activemq.ActivemqContants;
- import com.darren.activemq.ProducerConsumer;
- /**
- * 消息监听
- *
- * @author Darren
- *
- */
- public class UglyConsumerListener implements Runnable {
- private String name;
- private ProducerConsumer producerConsumer;
- public UglyConsumerListener(String name, ProducerConsumer producerConsumer) {
- this.name = name;
- this.producerConsumer = producerConsumer;
- }
- public void onMessage(Message message) {
- try {
- if (message instanceof TextMessage) {
- TextMessage textMessage = (TextMessage) message;
- System.out.println(name + " 接收到的消息 " + textMessage.getText());
- // 如果接收到结束标志,修改消费者的状态
- if (ActivemqContants.FINISH_FLAG.equals(textMessage.getText())) {
- // 消费者消费完成,关闭连接
- this.producerConsumer.closeConnection();
- }
- } else if (message instanceof ObjectMessage) {
- ObjectMessage objectMessage = (ObjectMessage) message;
- System.out.println(name + " 接收到的消息 " + objectMessage.getObject());
- } else {
- System.out.println("不支持的消息类型!");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- public void run() {
- while (!producerConsumer.isFinished()) {
- MessageConsumer messageConsumer = this.producerConsumer.getMessageConsumer();
- if (messageConsumer != null) {
- try {
- Message message = messageConsumer.receive(100000);
- this.onMessage(message);
- } catch (JMSException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
为什么称为丑陋的监听器,相信大家也不难发现,因为采用循环方式,并不是真正的监听器。 看了这两个监听器,那么我提出一个问题,这个问题也是在我写这篇文章的时候发现的,如果我再写一个抽象类去作为这两个监听器的父类,把onMessage方法提出来,是不是更加优化了,因为我发现两个onMessage方法其实一样,我也是故意给做的一样的。
当然今天讲的并不是Java的重构,而是JMS,这个问题就作为题外话好了。
现在你自己也可以去跑一跑这两个例子,细心的同学或发现在测试类中其实有一行有疑问。为什么发送结束消息的方法要加参数,可以设置发送多个结束消息。这个问题我们稍后介绍,下面我来运行一下代码,也许你就渐渐明白。
先跑一个异步消费的例子:
- INFO | Successfully connected to tcp://localhost:61616
- INFO | Successfully connected to tcp://localhost:61616
- I send the message 0
- I send the message 1
- I send the message 2
- I send the message 3
- I send the message 4
- I send the message 5
- I send the message 6
- I send the message 7
- I send the message 8
- I send the message 9
- Listener 1: 接收到的消息 I send the message 0
- Listener 1: 接收到的消息 I send the message 1
- Listener 1: 接收到的消息 I send the message 2
- Listener 1: 接收到的消息 I send the message 3
- Listener 1: 接收到的消息 I send the message 4
- Listener 1: 接收到的消息 I send the message 5
- Listener 1: 接收到的消息 I send the message 6
- Listener 1: 接收到的消息 I send the message 7
- Listener 1: 接收到的消息 I send the message 8
- Listener 1: 接收到的消息 I send the message 9
- I send the message 10
- I send the message 11
- I send the message 12
- I send the message 13
- I send the message 14
- I send the message 15
- I send the message 16
- I send the message 17
- I send the message 18
- I send the message 19
- Listener 1: 接收到的消息 I send the message 10
- Listener 1: 接收到的消息 I send the message 11
- Listener 1: 接收到的消息 I send the message 12
- Listener 1: 接收到的消息 I send the message 13
- Listener 1: 接收到的消息 I send the message 14
- Listener 1: 接收到的消息 I send the message 15
- Listener 1: 接收到的消息 I send the message 16
- Listener 1: 接收到的消息 I send the message 17
- Listener 1: 接收到的消息 I send the message 18
- Listener 1: 接收到的消息 I send the message 19
- I send the message 20
- I send the message 21
- I send the message 22
- I send the message 23
- I send the message 24
- I send the message 25
- I send the message 26
- I send the message 27
- I send the message 28
- I send the message 29
- Listener 1: 接收到的消息 I send the message 20
- Listener 1: 接收到的消息 I send the message 21
- Send finish flag: FINISHED
- Listener 1: 接收到的消息 I send the message 22
- Listener 1: 接收到的消息 I send the message 23
- Listener 1: 接收到的消息 I send the message 24
- Listener 1: 接收到的消息 I send the message 25
- Listener 1: 接收到的消息 I send the message 26
- Listener 1: 接收到的消息 I send the message 27
- Listener 1: 接收到的消息 I send the message 28
- Listener 1: 接收到的消息 I send the message 29
- Listener 1: 接收到的消息 FINISHED
- QueueProducer connection close!
- QueueListenerConsumer connection close!
然后再来跑一个同步消费的例子:
- //new QueueListenerConsumer("QueueListenerConsumer");
- new QueueReceiveConsumer("QueueReceiveConsumer");
只需把注水换一换就行了。
- INFO | Successfully connected to tcp://localhost:61616
- INFO | Successfully connected to tcp://localhost:61616
- I send the message 0
- I send the message 1
- I send the message 2
- I send the message 3
- I send the message 4
- I send the message 5
- I send the message 6
- I send the message 7
- I send the message 8
- I send the message 9
- UglyListener 1: 接收到的消息 I send the message 0
- UglyListener 1: 接收到的消息 I send the message 1
- UglyListener 1: 接收到的消息 I send the message 2
- UglyListener 1: 接收到的消息 I send the message 3
- UglyListener 1: 接收到的消息 I send the message 4
- UglyListener 1: 接收到的消息 I send the message 5
- UglyListener 1: 接收到的消息 I send the message 6
- UglyListener 1: 接收到的消息 I send the message 7
- UglyListener 1: 接收到的消息 I send the message 8
- UglyListener 1: 接收到的消息 I send the message 9
- I send the message 10
- I send the message 11
- I send the message 12
- I send the message 13
- I send the message 14
- I send the message 15
- I send the message 16
- I send the message 17
- I send the message 18
- I send the message 19
- UglyListener 1: 接收到的消息 I send the message 10
- UglyListener 1: 接收到的消息 I send the message 11
- UglyListener 1: 接收到的消息 I send the message 12
- UglyListener 1: 接收到的消息 I send the message 13
- UglyListener 1: 接收到的消息 I send the message 14
- UglyListener 1: 接收到的消息 I send the message 15
- UglyListener 1: 接收到的消息 I send the message 16
- UglyListener 1: 接收到的消息 I send the message 17
- UglyListener 1: 接收到的消息 I send the message 18
- UglyListener 1: 接收到的消息 I send the message 19
- I send the message 20
- I send the message 21
- I send the message 22
- I send the message 23
- I send the message 24
- I send the message 25
- I send the message 26
- I send the message 27
- I send the message 28
- I send the message 29
- Send finish flag: FINISHED
- UglyListener 1: 接收到的消息 I send the message 20
- UglyListener 1: 接收到的消息 I send the message 21
- UglyListener 1: 接收到的消息 I send the message 22
- UglyListener 1: 接收到的消息 I send the message 23
- UglyListener 1: 接收到的消息 I send the message 24
- UglyListener 1: 接收到的消息 I send the message 25
- UglyListener 1: 接收到的消息 I send the message 26
- UglyListener 1: 接收到的消息 I send the message 27
- UglyListener 1: 接收到的消息 I send the message 28
- UglyListener 1: 接收到的消息 I send the message 29
- UglyListener 1: 接收到的消息 FINISHED
- QueueReceiveConsumer connection close!
- QueueProducer connection close!
大家会发现和异步消费并未区别,因为其实也是异步消费。
那么如果我现在把测试类的注释都打开,会是什么结果呢:
- new QueueListenerConsumer("QueueListenerConsumer");
- new QueueReceiveConsumer("QueueReceiveConsumer");
- INFO | Successfully connected to tcp://localhost:61616
- INFO | Successfully connected to tcp://localhost:61616
- INFO | Successfully connected to tcp://localhost:61616
- I send the message 0
- I send the message 1
- I send the message 2
- I send the message 3
- I send the message 4
- I send the message 5
- I send the message 6
- I send the message 7
- I send the message 8
- I send the message 9
- Listener 1: 接收到的消息 I send the message 0
- Listener 1: 接收到的消息 I send the message 2
- UglyListener 1: 接收到的消息 I send the message 1
- Listener 1: 接收到的消息 I send the message 4
- Listener 1: 接收到的消息 I send the message 6
- Listener 1: 接收到的消息 I send the message 8
- UglyListener 1: 接收到的消息 I send the message 3
- UglyListener 1: 接收到的消息 I send the message 5
- UglyListener 1: 接收到的消息 I send the message 7
- UglyListener 1: 接收到的消息 I send the message 9
- I send the message 10
- I send the message 11
- I send the message 12
- I send the message 13
- I send the message 14
- I send the message 15
- I send the message 16
- I send the message 17
- I send the message 18
- I send the message 19
- Listener 1: 接收到的消息 I send the message 10
- Listener 1: 接收到的消息 I send the message 12
- Listener 1: 接收到的消息 I send the message 14
- UglyListener 1: 接收到的消息 I send the message 11
- Listener 1: 接收到的消息 I send the message 16
- UglyListener 1: 接收到的消息 I send the message 13
- Listener 1: 接收到的消息 I send the message 18
- UglyListener 1: 接收到的消息 I send the message 15
- UglyListener 1: 接收到的消息 I send the message 17
- UglyListener 1: 接收到的消息 I send the message 19
- I send the message 20
- I send the message 21
- I send the message 22
- I send the message 23
- I send the message 24
- I send the message 25
- I send the message 26
- I send the message 27
- I send the message 28
- I send the message 29
- Listener 1: 接收到的消息 I send the message 20
- Listener 1: 接收到的消息 I send the message 22
- Listener 1: 接收到的消息 I send the message 24
- Listener 1: 接收到的消息 I send the message 26
- Listener 1: 接收到的消息 I send the message 28
- Send finish flag: FINISHED
- UglyListener 1: 接收到的消息 I send the message 21
- UglyListener 1: 接收到的消息 I send the message 23
- UglyListener 1: 接收到的消息 I send the message 25
- UglyListener 1: 接收到的消息 I send the message 27
- UglyListener 1: 接收到的消息 I send the message 29
- Listener 1: 接收到的消息 FINISHED
- QueueProducer connection close!
- QueueListenerConsumer connection close!
这个时候有俩个消费者,一个消费者消费了一半的消息,这里我没有深入研究这种消费平均的方式是随机的还是真的平均,我猜猜可能是真的平均,因为我运行了多次都是同样的结果。
通过我打出来的消息,发现只有一个消费者结束了,另一个并没有结束,因为没有收到结束消息标志,所以需要发送两个结束标志才能使两个消费者都能结束。
- producer.sendFinishMessage(2);
这也正是我设置参数的原因:
改成2之后再来运行一次:
- INFO | Successfully connected to tcp://localhost:61616
- INFO | Successfully connected to tcp://localhost:61616
- INFO | Successfully connected to tcp://localhost:61616
- I send the message 0
- I send the message 1
- I send the message 2
- I send the message 3
- I send the message 4
- I send the message 5
- I send the message 6
- I send the message 7
- I send the message 8
- I send the message 9
- Listener 1: 接收到的消息 I send the message 0
- Listener 1: 接收到的消息 I send the message 2
- UglyListener 1: 接收到的消息 I send the message 1
- Listener 1: 接收到的消息 I send the message 4
- UglyListener 1: 接收到的消息 I send the message 3
- Listener 1: 接收到的消息 I send the message 6
- UglyListener 1: 接收到的消息 I send the message 5
- Listener 1: 接收到的消息 I send the message 8
- UglyListener 1: 接收到的消息 I send the message 7
- UglyListener 1: 接收到的消息 I send the message 9
- I send the message 10
- I send the message 11
- I send the message 12
- I send the message 13
- I send the message 14
- I send the message 15
- I send the message 16
- I send the message 17
- I send the message 18
- I send the message 19
- Listener 1: 接收到的消息 I send the message 10
- Listener 1: 接收到的消息 I send the message 12
- Listener 1: 接收到的消息 I send the message 14
- Listener 1: 接收到的消息 I send the message 16
- Listener 1: 接收到的消息 I send the message 18
- UglyListener 1: 接收到的消息 I send the message 11
- UglyListener 1: 接收到的消息 I send the message 13
- UglyListener 1: 接收到的消息 I send the message 15
- UglyListener 1: 接收到的消息 I send the message 17
- UglyListener 1: 接收到的消息 I send the message 19
- I send the message 20
- I send the message 21
- I send the message 22
- I send the message 23
- I send the message 24
- I send the message 25
- I send the message 26
- I send the message 27
- I send the message 28
- I send the message 29
- Listener 1: 接收到的消息 I send the message 20
- Listener 1: 接收到的消息 I send the message 22
- Listener 1: 接收到的消息 I send the message 24
- Listener 1: 接收到的消息 I send the message 26
- Listener 1: 接收到的消息 I send the message 28
- Send finish flag: FINISHED
- Send finish flag: FINISHED
- UglyListener 1: 接收到的消息 I send the message 21
- UglyListener 1: 接收到的消息 I send the message 23
- UglyListener 1: 接收到的消息 I send the message 25
- UglyListener 1: 接收到的消息 I send the message 27
- UglyListener 1: 接收到的消息 I send the message 29
- Listener 1: 接收到的消息 FINISHED
- UglyListener 1: 接收到的消息 FINISHED
- QueueProducer connection close!
- QueueReceiveConsumer connection close!
- QueueListenerConsumer connection close!
现在两个消费者都正常结束了。到此我的点对点模式就介绍完了。
订阅/发布模式
生产者:
- package com.darren.activemq.topic;
- import javax.jms.JMSException;
- import javax.jms.Session;
- import javax.jms.TextMessage;
- import com.darren.activemq.ActivemqContants;
- import com.darren.activemq.ProducerConsumer;
- public class TopicProducer extends ProducerConsumer {
- private int startNumber;
- private int endNumber;
- public TopicProducer(String name) throws JMSException {
- this.name = name;
- // 通过连接工厂获取连接
- this.connection = this.getConnection();
- // 启动连接
- this.connection.start();
- // 创建Session
- this.session = this.connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
- // 创建消息队列
- this.destination = this.session.createTopic("test-topic");
- // 创建消息生产者
- this.messageProducer = this.session.createProducer(destination);
- }
- /**
- * 发送消息
- *
- * @throws JMSException
- */
- public void sendMessage() throws JMSException {
- this.startNumber = this.endNumber;
- this.endNumber = this.startNumber + MESSAGE_COUNT;
- for (int i = this.startNumber; i < this.endNumber; i++) {
- TextMessage message = this.session.createTextMessage("I send the message " + i);
- System.out.println(message.getText());
- this.messageProducer.send(message);
- }
- }
- /**
- * 发送结束标志
- *
- * @throws JMSException
- */
- public void sendFinishMessage() throws JMSException {
- TextMessage message = this.session.createTextMessage(ActivemqContants.FINISH_FLAG);
- System.out.println("Send finish flag: " + message.getText());
- this.messageProducer.send(message);
- }
- /**
- * 提交事务
- *
- * @throws JMSException
- */
- public void commit() throws JMSException {
- this.session.commit();
- }
- }
消费者:
- package com.darren.activemq.topic;
- import javax.jms.JMSException;
- import javax.jms.Session;
- import com.darren.activemq.ProducerConsumer;
- import com.darren.activemq.listener.ConsumerListener;
- public class TopicListenerConsumer extends ProducerConsumer {
- public TopicListenerConsumer(String name) throws JMSException {
- this.name = name;
- // 通过连接工厂获取连接
- this.connection = this.getConnection();
- // 启动连接
- this.connection.start();
- // 创建Session
- this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- // 创建连接的消息队列
- this.destination = this.session.createTopic("test-topic");
- // 创建消息消费者
- this.messageConsumer = this.session.createConsumer(destination);
- // 设置消息监听
- this.messageConsumer.setMessageListener(new ConsumerListener("Listener 1:", this));
- }
- }
- package com.darren.activemq.topic;
- import javax.jms.JMSException;
- import javax.jms.Session;
- import com.darren.activemq.ProducerConsumer;
- import com.darren.activemq.listener.UglyConsumerListener;
- public class TopicReceiveConsumer extends ProducerConsumer {
- public TopicReceiveConsumer(String name) throws JMSException {
- this.name = name;
- // 通过连接工厂获取连接
- this.connection = this.getConnection();
- // 启动连接
- this.connection.start();
- // 创建Session
- this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- // 创建连接的消息队列
- this.destination = this.session.createTopic("test-topic");
- // 创建消息消费者
- this.messageConsumer = this.session.createConsumer(destination);
- // 启动一个异步线程接受消息,模拟一个消息监听器
- Thread thread = new Thread(new UglyConsumerListener("UglyListener 1:", this));
- thread.start();
- }
- }
大家可以看出相比点对点模式的例子,消息的订阅/发布模式只做了很小的改动:
- this.destination = this.session.createQueue("test-queue");
- //改成
- this.destination = this.session.createTopic("test-topic");
其他代码都重用了,接下来是测试类:
- package com.darren.activemq.topic;
- import javax.jms.JMSException;
- public class TopicTest {
- public static void main(String[] args) {
- Thread thread = null;
- try {
- // 启动消费者,消费者开始等待
- new TopicListenerConsumer("TopicListenerConsumer");
- new TopicReceiveConsumer("TopicReceiveConsumer");
- thread = new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- // 启动生产者,生产者定时生产消息
- TopicProducer producer = new TopicProducer("TopicProducer");
- Thread.sleep(2000);
- // 第一次发送
- producer.sendMessage();
- producer.commit();
- Thread.sleep(2000);
- // 第二次发送
- producer.sendMessage();
- producer.commit();
- Thread.sleep(2000);
- // 第三次发送
- producer.sendMessage();
- producer.commit();
- // 发送结束标志
- producer.sendFinishMessage();
- producer.commit();
- // 生产者生产完成,关闭连接
- producer.closeConnection();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- thread.start();
- } catch (JMSException e) {
- e.printStackTrace();
- }
- }
- }
运行这个例子:
- INFO | Successfully connected to tcp://localhost:61616
- INFO | Successfully connected to tcp://localhost:61616
- INFO | Successfully connected to tcp://localhost:61616
- I send the message 0
- I send the message 1
- I send the message 2
- I send the message 3
- I send the message 4
- I send the message 5
- I send the message 6
- I send the message 7
- I send the message 8
- I send the message 9
- Listener 1: 接收到的消息 I send the message 0
- Listener 1: 接收到的消息 I send the message 1
- UglyListener 1: 接收到的消息 I send the message 0
- Listener 1: 接收到的消息 I send the message 2
- UglyListener 1: 接收到的消息 I send the message 1
- Listener 1: 接收到的消息 I send the message 3
- Listener 1: 接收到的消息 I send the message 4
- UglyListener 1: 接收到的消息 I send the message 2
- Listener 1: 接收到的消息 I send the message 5
- UglyListener 1: 接收到的消息 I send the message 3
- Listener 1: 接收到的消息 I send the message 6
- Listener 1: 接收到的消息 I send the message 7
- UglyListener 1: 接收到的消息 I send the message 4
- Listener 1: 接收到的消息 I send the message 8
- UglyListener 1: 接收到的消息 I send the message 5
- Listener 1: 接收到的消息 I send the message 9
- UglyListener 1: 接收到的消息 I send the message 6
- UglyListener 1: 接收到的消息 I send the message 7
- UglyListener 1: 接收到的消息 I send the message 8
- UglyListener 1: 接收到的消息 I send the message 9
- I send the message 10
- I send the message 11
- I send the message 12
- I send the message 13
- I send the message 14
- I send the message 15
- I send the message 16
- I send the message 17
- I send the message 18
- I send the message 19
- Listener 1: 接收到的消息 I send the message 10
- Listener 1: 接收到的消息 I send the message 11
- Listener 1: 接收到的消息 I send the message 12
- Listener 1: 接收到的消息 I send the message 13
- Listener 1: 接收到的消息 I send the message 14
- Listener 1: 接收到的消息 I send the message 15
- Listener 1: 接收到的消息 I send the message 16
- Listener 1: 接收到的消息 I send the message 17
- Listener 1: 接收到的消息 I send the message 18
- Listener 1: 接收到的消息 I send the message 19
- UglyListener 1: 接收到的消息 I send the message 10
- UglyListener 1: 接收到的消息 I send the message 11
- UglyListener 1: 接收到的消息 I send the message 12
- UglyListener 1: 接收到的消息 I send the message 13
- UglyListener 1: 接收到的消息 I send the message 14
- UglyListener 1: 接收到的消息 I send the message 15
- UglyListener 1: 接收到的消息 I send the message 16
- UglyListener 1: 接收到的消息 I send the message 17
- UglyListener 1: 接收到的消息 I send the message 18
- UglyListener 1: 接收到的消息 I send the message 19
- I send the message 20
- I send the message 21
- I send the message 22
- I send the message 23
- I send the message 24
- I send the message 25
- I send the message 26
- I send the message 27
- I send the message 28
- I send the message 29
- Listener 1: 接收到的消息 I send the message 20
- Listener 1: 接收到的消息 I send the message 21
- Listener 1: 接收到的消息 I send the message 22
- Listener 1: 接收到的消息 I send the message 23
- Listener 1: 接收到的消息 I send the message 24
- Listener 1: 接收到的消息 I send the message 25
- Listener 1: 接收到的消息 I send the message 26
- Listener 1: 接收到的消息 I send the message 27
- Send finish flag: FINISHED
- UglyListener 1: 接收到的消息 I send the message 20
- UglyListener 1: 接收到的消息 I send the message 21
- Listener 1: 接收到的消息 I send the message 28
- UglyListener 1: 接收到的消息 I send the message 22
- Listener 1: 接收到的消息 I send the message 29
- UglyListener 1: 接收到的消息 I send the message 23
- UglyListener 1: 接收到的消息 I send the message 24
- UglyListener 1: 接收到的消息 I send the message 25
- UglyListener 1: 接收到的消息 I send the message 26
- Listener 1: 接收到的消息 FINISHED
- UglyListener 1: 接收到的消息 I send the message 27
- UglyListener 1: 接收到的消息 I send the message 28
- UglyListener 1: 接收到的消息 I send the message 29
- UglyListener 1: 接收到的消息 FINISHED
- TopicListenerConsumer connection close!
- TopicReceiveConsumer connection close!
- TopicProducer connection close!
两个订阅者获取的消息一模一样,并不是平半分了消息,这就是订阅/发布与点对点的不同。
注:订阅/发布模式必须要先订阅,这样订阅者才能收到消息。
完