前言:mq的订阅/发布模式和点对点模式的区别网上很多,博主不再赘述,直接上代码:
网上很多教程,操作相对简单,这里我就不详细说了。大概就是:
org.springframework.boot
spring-boot-starter-activemq
//端口默认为61616
spring.activemq.broker-url=tcp://192.168.1.110:61616
spring.activemq.user=admin
spring.activemq.password=admin
将mq的连接信息写入配置类的意义在于便于开发环境和生产环境的切换,不理解的可以留言,我看到了会回复的。
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.config.SimpleJmsListenerContainerFactory;
import org.springframework.jms.core.JmsMessagingTemplate;
@Configuration
public class ActiveMQConfiguration {
//这个Bean用于和mq建立连接、mq的地址、用户名、密码从配置文件中获取
@Bean(name = "innerConnectionFactory")
@Primary
public ActiveMQConnectionFactory firstConnectionFactory(
//mq服务地址
@Value("${spring.activemq.broker-url}") String brokerUrl,
//mq用户名
@Value("${spring.activemq.user}") String username,
//mq密码
@Value("${spring.activemq.password}") String password) {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
factory.setBrokerURL(brokerUrl);
factory.setUserName(username);
factory.setPassword(password);
return factory;
}
//这个Bean用于发布主题消息或者生产队列消息
@Bean(name = "jmsMessagingTemplate")
@Primary
public JmsMessagingTemplate jmsMessagingTemplate(
//将“innerConnectionFactory”这个Bean注入
@Qualifier("innerConnectionFactory") ActiveMQConnectionFactory connectionFactory) {
JmsMessagingTemplate template = new JmsMessagingTemplate(connectionFactory);
return template;
}
/**
* 在Queue模式中,对消息的监听需要对containerFactory进行配置
*/
@Bean("queueJmsListenerContainerFactory")
public JmsListenerContainerFactory> queueJmsListenerContainerFactory(
//将“innerConnectionFactory”这个Bean注入
@Qualifier("innerConnectionFactory") ActiveMQConnectionFactory connectionFactory){
SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setPubSubDomain(false);
return factory;
}
/**
* 在Topic模式中,对消息的监听需要对containerFactory进行配置
*/
@Bean("topicJmsListenerContainerFactory")
public JmsListenerContainerFactory> topicJmsListenerContainerFactory(
//将“innerConnectionFactory”这个Bean注入
@Qualifier("innerConnectionFactory") ActiveMQConnectionFactory connectionFactory){
SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setPubSubDomain(true);
return factory;
}
}
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Service;
import javax.jms.Destination;
@Service
public class TopicPublisherService {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
/**
* 发布主题消息
* @destinationName 主题名
* @message 消息
*/
public void publishMsg(String destinationName, String message) {
Destination destination = new ActiveMQTopic(destinationName);
jmsMessagingTemplate.convertAndSend(destination, message);
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
@Service
public class TopicSubscriberService {
@Autowired
SimpMessagingTemplate simpMessagingTemplate;
/**
* 订阅主题
*/
@JmsListener(destination = "test.topic",containerFactory = "topicJmsListenerContainerFactory")
public void subscribeTopicWebsocketSystemNoticeMsg(String message) {
System.out.println("接收到了一条主题消息:" + message);
}
}
import cn.xxx.SpringBootApplication;
import cn.xxx.mq.TopicPublisherService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringBootApplication.class) // 指定我们SpringBoot工程的Application启动类
@WebAppConfiguration // 由于是Web项目,Junit需要模拟ServletContext,因此我们需要给我们的测试类加上@WebAppConfiguration。
public class TestMq {
@Autowired
private TopicPublisherService topicPublisherService;
@Test
public void TestPublishMQTopic(){
topicPublisherService.publishMsg("test.topic","test...");
}
}
先用application启动项目,然后运行“TestPublishMQTopic()”方法,然后打开ActiveMQ的主页查看是否成功(我部署的路径为http://192.168.1.160:8161,8681是打开mq主页的默认端口):
首先进入首页:
然后点击Topic,查看所有的主题
找到test.topic这个主题,可以看到,有消息发布成功:
然后在控制台看到:
说明收到了发布的消息!
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.jms.Destination;
@Service
public class MessageProducerService {
@Resource
private JmsMessagingTemplate jmsMessagingTemplate;
/**
* 发送队列消息
* @param destinationName 消息目的地标识
* @param message 消息文本
*/
public void sendMsg(String destinationName, String message) {
Destination destination = new ActiveMQQueue(destinationName);
jmsMessagingTemplate.convertAndSend(destination, message);
}
}
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;
@Service
public class MessageConsumerService {
@JmsListener(destination="test.msg.queue",containerFactory = "queueJmsListenerContainerFactory")
public void receiveMessage(String text) { // 进行消息接收处理
System.out.println("【*** 接收消息 ***】" + text);
}
}
import cn.sinvie.SpringBootApplication;
import cn.sinvie.mq.MessageProducerService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringBootApplication.class) // 指定我们SpringBoot工程的Application启动类
@WebAppConfiguration // 由于是Web项目,Junit需要模拟ServletContext,因此我们需要给我们的测试类加上@WebAppConfiguration。
public class TestMq {
@Autowired
private MessageProducerService messageProducerService;
@Test
public void TestSendMQQueue(){
messageProducerService.sendMsg("test.msg.queue","test...");
}
}
先后运行SpringBootApplication主程序和TestSendMQQueue测试方法,然后去MQ的主页查看queue,看看是否有“test.msg.queue”这个消息队列
有就证明生产者生产消息成功,然后再看控制台是否打印了消息
成功打印了消息,说明消费者类消费成功!
实现了订阅/发布模式和点对点模式以后,可能会有疑问,两者实现的功能看起来不是一样的吗?
当然有区别,简单的来说,有这些区别:
Topic--订阅/发布 | Queue--点对点 |
实时通讯,发布时,若订阅者未连接则消息丢失 | 持久化,消息被消费者消费后才会消失 |
需要先订阅,后发布 | 生产了就能收到 |
发布的消息所有的订阅者都能收到 | 生产者生产的消息只能被消费者消费一次,故又被称为点对点模式 |