1 ActiveMQ简介
1.1支持多语言和协议便捷客户端。
语言:java、python、C、C++、php等
应用协议:OpenWrite,Stomp REST,XMPP,AMQP等
1.2对spring支持;
1.3支持通过JDBC和journal提供高速的消息持久化;即可保存在关系型数据库和内存File数据库中。
1.4支持集群,客户端-服务器,点对点
1.5支持与Ajax的整合等
2.ActiveMQ的安装
官网:http://activemq.apache.org/
2.1 windows安装
参考:
https://www.cnblogs.com/hanxiaohui/p/9425191.html
我是安装在linux上的,所以这里就略过了。
2.2 linux安装
参考:
https://www.jianshu.com/p/660d998b0b33
3.入门Demo
JMS开发的基本步骤
1)创建一个ConnectionFactory;
2)通过ConnectionFactory来创建JMSConnection
3)启动Connection
4)通过Connection创建JMSSession
5)通过Session创建消息目的地Destination
6)通过Session创建MessageProducer或MessageConsumer,并设置目的地
7)发送或接受消息(receice、监听器)
8)关闭资源
实际举例:
我是使用java项目来实现Demo的,也可以采用Maven工程方式;
创建java项目
导入ActiveMQ锁依赖的包:
3.2 PTP模式的实现
生产者代码:
package com.jzt.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author sj
*/
public class TextProducer {
public static String MQ_NAME = "admin";
public static String MQ_PASSWORD = "admin";
public static String MQ_BROKETURL = "tcp://192.168.106.131:61616";
public static void main(String[] args) throws JMSException {
//连接工厂
ConnectionFactory factory;
// 连接实例
Connection connection = null;
//会话
Session session = null;
// 消息发送目标地址
Destination destination;
// 消息创建者
MessageProducer messageProducer = null;
try{
factory = new ActiveMQConnectionFactory(MQ_NAME, MQ_PASSWORD, MQ_BROKETURL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("queue");
messageProducer = session.createProducer(destination);
for (int i = 0; i < 3; i++){
TextMessage textMsg = session.createTextMessage("这是第" + i + "条消息");
messageProducer.send(textMsg);
}
}catch (Exception e){
e.printStackTrace();
}finally {
messageProducer.close();
session.close();
connection.close();
}
}
}
消费者代码:
package com.jzt.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author sj
*/
public class TextConsumer {
public static String MQ_NAME = "admin";
public static String MQ_PASSWORD = "admin";
public static String MQ_BROKETURL = "tcp://192.168.106.131:61616";
public static void main(String[] args) {
ConnectionFactory factory = null;
Connection connection = null;
Session session = null;
Destination destination = null;
MessageConsumer messageConsumer = null;
try{
factory = new ActiveMQConnectionFactory(MQ_NAME, MQ_PASSWORD, MQ_BROKETURL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("queue");
messageConsumer = session.createConsumer(destination);
//方式1,通过receive()方法接收
// int i = 0;
// while (true) {
// // 设置接收者接收消息的时间,若参数为空,则表示会一直等待,直到接收到消息
// TextMessage message = (TextMessage) messageConsumer.receive();
// if (null != message) {
// i++;
// System.out.println("收到消息" + i +":"+ message.getText());
// } else {
// break;
// }
// }
//方式2:通过MessageListener监听器
/*
异步非阻塞式方式监听器(onMessage)
订阅者或消费者通过创建的消费者对象,给消费者注册消息监听器setMessageListener,
当消息有消息的时候,系统会自动调用MessageListener类的onMessage方法
我们只需要在onMessage方法内判断消息类型即可获取消息
*/
messageConsumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
if (null != message && message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
/*
这里是控制台保持活动状态
如果没有这句代码,当当监听器还没有监听到消息时,消费者线程已经结束,那么后续的 close()方法会关闭资源,导致无法获取到消息。
*/
System.in.read();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
messageConsumer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
启动生产者,查看ActiveMQ控制台:
启动消费者,查看控制台:
注意:
上面采用了两种方式接收消息:一种是通过receive()方法接收,另外一种使用Listenter监听器来接收。
两种接收消息的方式说明:
同步阻塞式(receive):
通过MessageConsumer的receive()方法来接收消息,当消息未接收到或者在指定的过期时间内未接受到消息时都会处于阻塞状态;
异步非阻塞式(监听器):
通过MessageConsumer的setMessageListenser注册一个消息监听器,当消息到的时,系统会自动调用MessageListener的onMessage(Message message)方法。
3.3 PUB&SUB模式
消息发布者:
package com.jzt.queue.topic;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author sj
*/
public class TopicProducer {
public static String MQ_NAME = "admin";
public static String MQ_PASSWORD = "admin";
public static String MQ_BROKETRUL = "tcp://192.168.106.131:61616";
public static void main(String[] args) {
try {
ConnectionFactory factory = new ActiveMQConnectionFactory(MQ_NAME, MQ_PASSWORD, MQ_BROKETRUL);
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination des = session.createTopic("topic");
MessageProducer producer = session.createProducer(des);
TextMessage message = session.createTextMessage("hello topic!");
producer.send(message);
producer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
消费者:
package com.jzt.queue.topic;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import javax.management.remote.rmi.RMIConnectionImpl;
/**
* @author sj
*/
public class TopicConsumer {
public static String MQ_NAME = "admin";
public static String MQ_PASSWARD = "admin";
public static String MQ_BROKETRUL = "tcp://192.168.106.131:61616";
public static void main(String[] args) {
Connection connection = null;
Session session = null;
Destination destination = null;
MessageConsumer consumer = null;
try {
ConnectionFactory factory = new ActiveMQConnectionFactory(MQ_NAME, MQ_PASSWARD, MQ_BROKETRUL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("topic");
consumer = session.createConsumer(destination);
// consumer.setMessageListener(new MessageListener() {
// @Override
// public void onMessage(Message message) {
// if (message instanceof TextMessage){
// TextMessage textMessage = (TextMessage) message;
// try {
// System.out.println(textMessage.getText());
// } catch (JMSException e) {
// e.printStackTrace();
// }
// }
// }
// });
consumer.setMessageListener(message -> {
if (message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
} catch (JMSException e) {
e.printStackTrace();
}finally {
try {
consumer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
发布订阅模式需要先启动消费者,再启动生产者,不然发送的消息是费消息。
小结:
两种模式比较:
Queue模式 | topic模式 | |
工作模式 | “点对点”模式,如果当前没有消费者消费消息,则消息不会丢弃,如果当前有多个消费者消费消息,则一条消息只会被其中一个消费者接收,并且要求消费者ACK信息; | “发布订阅”模式,如果当前没有消费者订阅消息,则消息会被丢弃;如果有多个消费者订阅消息,则多个订阅者都会收到消息; |
传递的完整性 | 消息不会被丢弃 | 如果没有消费者,消息会被丢弃 |
有无状态 | queue消息一般会以文件形式保存,也可以配置DB | 无状态 |
处理效率 | 不会因为消费者增多而导致性能下降 | 消费者增多,消息的复制份数增加,性能会下降 |