上一篇已经讲了JMS的基本概念,这一篇来上手练一练,如果对JMS基本概念还不熟悉,欢迎参靠JMS基本概

这篇文章所使用的代码已经不是我刚入手时的代码,已经经过我重构过的代码,便于理解,并且加了很多中文注释,希望对大家有所帮助。

在基本概念一篇中已经讲到,JMS有两种消息模型,一种是点对点,另一种的发布/订阅模式。本篇文章就基于这两种消息模型来写例子。

点对点模型

先看一下生产者代码:

  1. package com.darren.activemq.queue;
  2. import javax.jms.JMSException;
  3. import javax.jms.Session;
  4. import javax.jms.TextMessage;
  5. import com.darren.activemq.ActivemqContants;
  6. import com.darren.activemq.ProducerConsumer;
  7. /**
  8. * 消息生产类
  9. *
  10. * @author Darren
  11. *
  12. */
  13. public class QueueProducer extends ProducerConsumer {
  14. private int startNumber;
  15. private int endNumber;
  16. public QueueProducer(String name) throws JMSException {
  17. this.name = name;
  18. // 通过连接工厂获取连接
  19. this.connection = this.getConnection();
  20. // 启动连接
  21. this.connection.start();
  22. // 创建Session
  23. this.session = this.connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
  24. // 创建消息队列
  25. this.destination = this.session.createQueue("test-queue");
  26. // 创建消息生产者
  27. this.messageProducer = this.session.createProducer(destination);
  28. }
  29. /**
  30. * 发送消息
  31. *
  32. * @throws JMSException
  33. */
  34. public void sendMessage() throws JMSException {
  35. this.startNumber = this.endNumber;
  36. this.endNumber = this.startNumber + MESSAGE_COUNT;
  37. for (int i = this.startNumber; i < this.endNumber; i++) {
  38. TextMessage message = this.session.createTextMessage("I send the message " + i);
  39. System.out.println(message.getText());
  40. this.messageProducer.send(message);
  41. }
  42. }
  43. /**
  44. * 发送结束标志
  45. *
  46. * @param times
  47. *            发送次数
  48. * @throws JMSException
  49. */
  50. public void sendFinishMessage(int times) throws JMSException {
  51. for (int i = 0; i < times; i++) {
  52. TextMessage message = this.session.createTextMessage(ActivemqContants.FINISH_FLAG);
  53. System.out.println("Send finish flag: " + message.getText());
  54. this.messageProducer.send(message);
  55. }
  56. }
  57. /**
  58. * 提交事务
  59. *
  60. * @throws JMSException
  61. */
  62. public void commit() throws JMSException {
  63. this.session.commit();
  64. }
  65. }

我写了两种消息消费者,其实也就是上一篇讲的同步消费和异步消费。

先来看异步消费者:

  1. package com.darren.activemq.queue;
  2. import javax.jms.JMSException;
  3. import javax.jms.Session;
  4. import com.darren.activemq.ProducerConsumer;
  5. import com.darren.activemq.listener.ConsumerListener;
  6. /**
  7. * 消息消费者
  8. *
  9. * @author Darren
  10. *
  11. */
  12. public class QueueListenerConsumer extends ProducerConsumer {
  13. public QueueListenerConsumer(String name) throws JMSException {
  14. this.name = name;
  15. // 通过连接工厂获取连接
  16. this.connection = this.getConnection();
  17. // 启动连接
  18. this.connection.start();
  19. // 创建Session
  20. this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  21. // 创建连接的消息队列
  22. this.destination = this.session.createQueue("test-queue");
  23. // 创建消息消费者
  24. this.messageConsumer = this.session.createConsumer(destination);
  25. // 设置消息监听
  26. this.messageConsumer.setMessageListener(new ConsumerListener("Listener 1:", this));
  27. }
  28. }

异步消费者通过设置监听器来达到异步消费消息的目的。其实这个监听器也很简单,我们来看一下。

  1. package com.darren.activemq.listener;
  2. import javax.jms.Message;
  3. import javax.jms.MessageListener;
  4. import javax.jms.ObjectMessage;
  5. import javax.jms.TextMessage;
  6. import com.darren.activemq.ActivemqContants;
  7. import com.darren.activemq.ProducerConsumer;
  8. /**
  9. * 消息监听
  10. *
  11. * @author Darren
  12. *
  13. */
  14. public class ConsumerListener implements MessageListener {
  15. private String name;
  16. private ProducerConsumer producerConsumer;
  17. public ConsumerListener(String name, ProducerConsumer producerConsumer) {
  18. this.name = name;
  19. this.producerConsumer = producerConsumer;
  20. }
  21. @Override
  22. public void onMessage(Message message) {
  23. try {
  24. if (message instanceof TextMessage) {
  25. TextMessage textMessage = (TextMessage) message;
  26. System.out.println(name + " 接收到的消息 " + textMessage.getText());
  27. // 如果接收到结束标志,修改消费者的状态
  28. if (ActivemqContants.FINISH_FLAG.equals(textMessage.getText())) {
  29. // 消费者消费完成,关闭连接
  30. this.producerConsumer.closeConnection();
  31. }
  32. } else if (message instanceof ObjectMessage) {
  33. ObjectMessage objectMessage = (ObjectMessage) message;
  34. System.out.println(name + " 接收到的消息 " + objectMessage.getObject());
  35. } else {
  36. System.out.println("不支持的消息类型!");
  37. }
  38. } catch (Exception e) {
  39. e.printStackTrace();
  40. }
  41. }
  42. }

这里只有一个方法。

为了便于理解,我先把父类贴出来,这样就可以基本完整的看了。

  1. package com.darren.activemq;
  2. import javax.jms.Connection;
  3. import javax.jms.ConnectionFactory;
  4. import javax.jms.Destination;
  5. import javax.jms.JMSException;
  6. import javax.jms.MessageConsumer;
  7. import javax.jms.MessageProducer;
  8. import javax.jms.Session;
  9. import org.apache.activemq.ActiveMQConnection;
  10. import org.apache.activemq.ActiveMQConnectionFactory;
  11. public abstract class ProducerConsumer {
  12. protected static final String USERNAME = ActiveMQConnection.DEFAULT_USER;// 默认的连接用户名
  13. protected static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;// 默认的连接密码
  14. protected static final String BROKERURL = ActiveMQConnection.DEFAULT_BROKER_URL;// 默认的连接地址
  15. protected static final int MESSAGE_COUNT = 10;// 发送的消息数量
  16. protected static ConnectionFactory connectionFactory;// 连接工厂
  17. protected Connection connection = null; // 连接
  18. protected Session session;// 会话 接受或者发送消息的线程
  19. protected Destination destination;// 消息的目的地
  20. protected MessageProducer messageProducer;// 消息生产者
  21. protected MessageConsumer messageConsumer;// 消息消费者
  22. // 状态
  23. protected volatile boolean isFinished = false;
  24. protected String name;
  25. static {
  26. getConnectionFactory();
  27. }
  28. /**
  29. * 获取连接工厂
  30. *
  31. * @return
  32. */
  33. protected synchronized static ConnectionFactory getConnectionFactory() {
  34. if (connectionFactory == null) {
  35. connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKERURL);
  36. }
  37. return connectionFactory;
  38. }
  39. /**
  40. * 获取连接
  41. *
  42. * @return
  43. * @throws JMSException
  44. */
  45. protected Connection getConnection() throws JMSException {
  46. return connectionFactory.createConnection();
  47. };
  48. /**
  49. * 关闭连接
  50. *
  51. * @throws JMSException
  52. */
  53. public void closeConnection() throws JMSException {
  54. if (this.connection != null) {
  55. this.connection.close();
  56. }
  57. System.out.println(this.name + " connection close!");
  58. this.isFinished = true;
  59. }
  60. /**
  61. * 获取状态
  62. *
  63. * @return
  64. */
  65. public boolean isFinished() {
  66. return isFinished;
  67. }
  68. /**
  69. * 获取消费者
  70. *
  71. * @return
  72. */
  73. public MessageConsumer getMessageConsumer() {
  74. return messageConsumer;
  75. }
  76. /**
  77. * 获取生产者或消费者名称
  78. *
  79. * @return
  80. */
  81. public String getName() {
  82. return name;
  83. }
  84. }

用到的常量类

  1. package com.darren.activemq;
  2. public class ActivemqContants {
  3. public static final String FINISH_FLAG = "FINISHED";
  4. }

来个测试类吧,这样就完整了

  1. package com.darren.activemq.queue;
  2. import javax.jms.JMSException;
  3. public class QueueTest {
  4. public static void main(String[] args) {
  5. Thread thread = null;
  6. try {
  7. // 启动消费者,消费者开始等待
  8. new QueueListenerConsumer("QueueListenerConsumer");
  9. //new QueueReceiveConsumer("QueueReceiveConsumer");
  10. thread = new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. try {
  14. // 启动生产者,生产者定时生产消息
  15. QueueProducer producer = new QueueProducer("QueueProducer");
  16. Thread.sleep(2000);
  17. // 第一次发送
  18. producer.sendMessage();
  19. producer.commit();
  20. Thread.sleep(2000);
  21. // 第二次发送
  22. producer.sendMessage();
  23. producer.commit();
  24. Thread.sleep(2000);
  25. // 第三次发送
  26. producer.sendMessage();
  27. producer.commit();
  28. // 发送结束标志
  29. producer.sendFinishMessage(1);
  30. producer.commit();
  31. // 生产者生产完成,关闭连接
  32. producer.closeConnection();
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. });
  38. thread.start();
  39. } catch (JMSException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }

现在为止,一个异步消息的完整例子就真的完整了,我是为了便于看到消息的消费过程,所以把生产者放到了一个线程里来不断的生产消息,来通过观察消费者打印的消费信息,了解消费的过程。

再来看看同步消息模式:

同步消费的方式就是轮训的去要有没有消息,有没有消息。为了适应我的测试类,我把同步消息消费也按照异步消费的模式做了相应的改动,下边就是代码:

  1. package com.darren.activemq.queue;
  2. import javax.jms.JMSException;
  3. import javax.jms.Session;
  4. import com.darren.activemq.ProducerConsumer;
  5. import com.darren.activemq.listener.UglyConsumerListener;
  6. /**
  7. * 消息消费者
  8. *
  9. * @author Darren
  10. *
  11. */
  12. public class QueueReceiveConsumer extends ProducerConsumer {
  13. public QueueReceiveConsumer(String name) throws JMSException {
  14. this.name = name;
  15. // 通过连接工厂获取连接
  16. this.connection = this.getConnection();
  17. // 启动连接
  18. this.connection.start();
  19. // 创建Session
  20. this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  21. // 创建连接的消息队列
  22. this.destination = this.session.createQueue("test-queue");
  23. // 创建消息消费者
  24. this.messageConsumer = this.session.createConsumer(destination);
  25. // 启动一个异步线程接受消息,模拟一个消息监听器
  26. Thread thread = new Thread(new UglyConsumerListener("UglyListener 1:", this));
  27. thread.start();
  28. }
  29. }

在这里,我选择启动一个线程去设置一个监听器,为什么要启动线程,因为我上边提到了要适应我的测试类,我是先初始化消费者,如果不用线程,那么消费者就会在要消息的时候夯住,因为:

  1. Message message = messageConsumer.receive(100000);

这个方法是阻塞式方法。会程序在初始化消费者后就会夯住,然后生成者无法启动,无法生成消息,整个程序就死锁了,为了解决这个问题,所以采用模拟异步消费的方式来写同步消费例子。

下边来看我写的丑陋的监听器:

  1. package com.darren.activemq.listener;
  2. import javax.jms.JMSException;
  3. import javax.jms.Message;
  4. import javax.jms.MessageConsumer;
  5. import javax.jms.ObjectMessage;
  6. import javax.jms.TextMessage;
  7. import com.darren.activemq.ActivemqContants;
  8. import com.darren.activemq.ProducerConsumer;
  9. /**
  10. * 消息监听
  11. *
  12. * @author Darren
  13. *
  14. */
  15. public class UglyConsumerListener implements Runnable {
  16. private String name;
  17. private ProducerConsumer producerConsumer;
  18. public UglyConsumerListener(String name, ProducerConsumer producerConsumer) {
  19. this.name = name;
  20. this.producerConsumer = producerConsumer;
  21. }
  22. public void onMessage(Message message) {
  23. try {
  24. if (message instanceof TextMessage) {
  25. TextMessage textMessage = (TextMessage) message;
  26. System.out.println(name + " 接收到的消息 " + textMessage.getText());
  27. // 如果接收到结束标志,修改消费者的状态
  28. if (ActivemqContants.FINISH_FLAG.equals(textMessage.getText())) {
  29. // 消费者消费完成,关闭连接
  30. this.producerConsumer.closeConnection();
  31. }
  32. } else if (message instanceof ObjectMessage) {
  33. ObjectMessage objectMessage = (ObjectMessage) message;
  34. System.out.println(name + " 接收到的消息 " + objectMessage.getObject());
  35. } else {
  36. System.out.println("不支持的消息类型!");
  37. }
  38. } catch (Exception e) {
  39. e.printStackTrace();
  40. }
  41. }
  42. @Override
  43. public void run() {
  44. while (!producerConsumer.isFinished()) {
  45. MessageConsumer messageConsumer = this.producerConsumer.getMessageConsumer();
  46. if (messageConsumer != null) {
  47. try {
  48. Message message = messageConsumer.receive(100000);
  49. this.onMessage(message);
  50. } catch (JMSException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. }
  55. }
  56. }

为什么称为丑陋的监听器,相信大家也不难发现,因为采用循环方式,并不是真正的监听器。 看了这两个监听器,那么我提出一个问题,这个问题也是在我写这篇文章的时候发现的,如果我再写一个抽象类去作为这两个监听器的父类,把onMessage方法提出来,是不是更加优化了,因为我发现两个onMessage方法其实一样,我也是故意给做的一样的。

当然今天讲的并不是Java的重构,而是JMS,这个问题就作为题外话好了。

现在你自己也可以去跑一跑这两个例子,细心的同学或发现在测试类中其实有一行有疑问。为什么发送结束消息的方法要加参数,可以设置发送多个结束消息。这个问题我们稍后介绍,下面我来运行一下代码,也许你就渐渐明白。

先跑一个异步消费的例子:

  1. INFO | Successfully connected to tcp://localhost:61616
  2. INFO | Successfully connected to tcp://localhost:61616
  3. I send the message 0
  4. I send the message 1
  5. I send the message 2
  6. I send the message 3
  7. I send the message 4
  8. I send the message 5
  9. I send the message 6
  10. I send the message 7
  11. I send the message 8
  12. I send the message 9
  13. Listener 1: 接收到的消息 I send the message 0
  14. Listener 1: 接收到的消息 I send the message 1
  15. Listener 1: 接收到的消息 I send the message 2
  16. Listener 1: 接收到的消息 I send the message 3
  17. Listener 1: 接收到的消息 I send the message 4
  18. Listener 1: 接收到的消息 I send the message 5
  19. Listener 1: 接收到的消息 I send the message 6
  20. Listener 1: 接收到的消息 I send the message 7
  21. Listener 1: 接收到的消息 I send the message 8
  22. Listener 1: 接收到的消息 I send the message 9
  23. I send the message 10
  24. I send the message 11
  25. I send the message 12
  26. I send the message 13
  27. I send the message 14
  28. I send the message 15
  29. I send the message 16
  30. I send the message 17
  31. I send the message 18
  32. I send the message 19
  33. Listener 1: 接收到的消息 I send the message 10
  34. Listener 1: 接收到的消息 I send the message 11
  35. Listener 1: 接收到的消息 I send the message 12
  36. Listener 1: 接收到的消息 I send the message 13
  37. Listener 1: 接收到的消息 I send the message 14
  38. Listener 1: 接收到的消息 I send the message 15
  39. Listener 1: 接收到的消息 I send the message 16
  40. Listener 1: 接收到的消息 I send the message 17
  41. Listener 1: 接收到的消息 I send the message 18
  42. Listener 1: 接收到的消息 I send the message 19
  43. I send the message 20
  44. I send the message 21
  45. I send the message 22
  46. I send the message 23
  47. I send the message 24
  48. I send the message 25
  49. I send the message 26
  50. I send the message 27
  51. I send the message 28
  52. I send the message 29
  53. Listener 1: 接收到的消息 I send the message 20
  54. Listener 1: 接收到的消息 I send the message 21
  55. Send finish flag: FINISHED
  56. Listener 1: 接收到的消息 I send the message 22
  57. Listener 1: 接收到的消息 I send the message 23
  58. Listener 1: 接收到的消息 I send the message 24
  59. Listener 1: 接收到的消息 I send the message 25
  60. Listener 1: 接收到的消息 I send the message 26
  61. Listener 1: 接收到的消息 I send the message 27
  62. Listener 1: 接收到的消息 I send the message 28
  63. Listener 1: 接收到的消息 I send the message 29
  64. Listener 1: 接收到的消息 FINISHED
  65. QueueProducer connection close!
  66. QueueListenerConsumer connection close!

然后再来跑一个同步消费的例子:

  1. //new QueueListenerConsumer("QueueListenerConsumer");
  2. new QueueReceiveConsumer("QueueReceiveConsumer");

只需把注水换一换就行了。

  1. INFO | Successfully connected to tcp://localhost:61616
  2. INFO | Successfully connected to tcp://localhost:61616
  3. I send the message 0
  4. I send the message 1
  5. I send the message 2
  6. I send the message 3
  7. I send the message 4
  8. I send the message 5
  9. I send the message 6
  10. I send the message 7
  11. I send the message 8
  12. I send the message 9
  13. UglyListener 1: 接收到的消息 I send the message 0
  14. UglyListener 1: 接收到的消息 I send the message 1
  15. UglyListener 1: 接收到的消息 I send the message 2
  16. UglyListener 1: 接收到的消息 I send the message 3
  17. UglyListener 1: 接收到的消息 I send the message 4
  18. UglyListener 1: 接收到的消息 I send the message 5
  19. UglyListener 1: 接收到的消息 I send the message 6
  20. UglyListener 1: 接收到的消息 I send the message 7
  21. UglyListener 1: 接收到的消息 I send the message 8
  22. UglyListener 1: 接收到的消息 I send the message 9
  23. I send the message 10
  24. I send the message 11
  25. I send the message 12
  26. I send the message 13
  27. I send the message 14
  28. I send the message 15
  29. I send the message 16
  30. I send the message 17
  31. I send the message 18
  32. I send the message 19
  33. UglyListener 1: 接收到的消息 I send the message 10
  34. UglyListener 1: 接收到的消息 I send the message 11
  35. UglyListener 1: 接收到的消息 I send the message 12
  36. UglyListener 1: 接收到的消息 I send the message 13
  37. UglyListener 1: 接收到的消息 I send the message 14
  38. UglyListener 1: 接收到的消息 I send the message 15
  39. UglyListener 1: 接收到的消息 I send the message 16
  40. UglyListener 1: 接收到的消息 I send the message 17
  41. UglyListener 1: 接收到的消息 I send the message 18
  42. UglyListener 1: 接收到的消息 I send the message 19
  43. I send the message 20
  44. I send the message 21
  45. I send the message 22
  46. I send the message 23
  47. I send the message 24
  48. I send the message 25
  49. I send the message 26
  50. I send the message 27
  51. I send the message 28
  52. I send the message 29
  53. Send finish flag: FINISHED
  54. UglyListener 1: 接收到的消息 I send the message 20
  55. UglyListener 1: 接收到的消息 I send the message 21
  56. UglyListener 1: 接收到的消息 I send the message 22
  57. UglyListener 1: 接收到的消息 I send the message 23
  58. UglyListener 1: 接收到的消息 I send the message 24
  59. UglyListener 1: 接收到的消息 I send the message 25
  60. UglyListener 1: 接收到的消息 I send the message 26
  61. UglyListener 1: 接收到的消息 I send the message 27
  62. UglyListener 1: 接收到的消息 I send the message 28
  63. UglyListener 1: 接收到的消息 I send the message 29
  64. UglyListener 1: 接收到的消息 FINISHED
  65. QueueReceiveConsumer connection close!
  66. QueueProducer connection close!

大家会发现和异步消费并未区别,因为其实也是异步消费。

那么如果我现在把测试类的注释都打开,会是什么结果呢:

  1. new QueueListenerConsumer("QueueListenerConsumer");
  2. new QueueReceiveConsumer("QueueReceiveConsumer");
  1. INFO | Successfully connected to tcp://localhost:61616
  2. INFO | Successfully connected to tcp://localhost:61616
  3. INFO | Successfully connected to tcp://localhost:61616
  4. I send the message 0
  5. I send the message 1
  6. I send the message 2
  7. I send the message 3
  8. I send the message 4
  9. I send the message 5
  10. I send the message 6
  11. I send the message 7
  12. I send the message 8
  13. I send the message 9
  14. Listener 1: 接收到的消息 I send the message 0
  15. Listener 1: 接收到的消息 I send the message 2
  16. UglyListener 1: 接收到的消息 I send the message 1
  17. Listener 1: 接收到的消息 I send the message 4
  18. Listener 1: 接收到的消息 I send the message 6
  19. Listener 1: 接收到的消息 I send the message 8
  20. UglyListener 1: 接收到的消息 I send the message 3
  21. UglyListener 1: 接收到的消息 I send the message 5
  22. UglyListener 1: 接收到的消息 I send the message 7
  23. UglyListener 1: 接收到的消息 I send the message 9
  24. I send the message 10
  25. I send the message 11
  26. I send the message 12
  27. I send the message 13
  28. I send the message 14
  29. I send the message 15
  30. I send the message 16
  31. I send the message 17
  32. I send the message 18
  33. I send the message 19
  34. Listener 1: 接收到的消息 I send the message 10
  35. Listener 1: 接收到的消息 I send the message 12
  36. Listener 1: 接收到的消息 I send the message 14
  37. UglyListener 1: 接收到的消息 I send the message 11
  38. Listener 1: 接收到的消息 I send the message 16
  39. UglyListener 1: 接收到的消息 I send the message 13
  40. Listener 1: 接收到的消息 I send the message 18
  41. UglyListener 1: 接收到的消息 I send the message 15
  42. UglyListener 1: 接收到的消息 I send the message 17
  43. UglyListener 1: 接收到的消息 I send the message 19
  44. I send the message 20
  45. I send the message 21
  46. I send the message 22
  47. I send the message 23
  48. I send the message 24
  49. I send the message 25
  50. I send the message 26
  51. I send the message 27
  52. I send the message 28
  53. I send the message 29
  54. Listener 1: 接收到的消息 I send the message 20
  55. Listener 1: 接收到的消息 I send the message 22
  56. Listener 1: 接收到的消息 I send the message 24
  57. Listener 1: 接收到的消息 I send the message 26
  58. Listener 1: 接收到的消息 I send the message 28
  59. Send finish flag: FINISHED
  60. UglyListener 1: 接收到的消息 I send the message 21
  61. UglyListener 1: 接收到的消息 I send the message 23
  62. UglyListener 1: 接收到的消息 I send the message 25
  63. UglyListener 1: 接收到的消息 I send the message 27
  64. UglyListener 1: 接收到的消息 I send the message 29
  65. Listener 1: 接收到的消息 FINISHED
  66. QueueProducer connection close!
  67. QueueListenerConsumer connection close!

这个时候有俩个消费者,一个消费者消费了一半的消息,这里我没有深入研究这种消费平均的方式是随机的还是真的平均,我猜猜可能是真的平均,因为我运行了多次都是同样的结果。

通过我打出来的消息,发现只有一个消费者结束了,另一个并没有结束,因为没有收到结束消息标志,所以需要发送两个结束标志才能使两个消费者都能结束。

  1. producer.sendFinishMessage(2);

这也正是我设置参数的原因:

改成2之后再来运行一次:

  1. INFO | Successfully connected to tcp://localhost:61616
  2. INFO | Successfully connected to tcp://localhost:61616
  3. INFO | Successfully connected to tcp://localhost:61616
  4. I send the message 0
  5. I send the message 1
  6. I send the message 2
  7. I send the message 3
  8. I send the message 4
  9. I send the message 5
  10. I send the message 6
  11. I send the message 7
  12. I send the message 8
  13. I send the message 9
  14. Listener 1: 接收到的消息 I send the message 0
  15. Listener 1: 接收到的消息 I send the message 2
  16. UglyListener 1: 接收到的消息 I send the message 1
  17. Listener 1: 接收到的消息 I send the message 4
  18. UglyListener 1: 接收到的消息 I send the message 3
  19. Listener 1: 接收到的消息 I send the message 6
  20. UglyListener 1: 接收到的消息 I send the message 5
  21. Listener 1: 接收到的消息 I send the message 8
  22. UglyListener 1: 接收到的消息 I send the message 7
  23. UglyListener 1: 接收到的消息 I send the message 9
  24. I send the message 10
  25. I send the message 11
  26. I send the message 12
  27. I send the message 13
  28. I send the message 14
  29. I send the message 15
  30. I send the message 16
  31. I send the message 17
  32. I send the message 18
  33. I send the message 19
  34. Listener 1: 接收到的消息 I send the message 10
  35. Listener 1: 接收到的消息 I send the message 12
  36. Listener 1: 接收到的消息 I send the message 14
  37. Listener 1: 接收到的消息 I send the message 16
  38. Listener 1: 接收到的消息 I send the message 18
  39. UglyListener 1: 接收到的消息 I send the message 11
  40. UglyListener 1: 接收到的消息 I send the message 13
  41. UglyListener 1: 接收到的消息 I send the message 15
  42. UglyListener 1: 接收到的消息 I send the message 17
  43. UglyListener 1: 接收到的消息 I send the message 19
  44. I send the message 20
  45. I send the message 21
  46. I send the message 22
  47. I send the message 23
  48. I send the message 24
  49. I send the message 25
  50. I send the message 26
  51. I send the message 27
  52. I send the message 28
  53. I send the message 29
  54. Listener 1: 接收到的消息 I send the message 20
  55. Listener 1: 接收到的消息 I send the message 22
  56. Listener 1: 接收到的消息 I send the message 24
  57. Listener 1: 接收到的消息 I send the message 26
  58. Listener 1: 接收到的消息 I send the message 28
  59. Send finish flag: FINISHED
  60. Send finish flag: FINISHED
  61. UglyListener 1: 接收到的消息 I send the message 21
  62. UglyListener 1: 接收到的消息 I send the message 23
  63. UglyListener 1: 接收到的消息 I send the message 25
  64. UglyListener 1: 接收到的消息 I send the message 27
  65. UglyListener 1: 接收到的消息 I send the message 29
  66. Listener 1: 接收到的消息 FINISHED
  67. UglyListener 1: 接收到的消息 FINISHED
  68. QueueProducer connection close!
  69. QueueReceiveConsumer connection close!
  70. QueueListenerConsumer connection close!

现在两个消费者都正常结束了。到此我的点对点模式就介绍完了。

订阅/发布模式

生产者:

  1. package com.darren.activemq.topic;
  2. import javax.jms.JMSException;
  3. import javax.jms.Session;
  4. import javax.jms.TextMessage;
  5. import com.darren.activemq.ActivemqContants;
  6. import com.darren.activemq.ProducerConsumer;
  7. public class TopicProducer extends ProducerConsumer {
  8. private int startNumber;
  9. private int endNumber;
  10. public TopicProducer(String name) throws JMSException {
  11. this.name = name;
  12. // 通过连接工厂获取连接
  13. this.connection = this.getConnection();
  14. // 启动连接
  15. this.connection.start();
  16. // 创建Session
  17. this.session = this.connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
  18. // 创建消息队列
  19. this.destination = this.session.createTopic("test-topic");
  20. // 创建消息生产者
  21. this.messageProducer = this.session.createProducer(destination);
  22. }
  23. /**
  24. * 发送消息
  25. *
  26. * @throws JMSException
  27. */
  28. public void sendMessage() throws JMSException {
  29. this.startNumber = this.endNumber;
  30. this.endNumber = this.startNumber + MESSAGE_COUNT;
  31. for (int i = this.startNumber; i < this.endNumber; i++) {
  32. TextMessage message = this.session.createTextMessage("I send the message " + i);
  33. System.out.println(message.getText());
  34. this.messageProducer.send(message);
  35. }
  36. }
  37. /**
  38. * 发送结束标志
  39. *
  40. * @throws JMSException
  41. */
  42. public void sendFinishMessage() throws JMSException {
  43. TextMessage message = this.session.createTextMessage(ActivemqContants.FINISH_FLAG);
  44. System.out.println("Send finish flag: " + message.getText());
  45. this.messageProducer.send(message);
  46. }
  47. /**
  48. * 提交事务
  49. *
  50. * @throws JMSException
  51. */
  52. public void commit() throws JMSException {
  53. this.session.commit();
  54. }
  55. }

消费者:

  1. package com.darren.activemq.topic;
  2. import javax.jms.JMSException;
  3. import javax.jms.Session;
  4. import com.darren.activemq.ProducerConsumer;
  5. import com.darren.activemq.listener.ConsumerListener;
  6. public class TopicListenerConsumer extends ProducerConsumer {
  7. public TopicListenerConsumer(String name) throws JMSException {
  8. this.name = name;
  9. // 通过连接工厂获取连接
  10. this.connection = this.getConnection();
  11. // 启动连接
  12. this.connection.start();
  13. // 创建Session
  14. this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  15. // 创建连接的消息队列
  16. this.destination = this.session.createTopic("test-topic");
  17. // 创建消息消费者
  18. this.messageConsumer = this.session.createConsumer(destination);
  19. // 设置消息监听
  20. this.messageConsumer.setMessageListener(new ConsumerListener("Listener 1:", this));
  21. }
  22. }
  1. package com.darren.activemq.topic;
  2. import javax.jms.JMSException;
  3. import javax.jms.Session;
  4. import com.darren.activemq.ProducerConsumer;
  5. import com.darren.activemq.listener.UglyConsumerListener;
  6. public class TopicReceiveConsumer extends ProducerConsumer {
  7. public TopicReceiveConsumer(String name) throws JMSException {
  8. this.name = name;
  9. // 通过连接工厂获取连接
  10. this.connection = this.getConnection();
  11. // 启动连接
  12. this.connection.start();
  13. // 创建Session
  14. this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  15. // 创建连接的消息队列
  16. this.destination = this.session.createTopic("test-topic");
  17. // 创建消息消费者
  18. this.messageConsumer = this.session.createConsumer(destination);
  19. // 启动一个异步线程接受消息,模拟一个消息监听器
  20. Thread thread = new Thread(new UglyConsumerListener("UglyListener 1:", this));
  21. thread.start();
  22. }
  23. }

大家可以看出相比点对点模式的例子,消息的订阅/发布模式只做了很小的改动:

  1. this.destination = this.session.createQueue("test-queue");
  2. //改成
  3. this.destination = this.session.createTopic("test-topic");

其他代码都重用了,接下来是测试类:

  1. package com.darren.activemq.topic;
  2. import javax.jms.JMSException;
  3. public class TopicTest {
  4. public static void main(String[] args) {
  5. Thread thread = null;
  6. try {
  7. // 启动消费者,消费者开始等待
  8. new TopicListenerConsumer("TopicListenerConsumer");
  9. new TopicReceiveConsumer("TopicReceiveConsumer");
  10. thread = new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. try {
  14. // 启动生产者,生产者定时生产消息
  15. TopicProducer producer = new TopicProducer("TopicProducer");
  16. Thread.sleep(2000);
  17. // 第一次发送
  18. producer.sendMessage();
  19. producer.commit();
  20. Thread.sleep(2000);
  21. // 第二次发送
  22. producer.sendMessage();
  23. producer.commit();
  24. Thread.sleep(2000);
  25. // 第三次发送
  26. producer.sendMessage();
  27. producer.commit();
  28. // 发送结束标志
  29. producer.sendFinishMessage();
  30. producer.commit();
  31. // 生产者生产完成,关闭连接
  32. producer.closeConnection();
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. });
  38. thread.start();
  39. } catch (JMSException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }

运行这个例子:

  1. INFO | Successfully connected to tcp://localhost:61616
  2. INFO | Successfully connected to tcp://localhost:61616
  3. INFO | Successfully connected to tcp://localhost:61616
  4. I send the message 0
  5. I send the message 1
  6. I send the message 2
  7. I send the message 3
  8. I send the message 4
  9. I send the message 5
  10. I send the message 6
  11. I send the message 7
  12. I send the message 8
  13. I send the message 9
  14. Listener 1: 接收到的消息 I send the message 0
  15. Listener 1: 接收到的消息 I send the message 1
  16. UglyListener 1: 接收到的消息 I send the message 0
  17. Listener 1: 接收到的消息 I send the message 2
  18. UglyListener 1: 接收到的消息 I send the message 1
  19. Listener 1: 接收到的消息 I send the message 3
  20. Listener 1: 接收到的消息 I send the message 4
  21. UglyListener 1: 接收到的消息 I send the message 2
  22. Listener 1: 接收到的消息 I send the message 5
  23. UglyListener 1: 接收到的消息 I send the message 3
  24. Listener 1: 接收到的消息 I send the message 6
  25. Listener 1: 接收到的消息 I send the message 7
  26. UglyListener 1: 接收到的消息 I send the message 4
  27. Listener 1: 接收到的消息 I send the message 8
  28. UglyListener 1: 接收到的消息 I send the message 5
  29. Listener 1: 接收到的消息 I send the message 9
  30. UglyListener 1: 接收到的消息 I send the message 6
  31. UglyListener 1: 接收到的消息 I send the message 7
  32. UglyListener 1: 接收到的消息 I send the message 8
  33. UglyListener 1: 接收到的消息 I send the message 9
  34. I send the message 10
  35. I send the message 11
  36. I send the message 12
  37. I send the message 13
  38. I send the message 14
  39. I send the message 15
  40. I send the message 16
  41. I send the message 17
  42. I send the message 18
  43. I send the message 19
  44. Listener 1: 接收到的消息 I send the message 10
  45. Listener 1: 接收到的消息 I send the message 11
  46. Listener 1: 接收到的消息 I send the message 12
  47. Listener 1: 接收到的消息 I send the message 13
  48. Listener 1: 接收到的消息 I send the message 14
  49. Listener 1: 接收到的消息 I send the message 15
  50. Listener 1: 接收到的消息 I send the message 16
  51. Listener 1: 接收到的消息 I send the message 17
  52. Listener 1: 接收到的消息 I send the message 18
  53. Listener 1: 接收到的消息 I send the message 19
  54. UglyListener 1: 接收到的消息 I send the message 10
  55. UglyListener 1: 接收到的消息 I send the message 11
  56. UglyListener 1: 接收到的消息 I send the message 12
  57. UglyListener 1: 接收到的消息 I send the message 13
  58. UglyListener 1: 接收到的消息 I send the message 14
  59. UglyListener 1: 接收到的消息 I send the message 15
  60. UglyListener 1: 接收到的消息 I send the message 16
  61. UglyListener 1: 接收到的消息 I send the message 17
  62. UglyListener 1: 接收到的消息 I send the message 18
  63. UglyListener 1: 接收到的消息 I send the message 19
  64. I send the message 20
  65. I send the message 21
  66. I send the message 22
  67. I send the message 23
  68. I send the message 24
  69. I send the message 25
  70. I send the message 26
  71. I send the message 27
  72. I send the message 28
  73. I send the message 29
  74. Listener 1: 接收到的消息 I send the message 20
  75. Listener 1: 接收到的消息 I send the message 21
  76. Listener 1: 接收到的消息 I send the message 22
  77. Listener 1: 接收到的消息 I send the message 23
  78. Listener 1: 接收到的消息 I send the message 24
  79. Listener 1: 接收到的消息 I send the message 25
  80. Listener 1: 接收到的消息 I send the message 26
  81. Listener 1: 接收到的消息 I send the message 27
  82. Send finish flag: FINISHED
  83. UglyListener 1: 接收到的消息 I send the message 20
  84. UglyListener 1: 接收到的消息 I send the message 21
  85. Listener 1: 接收到的消息 I send the message 28
  86. UglyListener 1: 接收到的消息 I send the message 22
  87. Listener 1: 接收到的消息 I send the message 29
  88. UglyListener 1: 接收到的消息 I send the message 23
  89. UglyListener 1: 接收到的消息 I send the message 24
  90. UglyListener 1: 接收到的消息 I send the message 25
  91. UglyListener 1: 接收到的消息 I send the message 26
  92. Listener 1: 接收到的消息 FINISHED
  93. UglyListener 1: 接收到的消息 I send the message 27
  94. UglyListener 1: 接收到的消息 I send the message 28
  95. UglyListener 1: 接收到的消息 I send the message 29
  96. UglyListener 1: 接收到的消息 FINISHED
  97. TopicListenerConsumer connection close!
  98. TopicReceiveConsumer connection close!
  99. TopicProducer connection close!

两个订阅者获取的消息一模一样,并不是平半分了消息,这就是订阅/发布与点对点的不同。

注:订阅/发布模式必须要先订阅,这样订阅者才能收到消息。

JMS Activemq实战例子demo的更多相关文章

  1. spring +ActiveMQ 实战 topic selecter指定接收

    spring +ActiveMQ 实战 topic selecter指定接收 queue:点对点模式,一个消息只能由一个消费者接受 topic:一对多,发布/订阅模式,需要消费者都在线(可能会导致信息 ...

  2. SpringBoot使用JMS(activeMQ)的两种方式 队列消息、订阅/发布

    刚好最近同事问我activemq的问题刚接触所以分不清,前段时间刚好项目中有用到,所以稍微整理了一下,仅用于使用 1.下载ActiveMQ 地址:http://activemq.apache.org/ ...

  3. php冒泡排序实现方法,传入几个数字排序后 输出实战例子

    php冒泡排序实现方法,传入几个数字排序后 输出实战例子 算法和数据结构是一个编程工作人员的内功.四种入门级排序算法: 冒泡排序.选择排序.插入排序.快速排序. 一.冒泡排序 原理:对一组数据,比较相 ...

  4. ActiveMQ实战篇之ActiveMQ实现request/reply模型(二)

    ActiveMQ实战篇之ActiveMQ实现request/reply模型(二)

  5. mysql下的将多个字段名的值复制到另一个字段名中(批量更新数据)字符串拼接cancat实战例子

    mysql下的将多个字段名的值复制到另一个字段名中(批量更新数据)mysql字符串拼接cancat实战例子: mysql update set 多个字段相加,如果是数字相加可以直接用+号(注:hund ...

  6. [机器学习]-K近邻-最简单的入门实战例子

    本篇文章分为两个部分,前一部分主要简单介绍K近邻,后一部分是一个例子 第一部分--K近邻简介 从字面意思就可以容易看出,所谓的K近邻,就是找到某个样本距离(这里的距离可以是欧式距离,曼哈顿距离,切比雪 ...

  7. JMS - ActiveMQ集成Spring

    下面是ActiveMQ官网提供的文档.http://activemq.apache.org/spring-support.html 下面是我添加的一些dependency: <!-- jms a ...

  8. php从数据库里取出的数据列表里添加一个属性实战例子

    php从数据库里取出的数据列表里添加一个属性实战例子:$opendata = $this->omitmodel->getHistory(1,1);var_dump($opendata);f ...

  9. ActiveMQ入门实例Demo

    前面我们已经搭建和配置好了ActiveMQ,下面来看一个Demo,体验一下MQ. JMS 消息模型 JMS消息服务应用程序结构支持两种模型:点对点模型,发布者/订阅者模型. (1)点对点模型(Queu ...

随机推荐

  1. Activity(工作流-1)

    1.activity自带数据表的含义(23张表) (1)资源库流程规则表 1)act_re_deployment 部署信息表 2)act_re_model  流程设计模型部署表 3)act_re_pr ...

  2. Setting proxy for Windows CMD and Linux Terminal

    setting proxy for Windows CMD and Linux Terminal Linux Terminal: set http_proxy=http://127.0.0.1:811 ...

  3. Flink State 有可能代替数据库吗?

    有状态的计算作为容错以及数据一致性的保证,是当今实时计算必不可少的特性之一,流行的实时计算引擎包括 Google Dataflow.Flink.Spark (Structure) Streaming. ...

  4. 《DNS稳定保障系列3--快如闪电,域名解析秒级生效》

    在刚刚过去的双十一,又是一个全民狂欢的盛宴,天猫双十一的成交量高达2684亿.无数小伙伴在淘宝.天猫里买买买,今年你又剁手了多少?言归正传,在你疯狂秒杀的时候,有没有发现,今年的购物体验一如既往的好, ...

  5. mysql 8.0版本下载安装以及默认密码修改

    1. 下载 去mysql官网下载地址进行下载,选择需要的安装包 可以直接跳过登录进行下载 ps:我是先注册账号下载的,注册时遇到一个坑,就是在chrome75版本无法选择省市,不选择又不让注册. 可以 ...

  6. php基于SQLite实现的分页功能示例

    php基于SQLite实现的分页功能. 这里操作数据库文件使用的是前面文章<PHP基于PDO实现的SQLite操作类>中的SQLite数据库操作类. 代码: <?php class ...

  7. 2018-2019 ACM-ICPC Brazil Subregional Programming Contest F. Music Festival

    题目:https://codeforces.com/gym/101908/problem/F 题意:给你n个舞台,每个舞台有很多个节目,每个节目有个开始时间,结束时间,价值,每个舞台至少出演过一个节目 ...

  8. SVG开发包, 20 个有用的 SVG 工具,提供更好的图像处理

    20 个有用的 SVG 工具,提供更好的图像处理 SVG 现正在 Web 设计领域变得越发流行, 你可以使用 Illustrator 或者 Inkscape 来创建 SVG 图像. 但当进行 Web ...

  9. Python学习笔记二--函数

    1.使用global语句定义全局变量 2.默认参数 默认参数值应该是不可变的.注意: 只有在形参表末尾的那些参数可以有默认参数值,即你不能在声明函数形参的时候,先声明有默认值的形参而后声明没有默认值的 ...

  10. 大数据学习笔记之Hadoop(二):HDFS文件系统

    文章目录 一 HDFS概念 1.1 概念 1.2 组成 1.3 HDFS 文件块大小 二 HFDS命令行操作 三 HDFS客户端操作 3.1 eclipse环境准备 3.1.1 jar包准备 3.2 ...