一、概述:

1.什么是activemq

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

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

二、activemq 之hello world_java

  

二、activemq 之hello world_mq_02

2.应用场景举例

1)异步通信

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

2)应用解耦

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

3)流量削峰

例如秒杀系统;

 

3.消息模型:

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

4.安装:

二、JMS:

java 实现MQ的规范:

二、activemq 之hello world_java_03

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

特点:

 

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

 

队列的实现方式如图:

二、activemq 之hello world_java_04

 

消息生产者代码实现:

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条待处理的消息

二、activemq 之hello world_mq_05

 

消费者代码实现方法一:同步阻塞方式(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()方法设置了超时时间,则 消费者程序会在等待超时时间后,关闭消费者程序;

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

二、activemq 之hello world_java_06

控制台打印:

收到消息: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)。

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

运行流程:

二、activemq 之hello world_监听器_07

 消费者代码实现:

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后台显示如下:

二、activemq 之hello world_监听器_08

 

生产者代码实现:

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管理后台显示:

二、activemq 之hello world_代码实现_09

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​​ 

我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。 我要做一个自由又自律的人,靠势必实现的决心认真地活着。