简单介绍
ActiveMQ有两种模型,一种是Queue,在这种情况下producer生产的消息在队列中,如果有多个消费者那么她们会依次消费,也就是A消费一个之后下一个由B消费;另一种是topic方式,该方式类似于广播模式,如果consumer有多个,那么他们会同时收到消息。
实践方式
1. 建立一个SpringBoot的项目,然后加入其中的依赖
org.springframework.boot
spring-boot-starter-activemq
2. 建立一个配置类,在配置类里面配置Queue和Topic的Destination。同时,由于Springboot的@JmsListener
默认只监听Queue的消息,因此需要为Topic配置containerFactory。在该配置类中,我们可以通过查看源码的方式知道Queue和Topic都是实现了Destination的接口。因为该类是配置类,因此需要加上@Configuration
的注解。具体代码如下:
package com.example.demo.config;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import javax.jms.ConnectionFactory;
import javax.jms.Queue;
import javax.jms.Topic;
@Configuration
public class ActiveMQConfig {
/**
* 为Queue配置Destination
* @return
*/
@Bean
public Queue queue() {
return new ActiveMQQueue("test.queue");
}
/**
* 为Topic配置Destination
* @return
*/
@Bean
public Topic topic() {
return new ActiveMQTopic("test.topic");
}
/**
* 为Topic配置containerFactory
* @param connectionFactory
* @return
*/
@Bean
public JmsListenerContainerFactory topicListenerContainer(ConnectionFactory connectionFactory){
DefaultJmsListenerContainerFactory topicListenerContainer = new DefaultJmsListenerContainerFactory();
topicListenerContainer.setPubSubDomain(true);
topicListenerContainer.setConnectionFactory(connectionFactory);
return topicListenerContainer;
}
}
3. 编写queue的producer和consumer
在Queue的模式下,producer负责生产消息,然后consumer负责消费消息,如果有多个consumer,那么consumers将会采用轮换的方式消费消息。也就是如果有ABC三个consumer,那么消费的顺序将会是A先消费一个消息,然后B再消费一个消息,然后C再消费一个消息,就这样一直轮换。
Producer的代码如下:
package com.example.demo.mq;
import org.apache.activemq.command.ActiveMQMapMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
import javax.jms.MapMessage;
import javax.jms.Queue;
@Component
public class QueueProducer {
@Autowired
private JmsTemplate jmsTemplate;
@Autowired
private Queue queue;
/**
* producer负责发送消息
* @param message
*/
public void send(String message) {
try {
MapMessage mapMessage = new ActiveMQMapMessage();
mapMessage.setString("info",message);
this.jmsTemplate.convertAndSend(this.queue,mapMessage);
}catch (Exception e) {
e.printStackTrace();
}
}
}
Consumer的代码如下:
package com.example.demo.mq;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
/**
* Queue的消费者,为了展示轮流消费的效果,其中定义了两个消费者
*/
@Component
public class QueueConsumer {
/**
* 注解中的destination是我们最开始的
* 配置类中设置的destination
* @param message
*/
@JmsListener(destination = "test.queue")
public void receiveQueue(Message message) {
try {
MapMessage mapMessage = (MapMessage) message;
System.out.println("Q1 消费:" + mapMessage.getString("info"));
}catch (JMSException e) {
e.getMessage();
}
}
@JmsListener(destination = "test.queue")
public void receiveQueue1(Message message){
try {
MapMessage mapMessage = (MapMessage) message;
System.out.println("Q2 消费:" + mapMessage.getString("info"));
}catch (JMSException e) {
e.printStackTrace();
}
}
}
4、编写一个controller进行测试
package com.example.demo;
import com.example.demo.mq.QueueConsumer;
import com.example.demo.mq.QueueProducer;
import com.example.demo.mq.TopicProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 测试的controller
*/
@Controller
public class TestController {
/**
* 因为在前面已经使用@Component注解
* 因此现在可以使用依赖注入的方式获取
*/
@Autowired
QueueProducer queueProducer;
@Autowired
TopicProducer topicProducer;
@RequestMapping("/test")
@ResponseBody
public void test(String test){
queueProducer.send(test);
// topicProducer.send(test);
}
}
测试
此时,我们进入网址localhost:8080/test?test=123
并访问,我们可以看到控制台将会输出结果,如果多访问几次该网址,我们就能够发现两个queue的consumer在轮流消费消息。效果如下:
topic的消费模式
在topic模式下,一个producer生产消息然后多个consumer同时消费消息。我们先编写producer,方式和queueProducer相似。代码如下:
package com.example.demo.mq;
import org.apache.activemq.command.ActiveMQMapMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
import javax.jms.MapMessage;
import javax.jms.Topic;
/**
* topic的生产者,其代码和queueProducer相似
* 区别只有注入的是Topic而已
*/
@Component
public class TopicProducer {
@Autowired
private JmsTemplate jmsTemplate;
@Autowired
private Topic topic;
public void send(String message) {
try {
MapMessage mapMessage = new ActiveMQMapMessage();
mapMessage.setString("info",message);
this.jmsTemplate.convertAndSend(this.topic,mapMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
}
下面开始编写topicConsumer的代码,由@JmsListener
注解默认只监听queue,因此我们需要使用刚才在配置类中定义的containerFactory。
代码如下:
package com.example.demo.mq;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
/**
* 该类为topic的consumer,由于
* @JmsListener默认只监听Queue,
* 因此需要使用自定义的containerFactory
*/
@Component
public class TopicConsumer {
@JmsListener(destination = "test.topic", containerFactory = "topicListenerContainer")
public void receiveTopic1(Message message){
try {
MapMessage mapMessage = (MapMessage)message;
System.out.println("t1消费:" + mapMessage.getString("info"));
}catch (JMSException e){
e.printStackTrace();
}
}
@JmsListener(destination = "test.topic", containerFactory = "topicListenerContainer")
public void receiveTopic2(Message message){
try {
MapMessage mapMessage = (MapMessage)message;
System.out.println("t2消费:" + mapMessage.getString("info"));
}catch (JMSException e){
e.printStackTrace();
}
}
}
为了测试该代码只需要将controller中的一行注释掉的代码恢复就可以了。测试结果如下;
到此,结束,相信大家也对这两种方式有了一定的了解。