RabbitMQ详解(五):RabbitMQ整合Spring AMQP、SpringBoot、Spring Cloud Stream

八、RabbitMQ整合篇

1、RabbitMQ整合Spring AMQP详解

1)、RabbitAdmin

  • RabbitAdmin类可以很好的操作RabbitMQ,在Spring中直接进行注入即可
  • autoStartup必须要设置为true,否则Spring容器不会加载RabbitAdmin类
  • RabbitAdmin底层实现就是从Spring容器中获取Exchange、Binding、Routingkey以及Queue的@Bean声明
  • 然后使用RabbitTemplate的execut方法执行对应的声明、修改、删除等一系列RabbitMQ基础功能操作

配置类:

import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.hand.rabbitmq")
public class RabbitMqConfig {
    //
    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses("192.168.126.151:5672");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");
        return connectionFactory;
    }

    //ConnectionFactory形参名字和connectionFactory()方法名保持一致
    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
}

测试类:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringbootRabbitmqApplication.class)
public class RabbitmqApplicationTests {
    @Autowired
    private RabbitAdmin rabbitAdmin;

    @Test
    public void testAdmin() {
        //创建交换机
        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));
        //创建绑定关系 Binding构造函数的参数 队列名称、绑定类型、交换机名称、绑定键
        rabbitAdmin.declareBinding(new Binding("test.direct.queue",
                Binding.DestinationType.QUEUE,
                "test.direct",
                "direct",
                new HashMap<>()));
        rabbitAdmin.declareBinding(
                BindingBuilder.
                        bind(new Queue("test.topic.queue", false))
                        .to(new TopicExchange("test.topic", false, false))
                        .with("user.#"));
        rabbitAdmin.declareBinding(
                BindingBuilder.
                        bind(new Queue("test.fanout.queue", false))
                        .to(new FanoutExchange("test.fanout", false, false)));
        //清空队列数据
        rabbitAdmin.purgeQueue("test.topic.queue", false);
    }

}

RabbitAdmin源码分析:

@ManagedResource(description = "Admin Tasks")
public class RabbitAdmin implements AmqpAdmin, ApplicationContextAware, ApplicationEventPublisherAware,
		BeanNameAware, InitializingBean {

RabbitAdmin实现了InitializingBean接口,在Bean加载之后做一些设置

public interface InitializingBean {

	void afterPropertiesSet() throws Exception;

}
@ManagedResource(description = "Admin Tasks")
public class RabbitAdmin implements AmqpAdmin, ApplicationContextAware, ApplicationEventPublisherAware,
		BeanNameAware, InitializingBean {
	@Override
	public void afterPropertiesSet() {

		synchronized (this.lifecycleMonitor) {
            
			//如果是running或者autoStartup为false的话就直接return
			if (this.running || !this.autoStartup) {
				return;
			}

			if (this.retryTemplate == null && !this.retryDisabled) {
				this.retryTemplate = new RetryTemplate();
				this.retryTemplate.setRetryPolicy(new SimpleRetryPolicy(DECLARE_MAX_ATTEMPTS));
				ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
				backOffPolicy.setInitialInterval(DECLARE_INITIAL_RETRY_INTERVAL);
				backOffPolicy.setMultiplier(DECLARE_RETRY_MULTIPLIER);
				backOffPolicy.setMaxInterval(DECLARE_MAX_RETRY_INTERVAL);
				this.retryTemplate.setBackOffPolicy(backOffPolicy);
			}
			if (this.connectionFactory instanceof CachingConnectionFactory &&
					((CachingConnectionFactory) this.connectionFactory).getCacheMode() == CacheMode.CONNECTION) {
				this.logger.warn("RabbitAdmin auto declaration is not supported with CacheMode.CONNECTION");
				return;
			}

			// Prevent stack overflow...
			final AtomicBoolean initializing = new AtomicBoolean(false);

			this.connectionFactory.addConnectionListener(connection -> {

				if (!initializing.compareAndSet(false, true)) {
					// If we are already initializing, we don't need to do it again...
					return;
				}
				try {
					
					if (this.retryTemplate != null) {
						this.retryTemplate.execute(c -> {
                            //初始化
							initialize();
							return null;
						});
					}
					else {
						initialize();
					}
				}
				finally {
					initializing.compareAndSet(true, false);
				}

			});

			this.running = true;

		}
	}     
            
	@Override // NOSONAR complexity
	public void initialize() {

		if (this.applicationContext == null) {
			this.logger.debug("no ApplicationContext has been set, cannot auto-declare Exchanges, Queues, and Bindings");
			return;
		}

		this.logger.debug("Initializing declarations");
        
        //声明了Exchange、Queue、Binding三个集合
		Collection<Exchange> contextExchanges = new LinkedList<Exchange>(
				this.applicationContext.getBeansOfType(Exchange.class).values());
		Collection<Queue> contextQueues = new LinkedList<Queue>(
				this.applicationContext.getBeansOfType(Queue.class).values());
		Collection<Binding> contextBindings = new LinkedList<Binding>(
				this.applicationContext.getBeansOfType(Binding.class).values());

		processLegacyCollections(contextExchanges, contextQueues, contextBindings);
		//将Bean类型是Exchange、Queue、Binding的添加到集合中
        processDeclarables(contextExchanges, contextQueues, contextBindings);

		final Collection<Exchange> exchanges = filterDeclarables(contextExchanges);
		final Collection<Queue> queues = filterDeclarables(contextQueues);
		final Collection<Binding> bindings = filterDeclarables(contextBindings);

        //将Exchange、Queue集合循环拼接成RabbitMQ能够识别的方式
		for (Exchange exchange : exchanges) {
			if ((!exchange.isDurable() || exchange.isAutoDelete())  && this.logger.isInfoEnabled()) {
				this.logger.info("Auto-declaring a non-durable or auto-delete Exchange ("
						+ exchange.getName()
						+ ") durable:" + exchange.isDurable() + ", auto-delete:" + exchange.isAutoDelete() + ". "
						+ "It will be deleted by the broker if it shuts down, and can be redeclared by closing and "
						+ "reopening the connection.");
			}
		}

		for (Queue queue : queues) {
			if ((!queue.isDurable() || queue.isAutoDelete() || queue.isExclusive()) && this.logger.isInfoEnabled()) {
				this.logger.info("Auto-declaring a non-durable, auto-delete, or exclusive Queue ("
						+ queue.getName()
						+ ") durable:" + queue.isDurable() + ", auto-delete:" + queue.isAutoDelete() + ", exclusive:"
						+ queue.isExclusive() + ". "
						+ "It will be redeclared if the broker stops and is restarted while the connection factory is "
						+ "alive, but all messages will be lost.");
			}
		}

		if (exchanges.size() == 0 && queues.size() == 0 && bindings.size() == 0) {
			this.logger.debug("Nothing to declare");
			return;
		}
        
        //调用rabbitTemplate.execute在RabbitMQ上创建Exchange、Queue、Binding
		this.rabbitTemplate.execute(channel -> {
			declareExchanges(channel, exchanges.toArray(new Exchange[exchanges.size()]));
			declareQueues(channel, queues.toArray(new Queue[queues.size()]));
			declareBindings(channel, bindings.toArray(new Binding[bindings.size()]));
			return null;
		});
		this.logger.debug("Declarations finished");

	}       

private void processDeclarables(Collection<Exchange> contextExchanges, Collection<Queue> contextQueues,
			Collection<Binding> contextBindings) {
		Collection<Declarables> declarables = this.applicationContext.getBeansOfType(Declarables.class, false, true)
				.values();
		declarables.forEach(d -> {
			d.getDeclarables().forEach(declarable -> {
				if (declarable instanceof Exchange) {
					contextExchanges.add((Exchange) declarable);
				}
				else if (declarable instanceof Queue) {
					contextQueues.add((Queue) declarable);
				}
				else if (declarable instanceof Binding) {
					contextBindings.add((Binding) declarable);
				}
			});
		});
	}            

2)、RabbitTemplate(消息模板)

  • 在与Spring AMQP整合的时候进行发送消息的关键类
  • 提供了丰富的发送消息方法,包括可靠性投递消息方法、回调监听消息接口ConfirmCallback、返回值确认接口ReturnCallback等等。同样需要注入到Spring容器中,然后直接使用

配置类:

    //ConnectionFactory形参名字和connectionFactory()方法名保持一致
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory);
        return rabbitTemplate;
    }

测试方法:

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSendMessage() {
        //创建消息
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.getHeaders().put("desc", "信息描述");
        messageProperties.getHeaders().put("type", "自定义消息类型");
        Message message = new Message("Hello RabbitMQ".getBytes(), messageProperties);
        //发送消息
        rabbitTemplate.convertAndSend("topic001", "spring.amqp", message, new MessagePostProcessor() {
            //在执行消息转换后添加/修改标题或属性然后在进行发送
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().getHeaders().put("desc", "额外修改的信息描述");
                message.getMessageProperties().getHeaders().put("attr", "额外新添加的属性");
                return message;
            }
        });
    }

RabbitMQ详解(五):RabbitMQ整合Spring AMQP、SpringBoot、Spring Cloud Stream_第1张图片

rabbitTemplate其他的方法:

    @Test
    public void testSendMessage2() {
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType("text/plain");
        Message message = new Message("mq消息1".getBytes(), messageProperties);
        rabbitTemplate.send("topic001", "spring.amqp", message);

        rabbitTemplate.convertAndSend("topic001", "spring.amqp", "mq消息2");
    }

3)、SimpleMessageListenerContainer(简单消息监听容器)

  • 对这个类进行设置,对于消费者的配置项,这个类都可以满足
  • 监听队列(多个队列)、自动启动、自动声明功能
  • 设置事务特性、事务管理器、事务属性、事务容量(并发)、是否开始事务、回滚消息等
  • 设置消费者数量、最大最小数量、批量消费
  • 设置消息确认和自动确认模式、是否重回队列、异常捕获handler函数
  • 设置消费者标签生成策略、是否独占模式、消费者属性等
  • 设置具体的监听器、消息转换器等等

注意:SimpleMessageListenerContainer可以进行动态设置,比如在运行中的应用可以动态的修改其消费者数量的大小、接收消息的模式等

配置类:

    //ConnectionFactory形参名字和connectionFactory()方法名保持一致
    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
        //当前消费者数量
        container.setConcurrentConsumers(1);
        //最大消费者数量
        container.setMaxConcurrentConsumers(5);
        //设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
        container.setDefaultRequeueRejected(false);
        /**
         * 设置消息接收确认模式
         * - AcknowledgeMode.NONE:不确认
         * - AcknowledgeMode.MANUAL:自动确认
         * - AcknowledgeMode.AUTO:手动确认
         */
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费端的标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID();
            }
        });
        //设置消息监听
        container.setMessageListener(new ChannelAwareMessageListener() {
            @Override
            public void onMessage(Message message, Channel channel) throws Exception {
                String msg = new String(message.getBody());
                System.out.println("消费者:" + msg);
            }
        });
        return container;
    }

RabbitMQ详解(五):RabbitMQ整合Spring AMQP、SpringBoot、Spring Cloud Stream_第2张图片

4)、MessageListenerAdapter(消息监听适配器)

1)适配器方式一:自定义方法名称和参数类型

配置类:

    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
        //当前消费者数量
        container.setConcurrentConsumers(1);
        //最大消费者数量
        container.setMaxConcurrentConsumers(5);
        //设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
        container.setDefaultRequeueRejected(false);
        /**
         * 设置消息接收确认模式
         * - AcknowledgeMode.NONE:不确认
         * - AcknowledgeMode.MANUAL:自动确认
         * - AcknowledgeMode.AUTO:手动确认
         */
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费端的标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID();
            }
        });
        //适配器方式
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        //MessageListenerAdapter自定义方法名
        adapter.setDefaultListenerMethod("consumeMessage");
        //添加转换器:从字节数组转换为String,到MessageDelegate的时候调用参数为String类型的方法
        adapter.setMessageConverter(new TextMessageConverter());
        container.setMessageListener(adapter);
        return container;
    }
public class MessageDelegate {
    public void handleMessage(byte[] messageBody) {
        System.out.println("默认方法, 消息内容:" + new String(messageBody));
    }

    public void consumeMessage(byte[] messageBody) {
        System.out.println("字节数组方法, 消息内容:" + new String(messageBody));
    }

    public void consumeMessage(String messageBody) {
        System.out.println("字符串方法, 消息内容:" + messageBody);
    }
}
public class TextMessageConverter implements MessageConverter {
    //Java对象转换成Message对象的方式
    @Override
    public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
        return new Message(object.toString().getBytes(), messageProperties);
    }

    //Message对象转换成Java对象的方式
    @Override
    public Object fromMessage(Message message) throws MessageConversionException {
        String contentType = message.getMessageProperties().getContentType();
        if (contentType != null && contentType.contains("text")) {
            return new String(message.getBody());
        }
        return null;
    }
}

2)适配器方式二:队列的名称和方法名称也可以进行一一的匹配

配置类:

    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
        //当前消费者数量
        container.setConcurrentConsumers(1);
        //最大消费者数量
        container.setMaxConcurrentConsumers(5);
        //设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
        container.setDefaultRequeueRejected(false);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费端的标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID();
            }
        });
        //适配器方式:队列的名称和方法名称也可以进行一一的匹配
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        Map<String, String> queueOrTagToMethodName = new HashMap<>();
        queueOrTagToMethodName.put("queue001","method1");
        queueOrTagToMethodName.put("queue002","method2");
        adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
        adapter.setMessageConverter(new TextMessageConverter());
        container.setMessageListener(adapter);
        return container;
    }

MessageListenerAdapter源码分析:

public class MessageListenerAdapter extends AbstractAdaptableMessageListener {

    //将队列名和方法名进行匹配,将指定队列适配到指定方法里
	private final Map<String, String> queueOrTagToMethodName = new HashMap<String, String>();

	public static final String ORIGINAL_DEFAULT_LISTENER_METHOD = "handleMessage";

	//new MessageListenerAdapter(new MessageDelegate())时传入的委托对象,用于处理消息
	private Object delegate;

    //默认的监听方法的名字为handleMessage,如果要自定义MessageListenerAdapter方法名默认为handleMessage
	private String defaultListenerMethod = ORIGINAL_DEFAULT_LISTENER_METHOD;    

5)、MessageConverter(消息转换器)

在进行发送消息的时候,正常情况下消息体为二进制的数据方式进行传输,如果希望内部帮我们进行转换,或者指定自定义的转换器,就需要用到MessageConverter

自定义消息转换器,需要实现MessageConverter接口,重写toMessage(Java对象转换成Message对象的方式)和fromMessage(Message对象转换成Java对象的方式)方法

  • Json转换器:Jackson2JsonMessageConverter可以进行Java对象的转换功能
  • DefaultJackson2JavaTypeMapper映射器:可以进行Java对象的映射关系
  • 自定义二进制转换器:比如图片类型、PDF、PPT、流媒体

1)Jackson2JsonMessageConverter

配置类:

    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
        //当前消费者数量
        container.setConcurrentConsumers(1);
        //最大消费者数量
        container.setMaxConcurrentConsumers(5);
        //设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
        container.setDefaultRequeueRejected(false);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费端的标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID();
            }
        });
        //支持json格式的转换器
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        adapter.setDefaultListenerMethod("consumeMessage");
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        adapter.setMessageConverter(jackson2JsonMessageConverter);
        container.setMessageListener(adapter);
        return container;
    }

对应MessageDelegate中的方法:

    public void consumeMessage(Map messageBody) {
        System.out.println("map方法, 消息内容:" + messageBody);
    }

测试方法:

    @Test
    public void testSendJsonMessage() throws Exception {
        Order order = new Order("001","消息订单","描述信息");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(order);
        MessageProperties messageProperties = new MessageProperties();
        //这里一定要修改contentType为application/json
        messageProperties.setContentType("application/json");
        Message message = new Message(json.getBytes(), messageProperties);
        rabbitTemplate.send("topic001", "spring.order", message);
    }

2)Jackson2JsonMessageConverter & DefaultJackson2JavaTypeMapper支持Java对象的转换

配置类:

    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
        //当前消费者数量
        container.setConcurrentConsumers(1);
        //最大消费者数量
        container.setMaxConcurrentConsumers(5);
        //设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
        container.setDefaultRequeueRejected(false);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费端的标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID();
            }
        });
        //Jackson2JsonMessageConverter & DefaultJackson2JavaTypeMapper支持Java对象的转换
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        adapter.setDefaultListenerMethod("consumeMessage");
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
        //否则会抛出异常The class '...' is not in the trusted packages: [java.util, java.lang]. If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all (*).
        //默认只支持java.util和java.lang包下的类
        javaTypeMapper.setTrustedPackages("*");
        jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
        adapter.setMessageConverter(jackson2JsonMessageConverter);
        container.setMessageListener(adapter);
        return container;
    }

对应MessageDelegate中的方法:

    public void consumeMessage(Order order) {
        System.out.println("order对象, 消息内容, id: " + order.getId() +
                ", name: " + order.getName() +
                ", content: " + order.getContent());
    }

测试方法:

    @Test
    public void testSendJavaMessage() throws Exception {
        Order order = new Order("001","消息订单","描述信息");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(order);
        MessageProperties messageProperties = new MessageProperties();
        //这里一定要修改contentType为application/json
        messageProperties.setContentType("application/json");
        //__TypeId__指定类的全路径
        messageProperties.getHeaders().put("__TypeId__", "com.hand.rabbitmq.domain.Order");
        Message message = new Message(json.getBytes(), messageProperties);
        rabbitTemplate.send("topic001", "spring.order", message);
    }

3)DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter支持java对象多映射转换

配置类:

    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
        //当前消费者数量
        container.setConcurrentConsumers(1);
        //最大消费者数量
        container.setMaxConcurrentConsumers(5);
        //设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
        container.setDefaultRequeueRejected(false);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费端的标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID();
            }
        });
        //DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter支持java对象多映射转换
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        adapter.setDefaultListenerMethod("consumeMessage");
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
        Map<String, Class<?>> idClassMapping = new HashMap<>();
        idClassMapping.put("order", Order.class);
        idClassMapping.put("packaged", Packaged.class);
        javaTypeMapper.setIdClassMapping(idClassMapping);
        jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
        adapter.setMessageConverter(jackson2JsonMessageConverter);
        container.setMessageListener(adapter);
        return container;
    }

对应MessageDelegate中的方法:

    public void consumeMessage(Order order) {
        System.out.println("order对象, 消息内容, id: " + order.getId() +
                ", name: " + order.getName() +
                ", content: " + order.getContent());
    }

    public void consumeMessage(Packaged pack) {
        System.out.println("package对象, 消息内容, id: " + pack.getId() +
                ", name: " + pack.getName() +
                ", content: " + pack.getDescription());
    }

测试方法:

    @Test
    public void testSendMappingMessage() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        Order order = new Order("001","消息订单","描述信息");
        String json1 = mapper.writeValueAsString(order);
        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("002","包裹消息","包裹描述信息");
        String json2 = mapper.writeValueAsString(pack);
        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);
    }

4)ContentTypeDelegatingMessageConverter全局转换器

配置类:

    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
        //当前消费者数量
        container.setConcurrentConsumers(1);
        //最大消费者数量
        container.setMaxConcurrentConsumers(5);
        //设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
        container.setDefaultRequeueRejected(false);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //消费端的标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID();
            }
        });
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        adapter.setDefaultListenerMethod("consumeMessage");
        //全局的转换器
        ContentTypeDelegatingMessageConverter convert = new ContentTypeDelegatingMessageConverter();
        TextMessageConverter textConvert = new TextMessageConverter();
        convert.addDelegate("text", textConvert);
        convert.addDelegate("html/text", textConvert);
        convert.addDelegate("xml/text", textConvert);
        convert.addDelegate("text/plain", textConvert);
        Jackson2JsonMessageConverter jsonConvert = new Jackson2JsonMessageConverter();
        convert.addDelegate("json", jsonConvert);
        convert.addDelegate("application/json", jsonConvert);
        ImageMessageConverter imageConverter = new ImageMessageConverter();
        convert.addDelegate("image/png", imageConverter);
        convert.addDelegate("image", imageConverter);
        PDFMessageConverter pdfConverter = new PDFMessageConverter();
        convert.addDelegate("application/pdf", pdfConverter);
        adapter.setMessageConverter(convert);
        container.setMessageListener(adapter);
        return container;
    }

2、RabbitMQ整合SpringBoot详解

1)生产端整合

核心配置:

spring.rabbitmq.addresses=192.168.126.151:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.connection-timeout=15000
#开启Publisher Confirm机制
spring.rabbitmq.publisher-confirms=true
#开启Publisher Return机制
spring.rabbitmq.publisher-returns=true
#启用强制消息,设置为false收不到Publisher Return机制返回的消息
spring.rabbitmq.template.mandatory=false
  • publisher-confirms,实现一个监听器用于监听Broker端给我们返回的确认消息:RabbitTemplate.ConfirmCallback
  • publisher-returns,保证消息对Broker端是可达的,如果出现路由键不可达的情况,则使用监听器对不可达的消息进行后续的处理,保证消息的路由成功:RabbitTemplate.ReturnCallback
  • 在发送消息的时候对template进行配置mandatory=true保证监听有效
  • 生产端还可以配置其他属性,比如发送重试、超时时间、次数、间隔等
import com.hand.rabbitmq.domain.Order;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.UUID;

@Component
public class RabbitSender {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    final RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            System.out.println("correlationData:" + correlationData);
            System.out.println("ack:" + ack);
            if (!ack) {
                System.out.println("异常处理");
            }
        }
    };

    final RabbitTemplate.ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
        @Override
        public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText, String exchange, String routingKey) {
            System.out.println("return exchange:" + exchange + ",routingKey:" + routingKey + ",replyCode:" + replyCode + ",replyText:" + replyText);
        }
    };

    public void send(Object message, Map<String, Object> properties) {
        MessageHeaders mhs = new MessageHeaders(properties);
        Message msg = MessageBuilder.createMessage(message, mhs);
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        //最好是id +时间戳 全局唯一
        CorrelationData cd = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend("exchange-1", "springboot.hello", msg, cd);
    }

    //Order需要实现序列化接口
    public void sendOrder(Order order) {
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        //最好是id +时间戳 全局唯一
        CorrelationData cd = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend("exchange-2", "springboot.hello", order, cd);
    }
}

2)消费端整合

核心配置:

#确认模式为手动确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.concurrency=1
spring.rabbitmq.listener.simple.max-concurrency=5
  • 首先配置手工确认模式,用于ACK的手工确认,这样可以保证消息的可靠性送达,或者在消费端消费失败的时候可以做到重回队列、根据业务记录日志等处理
  • 可以设置消费端的监听个数和最大个数,用于控制消费端的并发情况
  • 消费端监听使用@RabbitListener注解,@RabbitListener是一个组合注解,里面可以注解配置@Queue、@QueueBinding、@Exchange直接通过这个组合注解一次性解决消费端交换机、队列、绑定、路由并且配置监听功能等

配置信息:

spring.rabbitmq.listener.order.queue.name=queue-2
spring.rabbitmq.listener.order.queue.durable=true
spring.rabbitmq.listener.order.exchange.name=exchange-2
spring.rabbitmq.listener.order.exchange.durable=true
spring.rabbitmq.listener.order.exchange.type=topic
spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions=true
spring.rabbitmq.listener.order.key=springboot.*
import com.hand.rabbitmq.domain.Order;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class RabbitReceiver {

    //如果没有可以自动创建交换机、队列、绑定、路由
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "queue-1", durable = "true"),
            exchange = @Exchange(value = "exchange-1", durable = "true", type = "topic", ignoreDeclarationExceptions = "true"),
            key = "springboot.*"
    ))
    @RabbitHandler
    public void onMessage(Message message, Channel channel) throws Exception {
        System.out.println("消费端:" + message.getPayload());
        Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
        //手工ACK
        channel.basicAck(deliveryTag, false);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "${spring.rabbitmq.listener.order.queue.name}",
                    durable = "${spring.rabbitmq.listener.order.queue.durable}"),
            exchange = @Exchange(value = "${spring.rabbitmq.listener.order.exchange.name}",
                    durable = "${spring.rabbitmq.listener.order.exchange.durable}",
                    type = "${spring.rabbitmq.listener.order.exchange.type}",
                    ignoreDeclarationExceptions = "${spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions}"),
            key = "${spring.rabbitmq.listener.order.key}"
    ))
    @RabbitHandler
    //Order需要实现序列化接口
    public void onMessage(@Payload Order order, @Headers Map<String, Object> headers, Channel channel) throws Exception {
        System.out.println("消费端:" + order);
        Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
        //手工ACK
        channel.basicAck(deliveryTag, false);
    }
}

3、RabbitMQ整合Spring Cloud Stream详解

Spring Cloud Stream核心架构图:

RabbitMQ详解(五):RabbitMQ整合Spring AMQP、SpringBoot、Spring Cloud Stream_第3张图片

Spring Cloud Stream通过定义绑定器Binder作为中间层,完美地实现了应用程序与消息中间件之间的隔离。通过向应用程序暴露统一的Channel通道,使得应用程序不需要再考虑各种不同的消息中间件的实现。当需要升级消息中间件,或是更换其他消息中间件产品时,只需要更换对应的Binder绑定器而不需要修改任何的应用逻辑

RabbitMQ详解(五):RabbitMQ整合Spring AMQP、SpringBoot、Spring Cloud Stream_第4张图片

上图中黄色的为RabbitMQ的部分,绿色的部分为Spring Cloud Stream在生产者和消费者添加了一层中间件

  • @EnableBinding:value参数指定用于定义绑定消息通道的接口,在应用启动时实现对定义消息通道的绑定
  • @Output:输出注解,用于定义发送消息接口
  • @Input:输入注解,用于定义消息的消费者接口
  • @StreamListener:用于定义监听方法的注解

使用Spring Cloud Stream不能实现可靠性的投递,会存在少量消息丢失的问题(为了兼顾Kafka)

添加依赖:

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-stream-rabbitartifactId>
            <version>2.1.0.RELEASEversion>
        dependency>

Spring Cloud Stream提供了Sink、Source和Processor三个默认实现消息通道的接口

public interface Sink {

	String INPUT = "input";

	@Input(Sink.INPUT)
	SubscribableChannel input();

}
public interface Source {

	String OUTPUT = "output";

	@Output(Source.OUTPUT)
	MessageChannel output();

}
public interface Processor extends Source, Sink {

}

1)生产端整合

server.port=8001

spring.application.name=producer
spring.cloud.stream.bindings.output_channel.destination=exchange-3
spring.cloud.stream.bindings.output_channel.group=queue-3
spring.cloud.stream.bindings.output_channel.binder=rabbit_cluster

spring.cloud.stream.binders.rabbit_cluster.type=rabbit
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.addresses=192.168.126.151:5672
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.username=guest
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.password=guest
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.virtual-host=/
public interface MqMessageSource {
    String OUTPUT_CHANNEL = "output_channel";

    @Output(MqMessageSource.OUTPUT_CHANNEL)
    MessageChannel output();
}
@EnableBinding(MqMessageSource.class)
@Service
public class RabbitmqSender {

    @Autowired
    private MqMessageSource mqMessageSource;

    /**
     * 发送消息
     */
    public String sendMessage(Object message, Map<String, Object> properties) {
        try {
            MessageHeaders mhs = new MessageHeaders(properties);
            Message msg = MessageBuilder.createMessage(message, mhs);
            boolean sendStatus = mqMessageSource.output().send(msg);
            System.out.println("发送数据:" + message + ",sendStatus: " + sendStatus);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
        return null;
    }

}

2)消费端整合

server.port=8002

spring.application.name=consumer
spring.cloud.stream.bindings.input_channel.destination=exchange-3
spring.cloud.stream.bindings.input_channel.group=queue-3
spring.cloud.stream.bindings.input_channel.binder=rabbit_cluster
spring.cloud.stream.bindings.input_channel.consumer.concurrency=1
spring.cloud.stream.rabbit.bindings.input_channel.consumer.requeue-rejected=false
spring.cloud.stream.rabbit.bindings.input_channel.consumer.acknowledge-mode=MANUAL
spring.cloud.stream.rabbit.bindings.input_channel.consumer.recovery-interval=3000
spring.cloud.stream.rabbit.bindings.input_channel.consumer.durable-subscription=true
spring.cloud.stream.rabbit.bindings.input_channel.consumer.max-concurrency=5

spring.cloud.stream.binders.rabbit_cluster.type=rabbit
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.addresses=192.168.126.151:5672
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.username=guest
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.password=guest
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.virtual-host=/
public interface MqMessageSink {
    String INPUT_CHANNEL = "input_channel";

    @Input(MqMessageSink.INPUT_CHANNEL)
    SubscribableChannel input();
}
@EnableBinding(MqMessageSink.class)
@Service
public class RabbitmqReceiver {

    @StreamListener(MqMessageSink.INPUT_CHANNEL)
    public void receiver(Message message) throws Exception {
        Channel channel = (Channel) message.getHeaders().get(AmqpHeaders.CHANNEL);
        Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
        System.out.println("Input Stream 1 接受数据:" + message);
        channel.basicAck(deliveryTag, false);
    }
}

你可能感兴趣的:(#,消息队列,RabbitMQ,Spring,AMQP,Spring,Cloud,Stream)