SpringAMQP理解与具体操作

一、关于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。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值