二、activemq 之hello world

一、概述:

1.什么是activemq

MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。两个系统或两个客户端之间进行消息传送,利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。说人话就是 处理详细的一个服务,当我们的后台服务需要发消息的时候,会把消息发送到MQ服务器,当后台服务需要取数据的时候,就MQ中取数据;作用就是异步化提升性能、降低耦合度、流量削峰。

举个例子 学生找老师回答问题的场景,如果有有100个学生同时找一个老师回答问题,那么老师只能一个一个的解答学生的问题。没有轮到的学生只能等着。而采用的mq的方式呢,就是 学生按照老师的要求(JMS)写好提问的内容,并把它交给班长,由班长交给老师。只要交了问题的学生就可以该干嘛就干嘛了,不用排队等着;老师拿到谁的提问,就处理谁的提问。

  

2.应用场景举例

1)异步通信

​ 注册时的短信、邮件通知,减少响应时间;

2)应用解耦

​ 信息发送者和消息接受者无需耦合,比如调用第三方;

3)流量削峰

​ 例如秒杀系统;

 

3.消息模型:

  • 队列:Point-to-Point(P2P) --- 点对点(生产者发送一条消息到queue,只有一个消费者能收到)
  • 主题:Publish/Subscribe(Pub/Sub)---  发布订阅(发布者发送到topic的消息,只有订阅了topic的订阅者才会收到消息)

4.安装:

https://www.cnblogs.com/lixiuming521125/p/16499787.html

二、JMS:

java 实现MQ的规范:

JMS开发步骤;

1.创建一个JMS connectionfactory
2.通过connection factory来创建JMS connection
3.启动JMS connection
4.通过connection创建JMS session
5.创建JMS destination
6.创建JMS producer,或者创建JMS message,并设置destination
7.创建JMS consumer,或者是注册一个JMS message listener
8.发送或者接受JMS message(s)
9.关闭所有的JMS资源(connection, session, producer, consumer等) 。

 

三、队列Queue

特点:

 

  • 每个消息只能有一个消费者。
  • 消息的生产者和消费者之间没有时间上的相关性。无论消费者在生产者发送消息的时候是否处于运行状态,它都可以提取消息。

 

队列的实现方式如图:

 

消息生产者代码实现:

 1 package org.muses.ssm.utils;
 2 
 3 import javax.jms.Connection;
 4 import javax.jms.JMSException;
 5 import javax.jms.MessageProducer;
 6 import javax.jms.Queue;
 7 import javax.jms.Session;
 8 import javax.jms.TextMessage;
 9 
10 import org.apache.activemq.ActiveMQConnectionFactory;
11 
12 public class TestActiveMqProducer {
13     private static final String ACTIVEMQ_URL = "tcp://192.168.65.116:61616";
14     private static final String QUEUE_NAME = "queue_01";
15  
16     public static void main(String[] args) throws JMSException {
17         // 创建连接工厂,按照给定的URL,采用默认用户名密码
18         ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
19         // 通过连接工厂 获取connection 并启动访问
20         Connection conn = activeMQConnectionFactory.createConnection();
21         conn.start();
22         // 创建session会话
23         Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
24         // 创建目的地 (具体是队列还是主题topic)
25         Queue queue = session.createQueue(QUEUE_NAME);
26 
27         // 创建消息的生产者
28         MessageProducer messageProducer = session.createProducer(queue);
29 
30         for (int i = 0; i < 3; i++) {
31             // 创建消息;可以理解为学生按照要求写好问题
32             TextMessage textMessage = session.createTextMessage("mession-------" + i);
33             // 通过messageProducer 发送给mq
34             messageProducer.send(textMessage);
35         }
36         messageProducer.close();
37         session.close();
38         conn.close();
39         System.out.println("发送消息成功");
40     }
41 
42 }
View Code

当发送者发送代码后,mq队列列表显示3条待处理的消息

 

消费者代码实现方法一:同步阻塞方式(receive())

订阅者或者接受者调用MessageConsumer的receive()方法来接受消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞;receive()可设置超时时间;

 1 package org.muses.ssm.utils;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.jms.Connection;
 6 import javax.jms.JMSException;
 7 import javax.jms.MessageConsumer;
 8 import javax.jms.Queue;
 9 import javax.jms.Session;
10 import javax.jms.TextMessage;
11 
12 import org.apache.activemq.ActiveMQConnectionFactory;
13 
14 public class TestActiveMqConsumer {
15     private static final String ACTIVEMQ_URL = "tcp://192.168.65.116:61616";
16     private static final String QUEUE_NAME = "queue_01";
17 
18     public static void main(String[] args) throws JMSException, IOException {
19         // 创建连接工厂,按照给定的URL,采用默认用户名密码
20         ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
21         // 通过连接工厂 获取connection 并启动访问
22         Connection conn = activeMQConnectionFactory.createConnection();
23         conn.start();
24         // 创建session会话
25         Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
26         // 创建目的地 (具体是队列还是主题topic)
27         Queue queue = session.createQueue(QUEUE_NAME);
28 
29         // 创建消息的生产者
30         MessageConsumer messageConsumer = session.createConsumer(queue);
31         /**
32          * 同步阻塞方式(receive()) 订阅者或者接受者调用MessageConsumer的receive()方法来接受消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞;
33          */
34         while (true) {
35             TextMessage textMessage = (TextMessage) messageConsumer.receive();
36             if (textMessage != null) {
37                 System.out.println("收到消息:" + textMessage.getText());
38             } else {
39                 break;
40             }
41         }
42 
43         messageConsumer.close();
44         session.close();
45         conn.close();
46 
47     }
48 
49 }
View Code

当发布者发布了消息,然后启动消费者代码,则MQ管理后台显示:此时,观察控制台,发现消费者程序一直处于运行状态。原因是recevice()没有设置超时时间,所以消费者会一直开着。如果receive()方法设置了超时时间,则 消费者程序会在等待超时时间后,关闭消费者程序;

消费者和生产者的队列名称一定要一致

控制台打印:

收到消息:mession-------0
收到消息:mession-------1
收到消息:mession-------2

消费者代码实现方法二:消费者监听模式(异步非阻塞方式(监听器onMessage))

订阅或者接收者通过MessageConsumer的setMessageListener(MessageListener messageListener),注册一个消息监听器, 当消息到达以后,系统自动调用监听器的MessageListener的 onMessage(Message message)方法;程序运行完成后,观察控制台发现,消费者程也是一直没有关闭,一直在监听;

消费者和生产者的队列名称一定要一致

 1 package org.muses.ssm.utils;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.jms.Connection;
 6 import javax.jms.JMSException;
 7 import javax.jms.Message;
 8 import javax.jms.MessageConsumer;
 9 import javax.jms.MessageListener;
10 import javax.jms.Queue;
11 import javax.jms.Session;
12 import javax.jms.TextMessage;
13 
14 import org.apache.activemq.ActiveMQConnectionFactory;
15 
16 public class TestActiveMqConsumer {
17     private static final String ACTIVEMQ_URL = "tcp://192.168.65.116:61616";
18     private static final String QUEUE_NAME = "queue_01";
19 
20     public static void main(String[] args) throws JMSException, IOException {
21         // 创建连接工厂,按照给定的URL,采用默认用户名密码
22         ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
23         // 通过连接工厂 获取connection 并启动访问
24         Connection conn = activeMQConnectionFactory.createConnection();
25         conn.start();
26         // 创建session会话
27         Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
28         // 创建目的地 (具体是队列还是主题topic)
29         Queue queue = session.createQueue(QUEUE_NAME);
30 
31         // 创建消息的生产者
32         MessageConsumer messageConsumer = session.createConsumer(queue);
33         /**
34          * 消费者监听模式; 异步非阻塞方式(监听器onMessage) 订阅或者接收者通过MessageConsumer的setMessageListener(MessageListener messageListener)
35          * 注册一个消息监听器, 当消息到达以后,系统自动调用监听器的MessageListener的 onMessage(Message message)方法
36          */
37         messageConsumer.setMessageListener(new MessageListener() {
38 
39             @Override
40             public void onMessage(Message message) {
41                 if (message != null && message instanceof TextMessage) {
42                     TextMessage textMessage = (TextMessage) message;
43                     try {
44                         System.out.println("收到消息:" + textMessage.getText());
45                     } catch (JMSException e) {
46                         // TODO Auto-generated catch block
47                         e.printStackTrace();
48                     }
49                 }
50             }
51 
52         });
53         // 消费者监听模式 必须要有 System.in.read() ;否则无法消费
54         System.in.read();
55 
56         messageConsumer.close();
57         session.close();
58         conn.close();
59 
60     }
61 
62 }
View Code

控制台打印:

收到消息:mession-------0
收到消息:mession-------1
收到消息:mession-------2

关于有多个消费者的情况说明(本例为两个)

情况一:先生产3条消息 ,先启动消费者1 再启动消费者2号: 2号没有消费到一条消息,1号消费者消费所有消息 

实现方式:

  生产3条消息,利用消息生产者代码即可实现;

  启动消费者1号:消费者代码实现方式二 启动 即可实现1号消费者,可以用 System.out.println("****我是1号消费者******")来表示;

  启动消费者2号:再次费者代码实现方式二 启动 即可实现2号消费者,可以用 System.out.println("****我是2号消费者******")来表示;

 

情况二:先启动2个消费者,再生产消息;那么就会出现 1号消费者和2号消费者平分消息的情况,例如 1号消费者先启动,2号消费者后启动,再生产3条消息:那么就是 1号消费者消费了2条消息,2号消费者消费了1条消息;如果生产了4条消息,那么1号和2号消费者各消费了2条消息;

  实现方式:

  启动消费者1号:消费者代码实现方式二 启动 即可实现1号消费者,可以用 System.out.println("****我是1号消费者******")来表示;

  启动消费者2号:再次费者代码实现方式二 启动 即可实现2号消费者,可以用 System.out.println("****我是2号消费者******")来表示;

  生产3条消息,利用消息生产者代码即可实现;

队列表头说明:

  1.   Name:消息队列的名称。
  2.   Number Of Pending Messages:未被消费的消息数目。
  3.   Number Of Consumers:消费者的数量。
  4.   Messages Enqueued:进入队列的消息 ;进入队列的总消息数目,包括已经被消费的和未被消费的。 这个数量只增不减。
  5.   Messages Dequeued:出了队列的消息,可以理解为是被消费掉的消息数量。在Queues里它和进入队列的总数量相等(因为一个消息只会被成功消费一次),如果暂时不等是因为消费者还没来得及消费。

四、订阅Topic

特点:

  • 每个消息可以有多个消费者。
  • 生产者和消费者之间有时间上的相关性
  • 订阅一个主题的消费者只能消费自它订阅之后发布的消息。JMS规范允许客户创建持久订阅,这在一定程度上放松了时间上的相关性要求 。持久订阅允许消费者消费它在未处于激活状态时发送的消息。
  • 在点对点消息传递域中,目的地被成为队列(queue);在发布/订阅消息传递域中,目的地被成为主题(topic)。

需要先有消费者,再有消息生产者。

运行流程:

 消费者代码实现:

 1 package org.muses.ssm.utils;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.jms.Connection;
 6 import javax.jms.JMSException;
 7 import javax.jms.Message;
 8 import javax.jms.MessageConsumer;
 9 import javax.jms.MessageListener;
10 import javax.jms.Session;
11 import javax.jms.TextMessage;
12 import javax.jms.Topic;
13 
14 import org.apache.activemq.ActiveMQConnectionFactory;
15 
16 public class TestActiveMqConsumer {
17     private static final String ACTIVEMQ_URL = "tcp://192.168.65.116:61616";
18     private static final String TOPIC_NAME = "TOPIC_NAME_1";
19 
20     public static void main(String[] args) throws JMSException, IOException {
21         // 创建连接工厂,按照给定的URL,采用默认用户名密码
22         ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
23         // 通过连接工厂 获取connection 并启动访问
24         Connection conn = activeMQConnectionFactory.createConnection();
25         conn.start();
26         // 创建session会话
27         Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
28         // 创建目的地 (具体是队列还是主题topic)
29         Topic topic = session.createTopic(TOPIC_NAME);
30 
31         // 创建消息的生产者
32         MessageConsumer messageConsumer = session.createConsumer(topic);
33 
34         System.out.println("****我是1号消费者******");
35         messageConsumer.setMessageListener(new MessageListener() {
36 
37             @Override
38             public void onMessage(Message message) {
39                 if (message != null && message instanceof TextMessage) {
40                     TextMessage textMessage = (TextMessage) message;
41                     try {
42                         System.out.println("收到消息:" + textMessage.getText());
43                     } catch (JMSException e) {
44                         // TODO Auto-generated catch block
45                         e.printStackTrace();
46                     }
47                 }
48             }
49 
50         });
51         System.in.read();
52 
53         messageConsumer.close();
54         session.close();
55         conn.close();
56 
57     }
58 
59 }
View Code

启动3个消费者:(分别启动3此消费者代码 System.out.println("****我是1/2/3号消费者******")来表示),MQ后台显示如下:

 

生产者代码实现:

 1 package org.muses.ssm.utils;
 2 
 3 import javax.jms.Connection;
 4 import javax.jms.JMSException;
 5 import javax.jms.MessageProducer;
 6 import javax.jms.Session;
 7 import javax.jms.TextMessage;
 8 import javax.jms.Topic;
 9 
10 import org.apache.activemq.ActiveMQConnectionFactory;
11 
12 public class TestActiveMqTopicProducer {
13     private static final String ACTIVEMQ_URL = "tcp://192.168.65.116:61616";
14     private static final String TOPIC_NAME = "TOPIC_NAME_1";
15 
16     public static void main(String[] args) throws JMSException {
17         // 创建连接工厂,按照给定的URL,采用默认用户名密码
18         ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
19         // 通过连接工厂 获取connection 并启动访问
20         Connection conn = activeMQConnectionFactory.createConnection();
21         conn.start();
22         // 创建session会话
23         Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
24         // 创建目的地 (具体是队列还是主题topic)
25         Topic topic = session.createTopic(TOPIC_NAME);
26 
27         // 创建消息的生产者
28         MessageProducer messageProducer = session.createProducer(topic);
29 
30         for (int i = 0; i < 3; i++) {
31             // 创建消息;可以理解为学生按照要求写好问题
32             TextMessage textMessage = session.createTextMessage("mession-------" + i);
33             // 通过messageProducer 发送给mq
34             messageProducer.send(textMessage);
35         }
36         messageProducer.close();
37         session.close();
38         conn.close();
39         System.out.println("发送消息成功");
40     }
41 
42 }
View Code

启动消息生产者代码:此时,1号消费者接受到3条消息;2号消费者也接收到3条消息,3号消费者也接收到3条消息;

控制台显示

****我是1号消费者******      ****我是2号消费者******      ****我是3号消费者******      
收到消息:mession-------0    收到消息:mession-------0      收到消息:mession-------0
收到消息:mession-------1    收到消息:mession-------1      收到消息:mession-------1
收到消息:mession-------2    收到消息:mession-------2      收到消息:mession-------2

MQ管理后台显示:

Topic &Queue对比

  Topic queue
概要 Publish  Subscribe messaging 发布订阅消息 Point-to-Point  点对点
有无状态 topic数据默认不落地,是无状态的。

Queue数据默认会在mq服务器上以文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data下面。也可以配置成DB存储。

完整性保障 并不保证publisher发布的每条数据,Subscriber都能接受到。 Queue保证每条数据都能被receiver接收。
消息是否会丢失 一般来说publisher发布消息到某一个topic时,只有正在监听该topic地址的sub能够接收到消息;如果没有sub在监听,该topic就丢失了。 Sender发送消息到目标Queue,receiver可以异步接收这个Queue上的消息。Queue上的消息如果暂时没有receiver来取,也不会丢失。
消息发布接收策略 一对多的消息发布接收策略,监听同一个topic地址的多个sub都能收到publisher发送的消息。Sub接收完通知mq服务器 一对一的消息发布接收策略,一个sender发送的消息,只能有一个receiver接收。receiver接收完后,通知mq服务器已接收,mq服务器对queue里的消息采取删除或其他操作。

 

需要的jar包:

版本需要和 activemq 服务的版本对应,例如,服务端装了5.16.5,那么Java程序端需要用5.16.5的jar包

 1        <!-- activemq 需要的依赖 -->
 2         <dependency>
 3             <groupId>org.apache.activemq</groupId>
 4             <artifactId>activemq-all</artifactId>
 5             <version>5.16.5</version>
 6         </dependency>
 7         <dependency>
 8             <groupId>org.apache.xbean</groupId>
 9             <artifactId>xbean-spring</artifactId>
10             <version>4.21</version>
11         </dependency>                

 

官方文档地址:

https://activemq.apache.org/features 

posted @ 2022-08-01 22:26  啄木鸟伍迪  阅读(100)  评论(0编辑  收藏  举报
//火箭 GenerateContentList();