1、使用Docker安装ActiveMQ
要实现ActiveMQ的通讯,首先肯定是需要一个ActiveMQ服务器,对于ActiveMQ的安装,可以采用常规方法,也可以使用Docker直接拉一个镜像,这里我推荐使用Docker,根据下面两篇文章,就可以搭建出ActiveMQ的环境:
不是使用Ubuntu的可以自行百度教程解决,另外由于在国内Docker拉镜像比较慢,附一个解决方案:
2、实现ActiveMQ通讯
2.1、添加ActiveMQ相关依赖
<!-- activemq jar -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.9</version>
</dependency>
2.2、Queue
ActiveMQ通讯有两种方式:
使用队列通讯就好像两个人打电话,A发送消息给队列,B从队列中获得消息,如下图:
需要注意的是,A发送到Queue里的消息,B和C之间只有一个可以获得,谁获得不重要,A只负责发送消息。
在ActiveMQ队列通讯中发送消息的被称为生产者,接收消息的被称为消费者,其代码分别如下:
2.2.1 生产者
public class JmsProduce {
// ActiveMQ服务器地址,端口号默认为61616,这里由于是Docker启动,端口映射为了61617,写代码的时候,应做相应更改
private static final String ACTIVEMQ_URL = "tcp://192.168.192.129:61617";
// 队列名
private static final String QUEUE_NAME = "queue01";
public static void main(String[] args) throws JMSException {
// 根据ActiveMQ的服务器地址创建ActiveMQ连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 通过连接工厂创建连接
Connection connection = activeMQConnectionFactory.createConnection();
// 开启连接
connection.start();
// 使用连接创建一个会话,两个参数分别为:1、是否开启会话【false表示不开启】;2、消息应答模式【AUTO_ACKNOWLEDGE表示自动应答,消息接收方自动给予回应】
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 通过会话与队列名创建队列
Queue queue = session.createQueue(QUEUE_NAME);
// 通过队列创建消息生产者
MessageProducer producer = session.createProducer(queue);
for(int i=0;i<3;i++){
TextMessage textMessage = session.createTextMessage("msg " + i);
// 使用生产者向队列发送文本消息
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("END.");
}
}
2.2.2 消费者
消息消费者有两种获取消息的模式:
2.2.2.1 同步阻塞
消费者一直等待生产者往队列中发送消息的模式叫做同步阻塞
public class JmsConsumer {
// ActiveMQ服务器地址,端口号默认为61616,这里由于是Docker启动,端口映射为了61617,写代码的时候,应做相应更改
private static final String ACTIVEMQ_URL = "tcp://192.168.192.129:61617";
// 队列名
private static final String QUEUE_NAME = "queue01";
public static void main(String[] args) throws JMSException {
// 根据ActiveMQ的服务器地址创建ActiveMQ连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 通过连接工厂创建连接
Connection connection = activeMQConnectionFactory.createConnection();
// 开启连接
connection.start();
// 使用连接创建一个会话,两个参数分别为:1、是否开启会话【false表示不开启】;2、消息应答模式【AUTO_ACKNOWLEDGE表示自动应答,消息接收方自动给予回应】
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 通过会话与队列名创建队列
Queue queue = session.createQueue(QUEUE_NAME);
// 通过队列创建消息生产者
MessageConsumer consumer = session.createConsumer(queue);
// 同步阻塞
while (true) {
// 使用消费者从队列中获取数据
TextMessage receive = (TextMessage) consumer.receive();
if (null != receive) {
System.out.println("receive: " + receive.getText());
} else {
break;
}
}
consumer.close();
session.close();
connection.close();
}
}
2.2.2.2 异步不阻塞
消费者通过监听器监听队列中是否有消息发送的模式叫做异步不阻塞
public class JmsConsumer {
// ActiveMQ服务器地址,端口号默认为61616,这里由于是Docker启动,端口映射为了61617,写代码的时候,应做相应更改
private static final String ACTIVEMQ_URL = "tcp://192.168.192.129:61617";
// 队列名
private static final String QUEUE_NAME = "queue01";
public static void main(String[] args) throws JMSException {
// 根据ActiveMQ的服务器地址创建ActiveMQ连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 通过连接工厂创建连接
Connection connection = activeMQConnectionFactory.createConnection();
// 开启连接
connection.start();
// 使用连接创建一个会话,两个参数分别为:1、是否开启会话【false表示不开启】;2、消息应答模式【AUTO_ACKNOWLEDGE表示自动应答,消息接收方自动给予回应】
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 通过会话与队列名创建队列
Queue queue = session.createQueue(QUEUE_NAME);
// 通过队列创建消息生产者
MessageConsumer consumer = session.createConsumer(queue);
// 异步非阻塞,使用消费者通过监听器获取队列中的数据
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
// 将消息转换为文本消息,此处是因为已经知道发送的是文本消息,不然应该进行类型判断
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("textMessage.getText() = " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
try {
// 本行代码使程序不终止,防止异步读取消息还未完成,程序先行终结
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
consumer.close();
session.close();
connection.close();
}
}
2.2.3 测试
生产者和消费者都创建好后,分别运行,此处运行顺序无关紧要,可在消费者界面中看到生产者发送的消息,如下图:
同时在浏览器中输入ActiveMQ控制台地址【http://192.168.1.2:8162/admin/queues.jsp,IP和端口自行替换,帐号密码都是admin】,如下图:
可以看到ActiveMQ服务器中已经有了相应的消息队列。
2.3 Topic
话题通讯就好比我们订阅一个微信公众号,公众号向所有订阅这个公众号的人发送消息,如下图:
A向话题发送消息,而B和C从话题获取同样的消息,在话题通讯中发送消息的同样被称为生产者,但也被称为发布者,而接收消息的同样被称为消费者,也被称为订阅者,其代码分别如下:
2.3.1 生产者
public class JmsProduceTopic {
// ActiveMQ服务器地址,端口号默认为61616,这里由于是Docker启动,端口映射为了61617,写代码的时候,应做相应更改
private static final String ACTIVEMQ_URL = "tcp://192.168.1.2:61617";
// 话题名
private static final String TOPIC_NAME = "topic-alageek";
public static void main(String[] args) throws JMSException {
// 根据ActiveMQ的服务器地址创建ActiveMQ连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 通过连接工厂创建连接
Connection connection = activeMQConnectionFactory.createConnection();
// 开启连接
connection.start();
// 使用连接创建一个会话,两个参数分别为:1、是否开启会话【false表示不开启】;2、消息应答模式【AUTO_ACKNOWLEDGE表示自动应答,消息接收方自动给予回应】
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 通过会话与话题名创建话题
Topic topic = session.createTopic(TOPIC_NAME);
// 通过话题创建消息生产者
MessageProducer producer = session.createProducer(topic);
for(int i=0;i<6;i++){
TextMessage textMessage = session.createTextMessage("msg " + i);
// 使用生产者向队列发送文本消息
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("END.");
}
}
2.3.2 消费者
此处消费者同样有同步与异步两种方式,前面已经演示过,这里只给出异步代码:
public class JmsConsumerTopic {
// ActiveMQ服务器地址,端口号默认为61616,这里由于是Docker启动,端口映射为了61617,写代码的时候,应做相应更改
private static final String ACTIVEMQ_URL = "tcp://192.168.1.2:61617";
// 话题名
private static final String TOPIC_NAME = "topic-alageek";
public static void main(String[] args) throws JMSException {
// 根据ActiveMQ的服务器地址创建ActiveMQ连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 通过连接工厂创建连接
Connection connection = activeMQConnectionFactory.createConnection();
// 开启连接
connection.start();
// 使用连接创建一个会话,两个参数分别为:1、是否开启会话【false表示不开启】;2、消息应答模式【AUTO_ACKNOWLEDGE表示自动应答,消息接收方自动给予回应】
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 通过会话与话题名创建话题
Topic topic = session.createTopic(TOPIC_NAME);
// 通过话题创建消息消费者
MessageConsumer consumer = session.createConsumer(topic);
// 通过消费者创建监听器获取消息,此处使用lambda表达式简写代码
consumer.setMessageListener(message -> {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("textMessage.getText() = " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
});
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
consumer.close();
session.close();
connection.close();
}
}
2.3.3 测试
生产者和消费者都创建好后,先运行消费者,再运行生产者,此处顺序非常重要,因为消费者只会从话题中获取订阅后的消息,如果先启动生产者,再启动消费者,那么生产者生产的消息都是在订阅前发布的,消费者不会收到,按照次序运行后运行结果如下:
同样,ActiveMQ控制台话题界面也会出现对应的话题。
3、内嵌式ActiveMQ(Broker)
由于ActiveMQ是由Java编写,所以我们可以将它打成一个jar包,在程序中直接运行
3.1 添加Broker相关依赖
<!-- Broker -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
3.2 内嵌ActiveMQ
public class EmbedBroker {
public static void main(String[] args) throws Exception {
BrokerService brokerService = new BrokerService();
brokerService.setUseJmx(true);
brokerService.addConnector("tcp://localhost:61616");
brokerService.start();
}
}
运行上述代码就可以启动一个ActiveMQ服务器,启动生产者和消费者的时候,只需将服务器地址换成上面地址即可。