#情景引入
小白:起床起床起床起床。。。。快起床~
我:怎么了又,大惊小怪,吓到我了。
小白:我有事有事想找你,十万火急呢~~
我:你能有什么事?反正我不信。。那你说说看~~
小白:就是我有两个小表弟,叫大白和二白,他们现在每天睡觉之前都要分别和我聊天,让我给他们讲故事,如果不讲他们就不睡觉。但是,如果一个个的跟他们轮流来说的话,我就需要每天说两遍,而且我还要找准他们的时间点,这个有时候我有事情都无法实现这个问题,他们就会很生气。。。
我:这不是挺好的嘛,小孩子就是爱听故事的呀。。。
小白:我也愿意讲,但是时间这个不是很好控制,有没有类似,比如我可以之前就描述好了,然后定点给他们两个一起发消息,而可以抛开时间和其他因素的影响呢?
我:这个嘛,很简单呀,你可以让他们关注你的一个公众号,这样你再定时的推送给他们故事不就可以了嘛。。或者,你可以拉他们进你的一个群这样,就方便了呀~
小白:这样是可以,但是如果以后还有小表妹要听我讲,我就要如此反复的做。。感谢好麻烦好麻烦。。。
我:emmm,我理解你的意思,你就想实现一种很多人都能够进行类似一种消息推送的方式嘛。。。
小白:对的对的。。就是这样一种,,,我记得我们在技术方面好像也有一种类似的技术,这个叫做什么去了呢?
我:这就是消息中间件,一种生产者和消费者的关系。
小白:我也想学我也想学,,你快给我讲讲,给我讲讲呗。。
我:真拿你没办法,好吧。。。下面我就给你讲一下这方面的知识。
#情景分析
其实,小白的这个问题,是一种比较普遍的问题。既然我们作为技术人员,当然我们就要从技术成分去分析如何解决了。这里面其实就是包含着一种消息中间件的技术。它也是最近技术层面用得非常非常多的,这也是非常值得我们进行学习。。这在如今的秒杀系统,推荐系统等等,都有广泛的应用。。所以,这章我就主要来跟大家说说这方面的知识。
#基本概念的引导
本模块主要讲解关于消息中间件的相关基础知识,也是方便我们后面的学习。
###什么是中间件?
非操作系统软件,非业务应用软件,不是直接给最终用户使用,不能直接给用户带来价值的软件,我们就可以称为中间件(比如Dubbo,Tomcat,Jetty,Jboss都是属于的)。
###什么是消息中间件?
百度百科解释:消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
关键点:关注于数据的发送和接受,利用高效可靠的异步消息机制传递机制集成分布式系统。
先简单的用下面这个图说明:

###为什么要使用消息中间件
举几个例子,我想你就会明白了。(其实使用消息中间件主要就是为了解耦合和异步两个作用)
1:微博,都用过吧。那么,当我们新关注一个用户,那么系统会相应的推送消息给我们,并且还做了很多关于我们关注的处理。这就是消息中间件的异步。
2:秒杀系统。100件商品,几十万个人在抢,那这个怎么弄呢?总不能就把服务器给宕机了吧。那么就可以把用户的请求进行缓存,然后再异步处理。
3:系统A给系统B进行通信,而系统B需要对A的消息进行相应处理之后才能给A反馈,这时候,总不能让A就傻傻等着吧。那么,这就是异步的功能。
###什么是JMS?
Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
总结起来说就是:Java对于应用程序之间进行信息交互的API(而且是异步)。
里面有下面的概念需要理解,对后续有帮助:

提供者:实现JMS的消息服务中间件服务器。

客户端:发送或接受消息的应用。

生产者/发布者:创建并发送消息的客户端。

消费者/订阅者:接受并处理消息的客户端。

消息:应用程序之间传递的数据。

消息模式:在客户端之间传递消息的模式,JMS主要是队列模式和主体模式。

队列模式特点:
(1)客户端包括生产者和消费者。
(2)队列中的一个消息只能被一个消费者使用。
(3)消费者可以随时取消息。

主体模式特点:
(1)客户端包括发布者和订阅者。
(2)主题中的消息可以被所有订阅者消费。
(3)消费者不能消费订阅之前发送的消息。
###什么是AMQP?
AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。
简单点说:就是对于消息中间件所接受的消息传输层的协议(不懂传输层,那么就需要多看看计算机网络相关知识了,OSI的层次划分),只有这样才能保证客户端和消息中间件能够进行交互(换位思考:HTTP和HTTPS甚至说是TCP/IP与UDP协议都要的道理)。
emmm,比较一下JMS和AMQP的不同吧。。

JMS是定义与Java,而AMQP是一种传输层协议。

JMS是属于Java的API,而AMQP是跨语言的。

JMS消息类型只有两种(主题和队列,后续会说),而AMQP是有五种。

JMS主要就是针对Java的开发的Client,而AMQP是面向消息,队列,路由。
###什么是ActiveMQ呢?
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。
简单点说:不就是为了实现我上述所想要的需求嘛。然后它就是一种实现的方式。就比如,Tomcat是什么?不就是为了实现一种client与服务器之间的交互的一种产品嘛。。所以,不需要死记概念,自己理解就好。
#ActiveMQ的安装
##环境:Windows
步骤:
(1)登录到ActiveMQ的官网,下载安装包。http://activemq.apache.org/activemq-5154-release.html
(2)下载Zip文件

(3)解压Zip文件,目录如下

(4)启动ActiveMQ服务(注意:要右键以管理员身份进行运行)

注意:有两种方式,第一种就是类似tomcat启动,那么启动图会一直显示。
而第二种的话,就是把这个ActiveMQ注册到服务列表中,这样更方便我们进行操作。(推荐使用这种)
(5)登录,验证是否启动成功

(6)进入管理页面

OK,进入之后就可以看我们的管理页面啦。。。是不是很简单呢?
##环境:Linux
步骤:(多余的我就不多说了。。。请看windows的步骤)
(1)同样需要下载对应的文件。后缀为tar.gz的这样的。其实可以直接通过下面的这个命令下载,快速一点,免得要移动到Linux(注意:如果是通过ssh连接的方式的话)。

wget https://mirrors.tuna.tsinghua.edu.cn/apache//activemq/5.15.4/apache-activemq-5.15.4-bin.tar.gz
1
(2)然后解压下载的文件
(3)同样进入相对应的目录,运行

./activemq start
1
(4)然后再访问相同的地址就可以看到啦。(具体看windows安装步骤)
#ActiveMQ的使用(基于Maven)
首先要再回头看看JMS中的一些关键接口。

ConnectionFactory:用于创建连接到消息中间件的连接工厂。
Connection:代表了应用程序和服务之间的连接通路。
Destination:指消息发布的地点,包括队列模式和主体模式。
Session:表示一个单线程的上下文,用于发送和接受消息。
MessageConsumer:由会话创建,用于接受发送到目的的消息。
MessageProducer:由会话创建,用于发送消息。
Message:是在消费者和生产者之间传递的对象,消息头,一组消息属性,和一个消息体。

环境:IDEA
步骤:
使用IDEA创建一个Maven项目,最简单的骨架即可(quick)
导入ActiveMq的依赖
<!--添加activemq的依赖-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.9.0</version>
</dependency>
1
2
3
4
5
6
###情形一:队列模型的消息
3. 编写生产者代码(使用队列模型的消息)

package com.hnu.scw.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @ Author :scw
* @ Date :Created in 上午 11:06 2018/7/14 0014
* @ Description:用于消息的创建类
* @ Modified By:
* @Version: $version$
*/
public class MessageProducer {
//定义ActivMQ的连接地址
private static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
//定义发送消息的队列名称
private static final String QUEUE_NAME = "MyMessage";

public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列目标
Destination destination = session.createQueue(QUEUE_NAME);
//创建一个生产者
javax.jms.MessageProducer producer = session.createProducer(destination);
//创建模拟100个消息
for (int i = 1 ; i <= 100 ; i++){
TextMessage message = session.createTextMessage("我发送message:" + i);
//发送消息
producer.send(message);
//在本地打印消息
System.out.println("我现在发的消息是:" + message.getText());
}
//关闭连接
connection.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
查看是否消息产生成功

编写消费者代码(消费队列模型的消息)
package com.hnu.scw.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @ Author :scw
* @ Date :Created in 上午 11:30 2018/7/14 0014
* @ Description:消息消费者
* @ Modified By:
* @Version: $version$
*/
public class MessageConsumer {
//定义ActivMQ的连接地址
private static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
//定义发送消息的队列名称
private static final String QUEUE_NAME = "MyMessage";
public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列目标
Destination destination = session.createQueue(QUEUE_NAME);
//创建消费者
javax.jms.MessageConsumer consumer = session.createConsumer(destination);
//创建消费的监听
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("获取消息:" + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
查看是否进行了消费

**备注:**我上面进行的是队列模式的消息,而且进行的都是单个消费者,那如果我换成同时有两个消费者消费生产者的消息会怎么样呢?(我们只需要运行两个消费者就可以啦。当然,要保证生产者是产生了消息的哦~~~~否则,拿什么消费呢~)
一个生产者,两个消费者的情况如下:
切记:先运行两个消费者,然后再运行生产者代码:
结果如下:

其实,这就是解释了,我之前说的,队列模式的消息,是只会被一个消费者所使用的,而不会被共享,这也就是和主题模型的差别哦~~~哈哈
###情形二:主题模型的消息
前面的步骤都一样,只是生产者和消费者的代码有点区别:

编写生产者(这个和队列模型其实很像,稍微修改就可以)

package com.hnu.scw.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
* @ Author :scw
* @ Date :Created in 上午 11:48 2018/7/14 0014
* @ Description:${description}
* @ Modified By:
* @Version: $version$
*/
public class MessageTopicProducer {

//定义ActivMQ的连接地址
private static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
//定义发送消息的主题名称
private static final String TOPIC_NAME = "MyTopicMessage";

public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列目标
Destination destination = session.createTopic(TOPIC_NAME);
//创建一个生产者
javax.jms.MessageProducer producer = session.createProducer(destination);
//创建模拟100个消息
for (int i = 1; i <= 100; i++) {
TextMessage message = session.createTextMessage("当前message是(主题模型):" + i);
//发送消息
producer.send(message);
//在本地打印消息
System.out.println("我现在发的消息是:" + message.getText());
}
//关闭连接
connection.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
查看生产者的消息

编写消费者
package com.hnu.scw.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
* @ Author :scw
* @ Date :Created in 上午 11:50 2018/7/14 0014
* @ Description:${description}
* @ Modified By:
* @Version: $version$
*/
public class MessageTopicConsumer {
//定义ActivMQ的连接地址
private static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
//定义发送消息的队列名称
private static final String TOPIC_NAME = "MyTopicMessage";
public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列目标
Destination destination = session.createTopic(TOPIC_NAME);
//创建消费者
javax.jms.MessageConsumer consumer = session.createConsumer(destination);
//创建消费的监听
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("获取消息:" + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
查看是否消费成功
然而,我们运行消费者代码,发现怎么没有消息消费呢?????????
其实,这就是主题模型的一个特点,如果消费者是在生产者产生消息之后来的,那么是不会对之前的消息进行消费的哦。。。现在知道它们的区别在哪了吧。
如果,现在是两个消费者和一个生产者的主题模型又是怎么的结果呢?

哎哟。。。。这种情况消费者都各自消费了所有的生产者的消息耶。。。。。这就是共享性消息的主题模式,这就是和队列模型的区别,,,大家好好的对比哦~~
#ActiveMQ使用(基于Spring)
步骤:
创建一个Maven项目(基于最简单的quick骨架即可)
导入Spring和ActiveMQ的相关依赖
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.hnu.scw</groupId>
<artifactId>activemq</artifactId>
<version>1.0-SNAPSHOT</version>

<name>activemq</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring.version>4.2.5.RELEASE</spring.version>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--添加activemq的依赖-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.9.0</version>
</dependency>

<!--spring整合activemq所需要的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.7.0</version>
<exclusions>
<exclusion>
<artifactId>spring-context</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
编写生产者的配置文件.xml,取名为producer.xml
<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">

<context:annotation-config />

<!--Activemq的连接工厂-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://127.0.0.1:61616" />
</bean>
<!--spring jms为我们提供的连接池 获取一个连接工厂-->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnectionFactory" />
</bean>

<!-- 消息目的地 点对点的模式-->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="SpringActiveMQMsg"/>
</bean>
<!-- jms模板 用于进行消息发送-->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
</beans>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
编写生产者的接口
package com.hnu.scw.spring;

/**
* @ Author :scw
* @ Date :Created in 下午 12:19 2018/7/14 0014
* @ Description:生产者的接口
* @ Modified By:
* @Version: $version$
*/
public interface ProduceService {
void sendMessage(String msg);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
编写生产者的实现
package com.hnu.scw.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import javax.annotation.Resource;
import javax.jms.*;

/**
* @ Author :scw
* @ Date :Created in 下午 2:21 2018/7/15 0015
* @ Description:生产者的实现类
* @ Modified By:
* @Version: $version$
*/

public class ProduceServiceImpl implements ProduceService {
@Autowired
private JmsTemplate jmsTemplate;
@Resource(name = "queueDestination")
private Destination destination;

/**
* 发送消息
* @param msg
*/
@Override
public void sendMessage(final String msg) {
jmsTemplate.send(destination , new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage textMessage = session.createTextMessage(msg);
return textMessage;
}
});
System.out.println("现在发送的消息为: " + msg);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
将生产者的类添加到上述的配置文件中
<!--注入我们的生产者-->
<bean class="com.hnu.scw.spring.ProduceServiceImpl"/>
1
2
编写生产者的测试类
package com.hnu.scw.spring;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* @ Author :scw
* @ Date :Created in 下午 2:27 2018/7/15 0015
* @ Description:生产者的测试
* @ Modified By:
* @Version: $version$
*/
public class ProducerTest {
public static void main(String[] args){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("producer.xml");
ProduceService bean = classPathXmlApplicationContext.getBean(ProduceService.class);
//进行发送消息
for (int i = 0; i < 100 ; i++) {
bean.sendMessage("test" + i);
}
//当消息发送完后,关闭容器
classPathXmlApplicationContext.close();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
运行测试类,查看生产者是否产生消息成功

通过上述的界面,就可以看到自己配置的队列模式的消息产生成功。
编写消费者的消息监听类
编写消费者的配置文件,命名为consumer.xml
<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">

<context:annotation-config />

<!--Activemq的连接工厂-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://127.0.0.1:61616" />
</bean>
<!--spring jms为我们提供的连接池 获取一个连接工厂-->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnectionFactory" />
</bean>

<!-- 消息目的地 点对点的模式-->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="SpringActiveMQMsg"/>
</bean>

<!-- 配置消息监听器-->
<bean id="consumerMessageListener" class="com.hnu.scw.spring.ComsumerMessageListener"/>
<!--配置消息容器-->
<bean id ="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!--配置连接工厂-->
<property name="connectionFactory" ref="connectionFactory"/>
<!--配置监听的队列-->
<property name="destination" ref="queueDestination"/>
<!--配置消息监听器-->
<property name="messageListener" ref="consumerMessageListener"/>
</bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
消息消费者ComsumerMessageListener类代码
package com.hnu.scw.spring;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

/**
* @ Author :scw
* @ Date :Created in 下午 3:06 2018/7/15 0015
* @ Description:消息的监听者,用于处理消息
* @ Modified By:
* @Version: $version$
*/
public class ComsumerMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("接受到消息:" + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
编写测试文件,测试消费者消费消息是否成功
package com.hnu.scw.spring;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* @ Author :scw
* @ Date :Created in 下午 3:13 2018/7/15 0015
* @ Description:消费者的测试
* @ Modified By:
* @Version: $version$
*/
public class ConsumerTest {
public static void main(String[] args){
//启动消费者
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("consumer.xml");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
查看ActiveMQ网站具体消息情况

ActiveMQ的队列模型就大功告成啦。。。。。。so easy!!!
备注:上面都是进行的ActiveMQ的队列模型的配置,那么我们如果想进行主题模型的又是如何进行操作呢?其实也很简单,只需要修改生产者的xml文件里面的队列即可。比如如下代码:
<!-- 消息目的地 (主题模式)-->
<!--<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQTopic">
&lt;!&ndash;配置队列模型的消息名称&ndash;&gt;
<constructor-arg value="SpringActiveMQMsgTopic"/>
</bean>-->
1
2
3
4
5
将上面的代码替换之前的就可以了。。。
总结:总的来说,基于Spring来使用消息队列还是非常方便的,这比我们正常进行JMS规范操作要简单很多,毕竟很多对象都是通过Spring的IOC进行容器管理了,所以,值得推荐使用哦~~~
#ActiveMQ的集群
###为什么要进行集群呢?
原因一:实现高可用:以排除单点故障所引起的服务终端。
原因二:实现负载均衡:以提升效率为更多的客户进行服务。
###集群的方式有哪些?
方式一:客户端集群:多个客户端消费同一个队列。
方式二:Broker clusters:多个Broker之间同步消息。(实现负载均衡)

这个的实现原理主要是通过网络连接器来进行。
网络连接器:用于配置ActiveMQ服务器与服务器之间的网络通讯方式,用于服务器透析消息。主要分为静态连接和动态连接。
方式三:Master Slave :实现高可用。
这种方式的话,可以联想到Mysql的主从配置和Zookeeper的负载均衡的主竞争关系master。
我们在实际的开发中,一般都是将方式二和方式三进行集成,从而实现高可用和负载均衡。下面的话,我也就这样的配置思想来进行讲解:(通过三台服务器来模拟消息集群的实现)

其中的NodeB和NodeC就是一张Master/slave的关系。都可以成为主服务器。(只要它们某一个宕机,那么就会其余的一台就进行继续服务)
###搭建步骤(基于Windows环境,而Linux环境也是一样的操作)
三台服务器的大体功能和描述:

由于自己没有三台服务器,所以就用自己的一台电脑来模拟三台消息服务器,其实这个就是假设有三个不同ActiveMQ消息服务器了。

复制三个ActiveMQ的服务配置到一个公共目录

修改activeMQA的配置文件

只需要在activemq.xml添加如下内容:
<networkConnectors>
<networkConnector name="local_network" uri ="static:(tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)" />
</networkConnectors>
1
2
3
修改ActiveMQB的配置文件
(1)首先在activemq,xml中添加如下内容:
<!--修改服务端口-->
<transportConnector name="openwire" uri="tcp://0.0.0.0:61617?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
<networkConnectors>
<networkConnector name="networktoA" uri="static:(tcp://127.0.0.1:61616)" />
</networkConnectors>
<!--并修改下面这个标签的内容 , 作为B和C的共享文件,目录就是自己之前创建的一个文件(可以回看上面的整个结构)-->
<persistenceAdapter>
<kahaDB directory="D:\Download\MQJiQun\shareDB"/>
</persistenceAdapter>
1
2
3
4
5
6
7
8
9
(2)修改jetty.xml内容,修改服务器的服务端口

<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
<!-- the default port number for the web console -->
<property name="host" value="0.0.0.0"/>
<property name="port" value="8162"/>
</bean>
1
2
3
4
5
修改ActiveMQC的配置文件(其实类似和B一样,只是服务端口不一样)
(1)修改activemq.xml中的内容
<!--修改服务端口-->
<transportConnector name="openwire" uri="tcp://0.0.0.0:61618?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
<networkConnectors>
<networkConnector name="networktoA" uri="static:(tcp://127.0.0.1:61616)" />
</networkConnectors>
<!--并修改下面这个标签的内容 , 作为B和C的共享文件,目录就是自己之前创建的一个文件(可以回看上面的整个结构)-->
<persistenceAdapter>
<kahaDB directory="D:\Download\MQJiQun\shareDB"/>
</persistenceAdapter>
1
2
3
4
5
6
7
8
9
(2)修改jetty.xml中的内容

<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
<!-- the default port number for the web console -->
<property name="host" value="0.0.0.0"/>
<property name="port" value="8163"/>
</bean>
1
2
3
4
5
集群搭建完成~~~~
集群测试(基于IDEA编辑器+Maven)
步骤:
(1)创建Maven项目
(2)导入依赖

<!--添加activemq的依赖-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.9.0</version>
</dependency>
1
2
3
4
5
6
(3)编写生产者代码

package com.hnu.scw.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @ Author :scw
* @ Date :Created in 上午 11:06 2018/7/14 0014
* @ Description:用于消息的创建类
* @ Modified By:
* @Version: $version$
*/
public class MessageProducer {
//通过集群的方式进行消息服务器的管理(failover就是进行动态转移,当某个服务器宕机,
// 那么就进行其他的服务器选择,randomize表示随机选择)
private static final String ACTIVEMQ_URL = "failover:(tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?randomize=true";
//定义发送消息的队列名称
private static final String QUEUE_NAME = "MyMessage";

public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列目标
Destination destination = session.createQueue(QUEUE_NAME);
//创建一个生产者
javax.jms.MessageProducer producer = session.createProducer(destination);
//创建模拟100个消息
for (int i = 1 ; i <= 100 ; i++){
TextMessage message = session.createTextMessage("当前message是:" + i);
//发送消息
producer.send(message);
//在本地打印消息
System.out.println("我现在发的消息是:" + message.getText());
}
//关闭连接
connection.close();
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(4)编写消费者代码

package com.hnu.scw.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
* @ Author :scw
* @ Date :Created in 上午 11:30 2018/7/14 0014
* @ Description:消息消费者
* @ Modified By:
* @Version: $version$
*/
public class MessageConsumer {
//通过集群的方式进行消息服务器的管理(failover就是进行动态转移,当某个服务器宕机,
// 那么就进行其他的服务器选择,randomize表示随机选择)
private static final String ACTIVEMQ_URL = "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?randomize=true";
//定义发送消息的队列名称
private static final String QUEUE_NAME = "MyMessage";

public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
//创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建队列目标
Destination destination = session.createQueue(QUEUE_NAME);
//创建消费者
javax.jms.MessageConsumer consumer = session.createConsumer(destination);
//创建消费的监听
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("获取消息:" + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
(5)进行查看各自的服务器的消息队列的情况。

首先,是要确保三个ActiveMQ服务器都进行打开。分析:当三个都服务都运行之后,我们从浏览器运行各自的地址,会发现:
比如:我这里的三个服务的地址分别如下:
http://127.0.0.1:8161/
http://127.0.0.1:8162/
http://127.0.0.1:8163/
###重点
为什么前面两个都可以访问,而第三个不可以呢?(同样也是按照我的这样的服务器打开方式哦。先打开的服务器A,接着B,最后C)但是,运行的时候,提示都成功了呀。。为什么为什么???
分析:其实很简单,我说过B和C是一种master/slave的方式,当B运行之后就获得了master的权限,那么C服务是可以看到是一种监听的状态,只有当B宕机之后,才有可能获取master的资源权限,所以,这时候C的地址当然就无法访问啦。这就是负载均衡的一种主/从服务的结构。当然,你可以试着先打开C,再打开B,这时候效果就反过来了。欢迎尝试哦~~~
再运行MessageProducer的类,用于产生消息。这时候,大家可以去查看每个服务器的地址,来观察消息的产生情况。我的如下:

我的消息是产生在服务器B的里面啦。。。。。。
再运行MessageConsumer的类,用于消费消息。这时候,同样可以去查看每个服务器的地址中的消息队列的情况,来观察消息的消费情况。我的如下:

如果,我们在生产者产生了消息之后,服务器B突然宕机了怎么办怎么办??
分析:其实,这时候服务器C就一样有消息保存进行同步了。。是不是这样就是一种高可用的架构了呢????大家,可以试试哦。。把B服务器关掉,再去访问服务器C的地址,就发现如下的结果。

这时候服务器C就作为了master,所以,类似zookeeper就是这样的一种方式的哦。~
###总结
好了,对于集群方面的简单使用就到这里了。其实已经可以根据这个进行扩展了,所以,小伙伴要好好理解这里面的过程和作用,这样才能够学以致用。。。
#其他的消息中间件
其实,类似ActiveMQ这样的消息中间件,用得比较多的还有就是RabbitMQ和Kafka。它们三者各自有各自的优势。大家可以百度进行了解,我就不进行多说了。后面我会同样把这两种消息中间件的使用进行详细的讲解,欢迎大家的关注哦~总的来说,只有适合的场景对应的消息中间件才能发挥最大的作用,没有一种是只有好处而没有坏处的~
#总结

主要是对消息中间件的基础知识进行讲解。
主要讲解ActiveMQ的使用
主要讲解了关于ActiveMQ的集群的搭建
稍微提到了类似ActiveMQ消息中间件的其他中间件
我所讲述的内容,够大家进行入门了,如果要进行深入的了解还是需要慢慢的去熟悉和学习的,而且消息中间件是非常重要的一个技术,希望大家去好好的了解。
最后,感谢各位的阅读哦~~~~
---------------------
作者:Cs_hnu_scw
来源:CSDN
原文:https://blog.csdn.net/cs_hnu_scw/article/details/81040834
版权声明:本文为博主原创文章,转载请附上博文链接!

手把手教你如何玩转消息中间件(ActiveMQ) https://blog.csdn.net/cs_hnu_scw/article/details/81040834的更多相关文章

  1. 手把手教你如何玩转消息中间件(ActiveMQ)

    手把手教你如何玩转消息中间件(ActiveMQ) 2018年07月15日 18:07:39 Cs_hnu_scw 阅读数 12270 标签: 中间件消息中间件ActiveMQ分布式集群 更多 个人分类 ...

  2. 手把手教你如何玩转Activiti工作流

    手把手教你如何玩转Activiti工作流 置顶 2018年01月30日 19:51:36 Cs_hnu_scw 阅读数:24023   版权声明:本文为博主原创文章,未经博主允许不得转载. https ...

  3. 转:手把手教你如何玩转Solr(包含项目实战)

    原文地址:手把手教你如何玩转Solr(包含项目实战) 参考原文

  4. 手把手教你如何玩转Solr(包含项目实战)

    一:Solr简介       Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口.用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引 ...

  5. 手把手教你如何玩转CLion

    声明:配置是基于CLion的2019.1版本 〇.CLion简介 一.安装 \(JetBrains\)官方下载地址:CLion2019.3 百度网盘:CLion2019.1 个人觉得还是2019.1版 ...

  6. 手把手教你启用Win10的Linux子系统(超详细)

    原文:手把手教你启用Win10的Linux子系统(超详细) 版权声明:转载请保留出处,谢谢! https://blog.csdn.net/zhangdongren/article/details/82 ...

  7. 手把手教你MyEclipseUML建模(上)

    手把手教你MyEclipseUML建模(上) 转 https://blog.csdn.net/qq_37939251/article/details/83444359 1.用UML 1建模 MyEcl ...

  8. 手把手教你实现RecyclerView的下拉刷新和上拉加载更多

    手把手教你实现RecyclerView的下拉刷新和上拉加载更多     版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明. 本文链接:https:// ...

  9. 手把手教你搭建FastDFS集群(下)

    手把手教你搭建FastDFS集群(下) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/u0 ...

随机推荐

  1. Spring Boot Starter自定义实现三步曲

    实现自定义的spring boot starter,只需要三步: 1.一个Bean 2.一个自动配置类 3.一个META-INF/spring.factories配置文件 下面用代码演示这三步. 项目 ...

  2. Chapter 4 图

    Chapter 4 图 . 1-   图的存储结构 无向图:对称 有向图:…… 2-   图的遍历 1   深度优先搜索(DFS) 类似于二叉树的先序遍历 2   广度优先搜索(BFS) 类似于二叉树 ...

  3. css3 做border = 0.5px的细线

    参考: https://blog.csdn.net/Tyro_java/article/details/52013531

  4. JS播放视频代码

    <div class="col-md-8"> <div id="moviecontainer"></div> </di ...

  5. HZOI20190902模拟35题解

    题面: A:公园 DAG上想拓扑dp 然而博主记忆化搜索了一下 设f[i][j]表示从i节点走j个点出公园所用的最小时间 则$f[u][i]=min(f[v][j-1]+dis_{u,v})$; 然后 ...

  6. element-ui 使用笔记

    1,获取级联选择器 cascader的值 获取value值:就是v-model绑定的值, 获取label值:要先给cascader组件一个ref值,然后通过 this.$refs.组件的ref值.cu ...

  7. PHP--通用化API接口数据输出 封装

    /** * 通用化API接口数据输出 * author qinpeizhou * @param $message * @param array $data * @param int $httpCode ...

  8. Python导出DBF文件到Excel的方法

    Python导出DBF文件到Excel的方法 这篇文章主要介绍了Python导出DBF文件到Excel的方法,实例分析了Python基于win32com模块实现文件导出与转换的相关技巧,分享给大家供大 ...

  9. PAT甲级——A1038 Recover the Smallest Number

    Given a collection of number segments, you are supposed to recover the smallest number from them. Fo ...

  10. mysql基本笔记之一

    1.创建数据库 CREATE DATABASE admin 2.查看数据库 SHOW DATABASES 3.使用数据库 USE admin 4.创建user表 CREATE TABLE user V ...