ActiveMQ的简介,安装,使用
简介
1.what(什么是ActiveMQ)
ActiveMQ 是Apache出品的最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。
解释:
MQ: message queue ,消息队列,也叫消息中间件
JMS:即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
ActiveMQ:即是对JMS规范的实现,简单说就是两个程序或者分布式系统中系统之间的异步通信实现。
即:JMS是系统之间通信的规范,ActiveMQ是JMS的一种实现。
除了ActiveMQ之外其他的实现还有:RabbitMQ、kafka等都属于MQ,是MQ的产品。
2.why(为什么要使用它?)
为什么要使用它?,可以理解为它可以帮助我们完成那些事情,即他的作用是什么?
在分布式的SOA架构系统中,我们进行完整的服务操作难免需要依赖其他的服务,例如:我针对某一商品进行了修改,可能要影响我商品的显示(缓存),查询(索引)等。而这些服务的逻辑可能别的系统中。
可解决的思路如下:
1.在当前服务下,续写别的服务的代码 (很明显代码冗余)
2.系统服务之间的通信(WebService,Duboo 等,这是很不错的方案)
3.通过消息队列,修改商品时发送消息,在别的系统中监听消息,然后针对这一消息做出响应。
可以看出:使用消息队列解决系统之间通信的优点是:降低了系统之间代码的耦合度,也降低的服务的管理难度,即在当前服务中不存在所依赖服务的代码,而是通过发送消息的方式,通知相应的系统做出相应的响应。
3.where(在那里使用?)
通过上述的分析,我们已经很容易可以看出,消息队列是用于系统之间进行通信的一种方式。
栗子:
xx商城的后台管理系统需要对A商品进行修改,而A商品的销售情况非常好,所以在前端商品页面中存在redis缓存。现在要对A的价格进行调动,此时如果利用消息队列来实现, 则在A修改的时候,发送一条消息出去,此时前台系统监听接受到该消息后重置该商品的缓存,以保证数据的准确性。
安装
1.环境准备
- Linux系统
- JDK
- ActiveMQ安装包(Apahc官方下载安装即可)
2.Linux下安装JDK
1.登录,切换到root用户
2.在usr目录下新建Java文件夹
mkdir /usr/java
3.拷贝jdk的文件夹到该目录,然后解压
cp jdk的目录 /usr/java
tar -zxvf jdk目录
4.编辑配置文件,配置环境变量
vim /etc/profile
添加如下内容:JAVA_HOME根据实际目录来
JAVA_HOME=/usr/java/jdk1.8.0_60
CLASSPATH=$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin
export PATH JAVA_HOME CLASSPATH
5.重启机器或重置profile文件
方式1:重新加载profile文件 source /etc/profile (推荐)
方式2:重启机器 sudo shutdown -r now
6.检测安装是否成功
java -version
如出现jdk的版本信息,则安装成功。
3.安装ActiveMQ
1.官网下载ActiveMQ后,上传到Linux系统中
2.解压
3.进入bin文件启动ActiveMQ即可(内置jetty服务器)
启动: ./activemq start
关闭: ./activemq stop
查看状态: ./activemq status
4.在浏览器输入 http://ip:8161/admin
回车后输入账号和密码都为admin即可。
使用
1.理论知识
对于消息的传递有两种类型:
一种是点对点的,即一个生产者和一个消费者一一对应;
另一种是发布/订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收。
JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。
· StreamMessage – Java原始值的数据流
· MapMessage–一套名称-值对
· TextMessage–一个字符串对象
· ObjectMessage–一个序列化的 Java对象
· BytesMessage–一个字节的数据流
2.常用的API
导入依赖:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.8</version>
</dependency>
1>点对点(一对一)-Queue
生产者:
@Test
public void testMQProducerQueue() throws JMSException {
// 1. 创建工厂对象-->需要指定ip和端口
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.133:61616");
// 2.通过连接工厂构建 构建一个真实连接对象
Connection connection = connectionFactory.createConnection();
// 3.开启连接
connection.start();
// 4.通过连接对象,构建一个session回话对象
// 参数1: 是否开启事务 参数2:消息的应答模式只有1为false才有效,手动?自动
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5.通过会话对象,构建一个Destination对象(queue 、 topic) ,指明消息发送的模式
// Destination 目的的 queue队列(一对一)、topic主题(一对多),参数:就是本次消息的名字
Queue queue = session.createQueue("test-queue");
// 6.通过回话创建一个消息发送者 ,并且指明发送消息的传递类型
MessageProducer producer = session.createProducer(queue);
// 7.构建消息 Message,此处构建一个TextMessage;
TextMessage textMessage = session.createTextMessage("this is queue first message: Hello World");
// 8.发送消息
producer.send(textMessage);
// 9. 关闭资源
producer.close();
session.close();
connection.close();
}
消费者
@Test
public void testMQConsumerQueue() throws JMSException, InterruptedException {
// 1. 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.133:61616");
// 2.构建真实的连接
Connection connection = connectionFactory.createConnection();
// 3.开启连接
connection.start();
// 4.通过连接 构建session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5.通过session 构建接受消息的模式以及名字
Queue queue = session.createQueue("test-queue");
// 6.通过session 构建一个消息接受者,指明要接受的队列模式
MessageConsumer consumer = session.createConsumer(queue);
// 7. 监听该队列,接受消息
consumer.setMessageListener(message -> {
if (message != null && message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// 此处的休眠,目的是为了防止主线程先结束,导致开启的监听消息的线程还未接受的消息就挂了。
Thread.sleep(2000000);
// 8 .关闭资源
consumer.close();
session.close();
connection.close();
}
测试:
发送之前:
发送一条消息:运行testMQProducerQueue()
接受消息后:
待处理消息为0,由于我们睡眠的时间比较久,所以目前消费者为1(当前sleep结束后则为0),消费的消息为1.
栗子1:我们连续发送两条消息,然后开启一个接收方:
可以看出只要消费者线程不关闭,他就会一直获取消息。
如果有两个消费者会怎样呢?
栗子2:一条消息,两个消费者:
大家可以自己手动测试看看,可以发现,消息的传递是轮询的,即A一次,B一次,
先开启的消费者会优先得到第一次消息。
通过上述的案例我们可以看出Queue模式的特点:queue消息
默认是存在于MQ的服务器中的,发送消息之后,随时取。但是一定是一个消费者取完就没了。
2>发布/订阅(一对多)-Topic
这里我就不贴代码了,只需要把上述一对一中Queue改成Topic即可。
栗子1:先发送消息,在接受消息。
无法接受到消息,查看后,发现确实存在一套消息,但是已处理消息为0。
原因很简单,topic的消息不会存在于MQ中,所以必须先开启消费者,在发送消息。
从左->右 : 一个消费者(线程未关闭),总共两条消息,一条已处理消息。
栗子2:发送一条消息,多个(两个)接受者。
可以看出发送了一条消息,但是接受了两条消息。
3.Spring整合ActiveMQ
导入依赖:
spring+activemq(这里我们直接导入webmvc的依赖)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.8</version>
</dependency>
这里我们没有引入spring和activemq的整合包,但是后边的程序却没有出错,所以大家依据自己的情况尝试。
1>生产者配置
applicationContext配置文件
<!--配置ActiveMQ的连接工厂-->
<bean id="targetConnection"
class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.133:61616"></property>
</bean>
<!-- 通用的connectionfacotry 指定真正使用的连接工厂 -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory"
ref="targetConnection"></property>
</bean>
<!-- 接收和发送消息时使用的类 -->
<bean class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"></property>
</bean>
<!-- 配置destination -->
<!-- 队列目的地 -->
<bean id="queueDestination"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg name="name" value="item-change-queue"></constructor-arg>
</bean>
<!-- 话题目的地 -->
<bean id="topicDestination"
class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg name="name" value="item-change-topic"></constructor-arg>
</bean>
Java测试代码
@Test
public void send() throws Exception {
// 1.初始化spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext-activemq.xml");
// 2.获取到jmstemplate的对象
JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
// 3.获取destination
Destination destination = (Destination) context.getBean("queueDestination");
// 4.发送消息
jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("通过spring发送的消息123");
}
});
}
2>消费者配置
applicationContext配置文件
<!--配置ActiveMQ的连接工厂-->
<bean id="targetConnection"
class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.133:61616"></property>
</bean>
<!-- 通用的connectionfacotry 指定真正使用的连接工厂 -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory"
ref="targetConnection"></property>
</bean>
<!-- 配置destination -->
<!-- 队列目的地 -->
<bean id="queueDestination"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg name="name" value="item-change-queue"></constructor-arg>
</bean>
<!-- 话题目的地 -->
<bean id="topicDestination"
class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg name="name" value="item-change-topic"></constructor-arg>
</bean>
<!-- 配置自定义监听器 -->
<bean id="myMessageListener"
class="activemq_consumer.MyMessageListener"></bean>
<!-- 配置监听器容器 -->
<bean
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"></property>
<property name="destination" ref="queueDestination"></property>
<property name="messageListener" ref="myMessageListener"></property>
</bean>
自定义监听器
public class MyMessageListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
String text;
try {
text = textMessage.getText();
System.out.println(text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
测试代码:
@Test
public void consumer() throws InterruptedException {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext-activemq.xml");
//sleep的目的是为了,防止因线程结束,而接受不到消息
Thread.sleep(100000);
}
3>测试
详细测试代码这里就不说了,大家仿照之前的进行测试就好。