概述
ActiveMQ是由Apache出品的,一款最流行的,能力强劲的开源消息总线。ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。
特性
- 遵循JMS规范:ActiveMQ的各种特性是JMS1.1规范的实现。它们包括同步和异步消息传递,一次和只有一次的消息传递,对于预订者的持久消息等等。依附于JMS规范意味着,不论JMS消息提供者是谁,同样的基本特性都是有效的。(JMS可查看前篇博文(ActiveMQ基础教程(一)JMS概述https://www.jianshu.com/p/639627f88a6e)。
- 连接:ActiveMQ提供各种连接选择,包括HTTP,HTTPS,IP多点传送,SSL,STOMP,TCP,UDP,XMPP等。大量的连接协议支持使之具有更好的灵活性。
- 支持多种语言客户端:ActiveMQ对多种语言提供客户端API,除了Java之外还有C/C++、.NET、Perl、PHP、Ruby、Python等。这使得ActiveMQ能用在Java之外的其它语言中。很多其它语言都可以通过ActiveMQ提供的客户端API使用ActiveMQ的全部特性。当然,ActiveMQ代理器(broker)仍然是运行在java虚拟机上,但是客户端能够使用其它的被支持的语言。
- 可插拔的持久性和安全:ActiveMQ提供多种持久性方案可供选择,也可以完全按自己需求定制验证和授权。例如,ActiveMQ通过KahaDB提供自己的超快速消息持久方案(ultra-fast message persistence),但也支持标准的JDBC方案。ActiveMQ可以通过配置文件提供简单的验证和授权,也提供标准的JAAS登陆模块。
- 简单的管理:ActiveMQ是为开发者设计的。它并不需要专门的管理工具,因为它提供各种易用且强大的管理特性。有很多方法去监控ActiveMQ的各个方面,可以通过JMX使用JConsole或ActiveMQ web console;可以运行ActiveMQ消息报告;可以用命令行脚本;可以通过日志。
- 支持集群:为了利于扩展,多个ActiveMQ broker能够联合工作。这个方式就是network of brokers并且能支持多种拓扑结构。
安装与管理后台
安装
管理后台
登陆后页面.png
Queues页面
Queues是队列方式消息,从菜单栏中点击Queues可以进入到Queues页面,页面主要内容包括:
- Name:消息队列的名称。
- Number Of Pending Messages:未被消费的消息数目。
- Number Of Consumers:消费者的数量。
- Messages Enqueued:进入队列的消息 ;进入队列的总消息数目,包括已经被消费的和未被消费的。 这个数量只增不减。
- Messages Dequeued:出了队列的消息,可以理解为是被消费掉的消息数量。在Queues里它和进入队列的总数量相等(因为一个消息只会被成功消费一次),如果暂时不等是因为消费者还没来得及消费。
Topics页面
Topics页面.png
Topics是主题方式消息,从菜单栏中点击Topics可以进入到Topics页面,页面主要内容包括:
- Name:主题名称。
- Number Of Pending Messages:未被消费的消息数目。
- Number Of Consumers:消费者的数量。
- Messages Enqueued:进入队列的消息 ;进入队列的总消息数目,包括已经被消费的和未被消费的。 这个数量只增不减。
- Messages Dequeued:出了队列的消息,可以理解为是被消费掉的消息数量。在Topics里,因为多消费者从而导致数量会比入队列数高。
Subscribers页面
Subscribers 是查看订阅者的页面,可以查看订阅者的信息等。只在Topics消息类型中这个页面才会有数据。
Connections页面
Connections页面可以查看到所有的连接数。
使用
Queue消息模式
点对点的模式主要建立在一个队列上面,当连接一个列队的时候,发送端不需要知道接收端是否正在接收,可以直接向ActiveMQ发送消息,发送的消息将会先进入队列中,如果有接收端在监听,则会发向接收端,如果没有接收端接收,则会保存在activemq服务器,直到接收端接收消息,点对点的消息模式可以有多个发送端,多个接收端,但是一条消息,只会被一个接收端给接收到,哪个接收端先连上ActiveMQ,则会先接收到,而后来的接收端则接收不到那条消息。
生产者
public class JmsProducer {
/**
* 默认连接用户名
*/
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
/**
* 默认用户密码
*/
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
/**
* 默认连接地址
*/
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;
private static final String QUEUE_NAME = "queue.test";
public static void main(String[] args) {
/**
* 第一步:创建连接工厂
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
/**
* 连接
*/
Connection connection = null;
/**
* 会话
*/
Session session = null;
/**
* 消息目的地
*/
Destination destination = null;
/**
* 消息生产者
*/
MessageProducer messageProducer = null;
try {
/**
* 第二步:创建连接
*/
connection = connectionFactory.createConnection();
/**
* 启动连接
*/
connection.start();
/**
* 第三步:创建会话
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
/**
* 第四步:创建消息目的地,其实就是连接到哪个队列,如果是点对点,那么它的实现是Queue,如果是订阅模式,那它的实现是Topic。这里我们创建一个名为queue.test的消息队列。
*/
destination = session.createQueue(QUEUE_NAME);
/**
* 第五步:创建消息生产者
*/
messageProducer = session.createProducer(destination);
/**
* 第六步:发送消息,这个步骤包括创建消息,然后发送消息
*/
sendMessage(session, messageProducer);
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (null != session) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (null != connection) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
/**
* 发送消息
*
* @param session
* @param messageProducer
* @throws JMSException
*/
public static void sendMessage(Session session, MessageProducer messageProducer) throws JMSException {
for (int i = 0; i < 10; i++) {
/**
* 创建一条文本消息
*/
TextMessage message = session.createTextMessage("ActiveMQ 发送消息" + i);
System.out.println("发送消息:Activemq 发送消息" + i);
/**
* 通过消息生产者发出消息
*/
messageProducer.send(message);
}
}
}
运行结果图:
程序运行截图.png
ActiveMQ控制台截图.png
我们可以看的,当运行JmsProducer程序时,在ActiveMQ控制台,可以看到生产者往queue.test的队列中发送了10条消息,因为这时还没有消费者,所以这边的Number Of Pending Messages显示的是10,
Number Of Consumers显示的是0,Messages Enqueued显示的也是10。
消费者
public class JmsConsumer {
/**
* 默认连接用户名
*/
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
/**
* 默认用户密码
*/
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
/**
* 默认连接地址
*/
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;
private static final String QUEUE_NAME = "queue.test";
public static void main(String[] args) {
/**
* 第一步:创建连接工厂
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
/**
* 连接
*/
Connection connection = null;
/**
* 会话
*/
Session session = null;
/**
* 消息目的地
*/
Destination destination = null;
/**
* 消息消费者
*/
MessageConsumer messageConsumer = null;
try {
/**
* 第二步:创建连接
*/
connection = connectionFactory.createConnection();
/**
* 启动连接
*/
connection.start();
/**
* 第三步:创建会话
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
/**
* 第四步:创建消息目的地,其实就是连接到哪个队列,如果是点对点,那么它的实现是Queue,如果是订阅模式,那它的实现是Topic。这里我们创建一个名为queue.test的消息队列。
*/
destination = session.createQueue(QUEUE_NAME);
/**
* 第五步:创建消费者
*/
messageConsumer = session.createConsumer(destination);
while (true) {
/**
* 接收数据的时间(等待) 100 ms
*/
TextMessage textMessage = (TextMessage) messageConsumer.receive(1000 * 100);
if (textMessage != null) {
System.out.println("收到的消息:" + textMessage.getText());
} else {
break;
}
}
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (null != session) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (null != connection) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
运行结果图:
我们可以看到,但运行JmsConsumer程序时,在运行程序的控制台中我们可以看到消费者消费了刚刚生产者生产的消息。在ActiveMQ控制台,可以看到所以这边的Number Of Pending Messages显示的是0,Number Of Consumers显示的是1,Messages Enqueued显示的是10,Messages Dequeued显示的也是10,即消息被消费。
在前面的消费者例子中,我们这边使用while (true) 死循环来不停接受消息。这样很浪费cpu资源,实际生产中不会这么做。下面,我们采用注册一个监听器的方法,当监听到有消息入队列后,才去接收消息。
public class JmsConsumerMessageListener {
/**
* 默认连接用户名
*/
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
/**
* 默认用户密码
*/
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
/**
* 默认连接地址
*/
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;
private static final String QUEUE_NAME = "queue.test";
public static void main(String[] args) {
/**
* 第一步:创建连接工厂
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
/**
* 连接
*/
Connection connection = null;
/**
* 会话
*/
Session session = null;
/**
* 消息目的地
*/
Destination destination = null;
/**
* 消息消费者
*/
MessageConsumer messageConsumer = null;
try {
/**
* 第二步:创建连接
*/
connection = connectionFactory.createConnection();
/**
* 启动连接
*/
connection.start();
/**
* 第三步:创建会话
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
/**
* 第四步:创建消息目的地,其实就是连接到哪个队列,如果是点对点,那么它的实现是Queue,如果是订阅模式,那它的实现是Topic。这里我们创建一个名为queue.test的消息队列。
*/
destination = session.createQueue(QUEUE_NAME);
/**
* 第五步:创建消费者
*/
messageConsumer = session.createConsumer(destination);
/**
* 第六步:创建监听器
*/
messageConsumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("收到的消息:" + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
} catch (JMSException e) {
e.printStackTrace();
} finally {
// if (null != session) {
// try {
// session.close();
// } catch (JMSException e) {
// e.printStackTrace();
// }
// }
// if (null != connection) {
// try {
// connection.close();
// } catch (JMSException e) {
// e.printStackTrace();
// }
// }
}
}
}
运行结果图:
当生产者一生产消息到队列中时,我们的消费者就马上进行消费,注意程序中我们没有将会话和连接关闭,因为监听器是异步的,如果关闭后就无法接收到消息。
Topic消息模式
订阅/发布模式,同样可以有着多个发送端与多个接收端,但是接收端与发送端存在时间上的依赖,就是如果发送端发送消息的时候,接收端并没有监听消息,那么ActiveMQ将不会保存消息,将会认为消息已经发送,换一种说法,就是发送端发送消息的时候,接收端不在线,是接收不到消息的,哪怕以后监听消息,同样也是接收不到的。这个模式还有一个特点,那就是发送端发送的消息,将会被所有的接收端给接收到,不类似点对点,一条消息只会被一个接收端给接收到。
发布者
public class JmsProducer {
/**
* 默认连接用户名
*/
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
/**
* 默认用户密码
*/
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
/**
* 默认连接地址
*/
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;
private static final String TOPIC_NAME = "topic.test";
public static void main(String[] args) {
/**
* 第一步:创建连接工厂
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
/**
* 连接
*/
Connection connection = null;
/**
* 会话
*/
Session session = null;
/**
* 消息目的地,其实就是连接到哪个队列,如果是点对点,那么它的实现是Queue,如果是订阅模式,那它的实现是Topic
*/
Destination destination = null;
/**
* 消息生产者
*/
MessageProducer messageProducer = null;
try {
/**
* 第二步:创建连接
*/
connection = connectionFactory.createConnection();
/**
* 启动连接
*/
connection.start();
/**
* 第三步:创建会话
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
/**
* 第四步:创建消息目的地,这里我们创建一个名为topic.test的主题
*/
destination = session.createTopic(TOPIC_NAME);
/**
* 第五步:创建消息生产者
*/
messageProducer = session.createProducer(destination);
/**
* 第六步:发送消息,这个步骤包括创建消息,然后发送消息
*/
sendMessage(session, messageProducer);
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (null != session) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (null != connection) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
/**
* 发送消息
*
* @param session
* @param messageProducer
* @throws JMSException
*/
public static void sendMessage(Session session, MessageProducer messageProducer) throws JMSException {
for (int i = 0; i < 10; i++) {
/**
* 创建一条文本消息
*/
TextMessage message = session.createTextMessage("ActiveMQ 发送消息" + i);
System.out.println("发送消息:Activemq 发送消息" + i);
/**
* 通过消息生产者发出消息
*/
messageProducer.send(message);
}
}
}
运行结果图:
订阅者
public class JmsConsumer {
/**
* 默认连接用户名
*/
private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
/**
* 默认用户密码
*/
private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
/**
* 默认连接地址
*/
private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;
private static final String TOPIC_NAME = "topic.test";
public static void main(String[] args) {
/**
* 第一步:创建连接工厂
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
/**
* 连接
*/
Connection connection = null;
/**
* 会话
*/
Session session = null;
/**
* 消息目的地,其实就是连接到哪个队列,如果是点对点,那么它的实现是Queue,如果是订阅模式,那它的实现是Topic
*/
Destination destination = null;
/**
* 消息消费者
*/
MessageConsumer messageConsumer = null;
try {
/**
* 第二步:创建连接
*/
connection = connectionFactory.createConnection();
/**
* 启动连接
*/
connection.start();
/**
* 第三步:创建会话
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
/**
* 第四步:创建消息目的地,这里我们创建一个名为topic.test的主题
*/
destination = session.createTopic(TOPIC_NAME);
/**
* 第五步:创建消费者
*/
messageConsumer = session.createConsumer(destination);
messageConsumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("收到的消息:" + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
} catch (JMSException e) {
e.printStackTrace();
} finally {
// if (null != session) {
// try {
// session.close();
// } catch (JMSException e) {
// e.printStackTrace();
// }
// }
// if (null != connection) {
// try {
// connection.close();
// } catch (JMSException e) {
// e.printStackTrace();
// }
// }
}
}
}
运行结果图:
我们可以发现,Topic消息模式的代码跟Queue消息模式的代码基本是一样的,除了在创建消息目的地的时候,一个是queue一个是topic;还有一点区别就是,Topic消息模式,订阅者需要先订阅,才能接收到发布者发布的消息。
--------------------------------------------------------------------------------
作者:闽越布衣
链接:https://www.jianshu.com/p/0363ac9ff574
来源:简书