一、关于rabbitmqAdmin
(1)、 简单来说,就算把我们之前用rabbitmq原生API对mq交换器、队列、绑定、消息等做的一系列操作,做了一下封装。
(2)、具体操作
声明rabbitAdmin
//声明连接工厂
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("127.0.0.1:5672");
connectionFactory.setUsername("zhangcheng");
connectionFactory.setPassword("zhangcheng");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}
//声明rabbitAdmin
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
调用rabbitAdmin的API,做具体操作:
//声明交换器
rabbitAdmin.declareExchange(new DirectExchange("test.direct", false, false));
rabbitAdmin.declareExchange(new TopicExchange("test.topic", false, false));
rabbitAdmin.declareExchange(new FanoutExchange("test.fanout", false, false));
//声明队列
rabbitAdmin.declareQueue(new Queue("test.direct.queue", false));
rabbitAdmin.declareQueue(new Queue("test.topic.queue", false));
rabbitAdmin.declareQueue(new Queue("test.fanout.queue", false));
// 第一个参数:具体的队列
// 第二个参数:绑定的类型
// 第三个参数:交换机
// 第四个参数:路由key
// 第五个参数:arguments 参数
rabbitAdmin.declareBinding(new Binding("test.direct.queue",
Binding.DestinationType.QUEUE,
"test.direct", "direct", new HashMap<>()));
//BindingBuilder 链式编程
rabbitAdmin.declareBinding(
BindingBuilder
.bind(new Queue("test.topic.queue", false)) //直接创建队列
.to(new TopicExchange("test.topic", false, false)) //直接创建交换机 建立关联关系
.with("user.#")); //指定路由Key
rabbitAdmin.declareBinding(
BindingBuilder
.bind(new Queue("test.fanout.queue", false))
.to(new FanoutExchange("test.fanout", false, false)));
//清空队列数据
rabbitAdmin.purgeQueue("test.topic.queue", false);
二、关于Spring AMQP声明
使用SpringAMQP声明,即声明@Bean方式,做一些关于rabbitmq的操作:
(1)、声明连接工厂、创建交换器、队列、和绑定队列
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("127.0.0.1:5672");
connectionFactory.setUsername("zhangcheng");
connectionFactory.setPassword("zhangcheng");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}
//声明一个交换器(名称:topic001)
@Bean
public TopicExchange exchange001() {
return new TopicExchange("topic001", true, false);
}
//声明一个队列(名称:queue001)
@Bean
public Queue queue001() {
return new Queue("queue001", true); //队列持久
}
//声明一个绑定,queue001队列绑定到topic001交换器上,路由规则是spring.*
@Bean
public Binding binding001() {
return BindingBuilder.bind(queue001()).to(exchange001()).with("spring.*");
}
//声明一个队列(名称:queue002)
@Bean
public Queue queue002() {
return new Queue("queue002", true); //队列持久
}
//声明一个绑定,queue002队列绑定到topic001交换器上,路由规则是mq.*
@Bean
public Binding binding002() {
//同一个Exchange绑定了2个队列
return BindingBuilder.bind(queue002()).to(exchange001()).with("mq.*");
}
//声明一个交换器(名称:topic002)
@Bean
public TopicExchange exchange002() {
return new TopicExchange("topic002", true, false);
}
//声明一个队列(名称:queue003)
@Bean
public Queue queue003() {
return new Queue("queue003", true); //队列持久
}
//声明一个绑定,queue003队列绑定到topic002交换器上,路由规则是rabbit.*
@Bean
public Binding binding003() {
return BindingBuilder.bind(queue003()).to(exchange002()).with("rabbit.*");
}
(2)、Spring AMQP 提供了 RabbitTemplate 来简化 RabbitMQ 发送和接收消息操作:
声明RabbitTemplate:
//声明RabbitTemplate
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
调用RabbitTemplate API发送和接收消息
@Test
public void testSendMessage2() throws Exception {
//1 创建消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("text/plain");
Message message = new Message("mq 消息1234".getBytes(), messageProperties);
//send只能发送Message对象
rabbitTemplate.send("topic001", "spring.abc", message);
//convertAndSend转换后再发送,可以发送object
rabbitTemplate.convertAndSend("topic001", "mq.abc", "hello object message send!");
rabbitTemplate.convertAndSend("topic002", "rabbit.abc", "hello object message send!");
}
(3)、消费监听容器
实际上就是消费者的容器,可以设置一些消费者属性,以及指定监听到消息之后的消费方法:
//声明消费监听容器,实际上就是用来设置消费者属性,并且消费消息的
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
//创建容器
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
//添加多个队列进行监听
container.setQueues(queue001(), queue002(), queue003());
//设置多个并发消费者一起消费,并支持运行时动态修改。
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//设置重回队列,一般设置false
container.setDefaultRequeueRejected(false);
//设置自动签收机制
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//设置listener外露
container.setExposeListenerChannel(true);
//消费端标签生成策略(生成每个消费者的标签)
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
//每个消费端都有自己独立的标签
return queue + "_" + UUID.randomUUID().toString();
}
});
//消息监听(消息来了,做什么操作,实际上就算消费消息)
container.setMessageListener(new ChannelAwareMessageListener() {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
String msg = new String(message.getBody());
System.err.println("----------消费者: " + message.getMessageProperties().getConsumerTag() + ",消费消息:" + msg);
}
});
}
(4)、消费监听容器适配器:
//适配器模式(第一种方式,不细分到队列)
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
container.setMessageListener(adapter);
//指定消费方法
adapter.setDefaultListenerMethod("consumeMessage");
//指定消息转换器
adapter.setMessageConverter(new TextMessageConverter());
//给容器设置监听(传入适配器对象)
container.setMessageListener(adapter);
//2 适配器方式: 我们的队列名称 和 方法名称 也可以进行一一的匹配
//声明一个适配器
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
//设置转换器
adapter.setMessageConverter(new TextMessageConverter());
//声明队列和消费方法对应的Map
Map<String, String> queueOrTagToMethodName = new HashMap<>();
queueOrTagToMethodName.put("queue001", "method1");
queueOrTagToMethodName.put("queue002", "method2");
//队列名称 和 方法名称对应,就是说,对应的队列消息被监听到了,会被不同的方法消费
adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
//给容器设置监听(传入适配器对象)
container.setMessageListener(adapter);
public class MessageDelegate {
//普通消费方法,不分队列
public void consumeMessage(byte[] messageBody) {
System.err.println("默认方法, 消息内容:" + new String(messageBody));
}
//队列为queue001的消息被监听到了,执行此方法
public void method1(String messageBody) {
System.err.println("method1 收到消息内容:" + new String(messageBody));
}
//队列为queue002的消息被监听到了,执行此方法
public void method2(String messageBody) {
System.err.println("method2 收到消息内容:" + new String(messageBody));
}
}
无非也就是指定一个类,声明一些方法,通过适配模式,将消息监听容器的某些属性和这个适配类的方法联系起来,比如adapter.setDefaultListenerMethod(“consumeMessage”),就是监听到消息之后,就执行适配类MessageDelegate的consumeMessage方法。
三、关于转换器:
我们工作中各服务之间大多数数据都是以JSON类型的数据二进制化之后进行传输的,即生产者服务将JSON类型的数据传递到对应的队列,而消费端处理器中接收到的数据类型也是JSON类型。
这时候,如果要将这些二进制的数据,转换为java对象,一个个的手动转换,太麻烦了,所以我们需要转换器
(1)、自定义转换器:String和Message类型互转
public class TextMessageConverter implements MessageConverter {
//Object转换为msg
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
return new Message(object.toString().getBytes(), messageProperties);
}
//msg对象转换为Object
@Override
public Object fromMessage(Message message) throws MessageConversionException {
String contentType = message.getMessageProperties().getContentType();
if(null != contentType && contentType.contains("text")) {
return new String(message.getBody());
}
return message.getBody();
}
}
@Test
public void testSendMessage2() throws Exception {
//1 创建消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("text/plain");
Message message = new Message("mq 消息1234".getBytes(), messageProperties);
//send只能发送Message对象
rabbitTemplate.send("topic001", "spring.abc", message);
//convertAndSend转换后再发送,可以发送object
rabbitTemplate.convertAndSend("topic001", "mq.abc", "hello object message send!");
rabbitTemplate.convertAndSend("topic002", "rabbit.abc", "hello object message send!");
}
(2)、二进制消息自动转化为java对象(map或者list):Jackson2JsonMessageConverter转换器
@Bean //二进制消息自动转化为java对象(map或者list)
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003()); //监听的队列
container.setConcurrentConsumers(1); //当前的消费者数量
container.setMaxConcurrentConsumers(5); // 最大的消费者数量
container.setDefaultRequeueRejected(false); //是否重回队列
container.setAcknowledgeMode(AcknowledgeMode.AUTO); //签收模式
container.setExposeListenerChannel(true);
container.setConsumerTagStrategy(new ConsumerTagStrategy() { //消费端的标签策略
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID().toString();
}
});
//3 支持json格式的转换器
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMapMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
return container;
}
@Test
public void testSendJsonMessage() throws Exception {
Order order = new Order();
order.setId("001");
order.setName("test1001消息订单");
order.setContent("test1001订单描述信息");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(order);
System.out.println("order 4 json: " + json);
MessageProperties messageProperties = new MessageProperties();
//这里注意一定要修改contentType为 application/json
messageProperties.setContentType("application/json");
Message message = new Message(json.getBytes(), messageProperties);
rabbitTemplate.send("topic001", "spring.order", message);
}
总结:
1、使用Jackson2JsonMessageConverter处理器,生产者发送JSON类型数据,但是没有指定消息的contentType类型,那么Jackson2JsonMessageConverter就会将消息转换成byte[]类型的消息进行消费。
2、如果指定了contentType为application/json,那么消费端就会将消息转换成Map类型的消息进行消费。
3、如果指定了contentType为application/json,并且生产端是List类型的JSON格式,那么消费端就会将消息转换成List类型的消息进行消费。
(3)、DefaultJackson2JavaTypeMapper映射器:
上面我们已经做好了生产者发过来的二进制消息,转换为map或者list,但是这样转换没有多大意义,我们需要消费者将生产者的消息对象格式转换成对应的消息格式,而不是Map或者List对象。
@Bean //二进制消息自动转化为java对象(由生产者决定转换为具体的哪个java对象)
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003()); //监听的队列
container.setConcurrentConsumers(1); //当前的消费者数量
container.setMaxConcurrentConsumers(5); // 最大的消费者数量
container.setDefaultRequeueRejected(false); //是否重回队列
container.setAcknowledgeMode(AcknowledgeMode.AUTO); //签收模式
container.setExposeListenerChannel(true);
container.setConsumerTagStrategy(new ConsumerTagStrategy() { //消费端的标签策略
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID().toString();
}
});
// 4 DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter 支持java对象转换
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
//指定消费方法
adapter.setDefaultListenerMethod("consumeJavaTypeMapperMessage");
//声明转换器
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
//声明对象映射器
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
//设置具体的映射关系
Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
idClassMapping.put("order", Order.class);
idClassMapping.put("packaged", Packaged.class);
javaTypeMapper.setIdClassMapping(idClassMapping);
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
return container;
}
@Test
public void testSendMappingMessage() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Order order = new Order();
order.setId("1001");
order.setName("1001订单消息");
order.setContent("1001订单描述信息");
String json1 = mapper.writeValueAsString(order);
System.out.println("order java: " + json1);
MessageProperties messageProperties1 = new MessageProperties();
//这里注意一定要修改contentType为 application/json
messageProperties1.setContentType("application/json");
messageProperties1.getHeaders().put("__TypeId__", "order");
Message message1 = new Message(json1.getBytes(), messageProperties1);
rabbitTemplate.send("topic001", "spring.order", message1);
Packaged pack = new Packaged();
pack.setId("1002");
pack.setName("1002包裹消息");
pack.setDescription("1002包裹描述信息");
String json2 = mapper.writeValueAsString(pack);
System.out.println("pack java: " + json2);
MessageProperties messageProperties2 = new MessageProperties();
//这里注意一定要修改contentType为 application/json
messageProperties2.setContentType("application/json");
messageProperties2.getHeaders().put("__TypeId__", "packaged");
Message message2 = new Message(json2.getBytes(), messageProperties2);
rabbitTemplate.send("topic001", "spring.pack", message2);
}
总结:
生产者在发送消息时,通过设置消息参数"TypeId",设置一个key,然后在消费端:
//设置具体的映射关系
Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
idClassMapping.put("order", Order.class);
idClassMapping.put("packaged", Packaged.class);
javaTypeMapper.setIdClassMapping(idClassMapping);
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
这样就建立了映射关系,生产者发生过来的消息,通过"TypeId"参数,自动转换为对应的对象类型。
(4)、如果消息类型是List或者Map类型的时候:
public static void sendOrderList(RabbitTemplate rabbitTemplate) throws Exception{
Order order = new Order();
order.setId(1);
order.setUserId(1000);
order.setAmout(88d);
order.setTime(LocalDateTime.now().toString());
Order order2 = new Order();
order2.setId(2);
order2.setUserId(2000);
order2.setAmout(99d);
order2.setTime(LocalDateTime.now().toString());
List<Order> orderList = Arrays.asList(order,order2);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(orderList);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/json");
messageProperties.getHeaders().put("__TypeId__","java.util.List");
messageProperties.getHeaders().put("__ContentTypeId__","order");
Message message = new Message(json.getBytes(),messageProperties);
rabbitTemplate.send("","zhihao.miao.order",message);
}
public static void sendOrderMap(RabbitTemplate rabbitTemplate) throws Exception{
Order order = new Order();
order.setId(1);
order.setUserId(1000);
order.setAmout(88d);
order.setTime(LocalDateTime.now().toString());
Order order2 = new Order();
order2.setId(2);
order2.setUserId(2000);
order2.setAmout(99d);
order2.setTime(LocalDateTime.now().toString());
Map<String,Object> orderMaps = new HashMap<>();
orderMaps.put("10",order);
orderMaps.put("20",order2);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(orderMaps);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/json");
messageProperties.getHeaders().put("__TypeId__","java.util.Map");
messageProperties.getHeaders().put("__KeyTypeId__","java.lang.String");
messageProperties.getHeaders().put("__ContentTypeId__","order");
Message message = new Message(json.getBytes(),messageProperties);
rabbitTemplate.send("","zhihao.miao.order",message);
}
public void onMessage(List<Order> orders){
System.out.println("---------onMessage---List<Order>-------------");
orders.stream().forEach(order -> System.out.println(order));
}
public void onMessage(Map<String,Object> orderMaps){
System.out.println("-------onMessage---Map<String,Object>------------");
orderMaps.keySet().forEach(key -> System.out.println(orderMaps.get(key)));
}
总结:
1、如果生产者发送的是list的json数据,则还需要增加一个__ContentTypeId__的header,用于指明List里面的具体对象。
2、如果生产者发送的是map的json数据,则需要指定__KeyTypeId__,__ContentTypeId__的header,用于指明map里面的key,value的具体对象。
(5)、全局转换器:
设置全局消息转换器,其实就算前面几种转换器,集中起来,设一定的规则,消息参数属于那种类型,就执行那个转换器
//设置全局消息转换器,其实就算前面几种转换器,集中起来,设一定的规则,消息参数属于那种类型,就执行那个转换器
@Bean
public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003()); //监听的队列
container.setConcurrentConsumers(1); //当前的消费者数量
container.setMaxConcurrentConsumers(5); // 最大的消费者数量
container.setDefaultRequeueRejected(false); //是否重回队列
container.setAcknowledgeMode(AcknowledgeMode.AUTO); //签收模式
container.setExposeListenerChannel(true);
container.setConsumerTagStrategy(new ConsumerTagStrategy() { //消费端的标签策略
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID().toString();
}
});
// 创建适配器
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
//声明Json转换器
Jackson2JsonMessageConverter jackson2JsonMessageConverter =new Jackson2JsonMessageConverter();
//声明映射规则
Map<String, Class<?>> idClassMapping = new HashMap<>();
idClassMapping.put("order",Order.class);
idClassMapping.put("packaged",Packaged.class);
//创建映射器
DefaultJackson2JavaTypeMapper jackson2JavaTypeMapper = new DefaultJackson2JavaTypeMapper();
//创建映射规则
jackson2JavaTypeMapper.setIdClassMapping(idClassMapping);
//给转换器设置映射器
jackson2JsonMessageConverter.setJavaTypeMapper(jackson2JavaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
//声明自定义消息转换器
TextMessageConverter textMessageConverter = new TextMessageConverter();
//声明全局消息转换器,并且设置规则,消息参数是什么类型,就执行对应的转换器转换数据
ContentTypeDelegatingMessageConverter contentTypeDelegatingMessageConverter = new ContentTypeDelegatingMessageConverter();
contentTypeDelegatingMessageConverter.addDelegate("text",textMessageConverter);
contentTypeDelegatingMessageConverter.addDelegate("html/text",textMessageConverter);
contentTypeDelegatingMessageConverter.addDelegate("xml/text",textMessageConverter);
contentTypeDelegatingMessageConverter.addDelegate("text/plain",textMessageConverter);
contentTypeDelegatingMessageConverter.addDelegate("json",jackson2JsonMessageConverter);
contentTypeDelegatingMessageConverter.addDelegate("application/json",jackson2JsonMessageConverter);
adapter.setMessageConverter(contentTypeDelegatingMessageConverter);
//设置处理器的消费消息的默认方法
adapter.setDefaultListenerMethod("onMessage");
container.setMessageListener(adapter);
return container;
}
总结:
1、ContentTypeDelegatingMessageConverter是一个代理的MessageConverter。
2、ContentTypeDelegatingMessageConverter本身不做消息转换的具体动作,而是将消息转换委托给具体的MessageConverter。我们可以设置COntentType和MessageConverter的映射关系。
3、ContentTypeDelegatingMessageConverter还有一个默认的MessageConverter,也就是说当根据ContentType没有找到映射的MessageConverter的时候,就会使用默认的MessageConverter。