在我们使用ActiveMQ的时候我们有时候希望可以向多个不同的queue和topic同时发送相同的消息,有时候又想用一个消费者来同时消费不同的queue和topic,前者我们可以直接通过jms做到,但是后者使用jms不能做到,只能同时消费queue或者topic的之一,我们看下使用原生的代码实现。
注意:原生的代码是利用他人的代码。
生产者代码:
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class ProducerThread extends Thread {
String brokerUrl;
String destinationUrl;
public ProducerThread(String brokerUrl, String destinationUrl) {
this.brokerUrl = brokerUrl;
this.destinationUrl = destinationUrl;
}
@Override
public void run() {
ActiveMQConnectionFactory connectionFactory;
Connection conn;
Session session;
try {
// 1、创建连接工厂
connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
// 2、创建连接
conn = connectionFactory.createConnection();
conn.start(); // 一定要start
// 3、创建会话(可以创建一个或者多个session)
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 4、创建消息发送目标 (Topic or Queue)
Destination destination = session.createQueue(destinationUrl);
// 5、用目的地创建消息生产者
MessageProducer producer = session.createProducer(destination);
// 设置递送模式(持久化 / 不持久化)
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
// 6、创建文本消息
for (int i = 0; i < 5; i++) {
String text = "message! From: " + Thread.currentThread().getName() + " : "
+ System.currentTimeMillis();
TextMessage message = session.createTextMessage(text);
// 7、通过producer 发送消息
producer.send(message);
System.out.println("Sent message: " + text + " id: " + message.getJMSMessageID());
Thread.sleep(2000L);
}
// 8、 清理、关闭连接
session.close();
conn.close();
} catch (JMSException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
消费者代码
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class ConsumerThread extends Thread {
String brokerUrl;
String destinationUrl;
public ConsumerThread(String brokerUrl, String destinationUrl) {
this.brokerUrl = brokerUrl;
this.destinationUrl = destinationUrl;
}
@Override
public void run() {
ActiveMQConnectionFactory connectionFactory;
Connection conn;
Session session;
try {
// 1、创建连接工厂
connectionFactory = new ActiveMQConnectionFactory(this.brokerUrl);
// 2、创建连接对象
conn = connectionFactory.createConnection();
conn.start(); // 一定要启动
// 3、创建会话(可以创建一个或者多个session)
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 4、创建消息消费目标(Topic or Queue)
//Destination destination = session.createQueue(destinationUrl);
Destination destination = session.createTopic(destinationUrl);
// 5、创建消息消费者 http://activemq.apache.org/destination-options.html
MessageConsumer consumer = session.createConsumer(destination);
// 6、异步接收消息
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(Thread.currentThread().getName() + " Time: " + System.currentTimeMillis()
+ " 收到文本消息:" + ((TextMessage) message).getText() + " id: " + message.getJMSMessageID());
} catch (JMSException e) {
e.printStackTrace();
}
} else {
System.out.println(message);
}
}
});
// consumer.close();
// session.close();
// conn.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
1.同时创建一个queue和topic
new ProducerThread("tcp://localhost:61616", "queue2,topic://topic2").start();
消费者消费
new ConsumerThread("tcp://localhost:61616", "queue://queue2,topic2").start();
通过打印出现的效果我们可以看到它只消费到了topic的消息,具体消息打印就不浪费字体了,可以自己去试一下。
2.同时创建2个topic
new ProducerThread("tcp://localhost:61616", "topic://topic1,topic://topic2").start();
消费者代码如下
new ConsumerThread("tcp://localhost:61616", "topic1,topic2").start();
通过打印出现的效果我们可以看到它同时消费掉了这两个topic。
通过以上的代码我们也可以了解到使用原生的代码是没有办法同时进行queue和topic的消费的,还好的是spring为我们提供了这种消费方式
1.先定义MQ的实现
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import javax.jms.ConnectionFactory;
/**
* @author liaoyubo
* @version 1.0
* @date 2019/12/12
* @description
*/
@Configuration
public class ActiveMQConfig {
@Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
@Bean
public DefaultJmsListenerContainerFactory topicFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
// factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
// factory.setSessionTransacted(true);
// 持久订阅设置
// 设置为topic方式目标
factory.setPubSubDomain(true);
// 设置客户端id (区分到客户端级别)
//factory.setClientId("client-2");
// 设置为持久订阅
//factory.setSubscriptionDurable(true);
return factory;
}
@Bean(name = "topicJmsTemplate")
public JmsTemplate topicJmsTemplate(ConnectionFactory connectionFactory,
MessageConverter jacksonJmsMessageConverter){
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
// 开启为topic方式
jmsTemplate.setPubSubDomain(true);
// 消息的转换方式
jmsTemplate.setMessageConverter(jacksonJmsMessageConverter);
return jmsTemplate;
}
@Bean
@Primary
public JmsTemplate queueJmsTemplate(ConnectionFactory connectionFactory,
MessageConverter jacksonJmsMessageConverter){
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
// 消息的转换方式
jmsTemplate.setMessageConverter(jacksonJmsMessageConverter);
return jmsTemplate;
}
}
2.按照restful的方式定义一个接口
@GetMapping("/testMQ")
public BaseResponse testMQ(){
BaseResponse baseResponse = new BaseResponse();
baseResponse.setCode(ResponseCode.SUCCESS);
baseResponse.setMsg("删除订单成功");
try {
OrderDeleteDTO orderDeleteDTO = new OrderDeleteDTO();
orderDeleteDTO.setOrderId("1");
orderDeleteDTO.setUserType("01");
jmsTemplate.convertAndSend("helloQueue1", orderDeleteDTO,message -> {
message.setStringProperty("queueName","helloQueue1");
return message;
});
Thread.sleep(5000);
jmsTemplate.convertAndSend("helloQueue2", orderDeleteDTO,message -> {
message.setStringProperty("queueName","helloQueue2");
return message;
});
Thread.sleep(5000);
topicJmsTemplate.convertAndSend("helloTopic2",orderDeleteDTO,message -> {
message.setStringProperty("topicName","helloTopic2");
message.setStringProperty("queueName","helloTopic2");
return message;
});
} catch (Exception e) {
log.error("删除订单错误:{}", e);
baseResponse.setCode(ResponseCode.SERVER_ERROR);
baseResponse.setMsg("服务器错误");
}
return baseResponse;
}
3.消费者类的实现
/**
* 同时订阅多个主题
*
* @param orderDeleteDTO
* @param queueName
*/
@JmsListeners(value = {@JmsListener(destination = "helloQueue1"),
@JmsListener(destination = "helloQueue2"),
@JmsListener(destination = "helloTopic2",
containerFactory = "topicFactory")})
public void jmsManyQueueTest(OrderDeleteDTO orderDeleteDTO,
@Header(value = "queueName") String queueName) {
System.out.println(orderDeleteDTO + ":" + queueName);
}
根据restful的方式调用接口后就会得到我们同时消费queue和topic的打印信息。