整合RabbitMQ&Spring

weixin_34365417發表於2018-10-19

RabbitAdmin

RabbitAdmin類可以很好的操作RabbitMQ,在spring中直接進行注入即可

@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
  RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
  rabbitAdmin.setAutoStartUp(true);
  return rabbitAdmin;
}
  • 注意: autoStartUp必須設定為true,否則Spring容器不會載入RabbitAdmin類
  • RabbitAdmin底層實現就是從Spring容器中獲取Exchange、Bingding、RoutingKey以及Queue的@Bean方式的宣告
  • 然後使用RabbitTemplate的execute方法指定對應的宣告、修改、刪除等一系列RabbitMQ基礎功能操作。
  • 例如:新增一個交換機、刪除一個繫結、清空一個佇列的訊息等等就要使用的RabbitAdmin

例項:

  1. 新增maven依賴
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    <dependency>
    <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>3.6.5</version>
    </dependency>
  1. 編寫RabbitMQConfig類
package com.pyy.spring;
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.pyy.spring.*"})
public class RabbitMQConfig {

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses("192.168.43.113:5672");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("123456");
        connectionFactory.setVirtualHost("/");
        return connectionFactory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
}
  1. 編寫測試類
@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));

        rabbitAdmin.declareBinding(new Binding("test.direct.queue",
                Binding.DestinationType.QUEUE, "test.direct", "direct", new HashMap<>()));


        rabbitAdmin.declareBinding(BindingBuilder.bind(
                new Queue("test.topic.queue1", false))  // 直接建立佇列
                .to(new TopicExchange("test.topic", false, false))// 直接建立交換機建立關係
                .with("user.#"));// 直接指定路由鍵

        rabbitAdmin.declareBinding(BindingBuilder.bind(
                new Queue("test.topic.queue1", false))  // 直接建立佇列
                .to(new FanoutExchange("test.topic", false, false)));// 直接建立交換機建立關係
    }

SpringAMQP-RabbitMQ宣告式配置使用

  • 在Rabbit基礎API裡面宣告一個Exchange、宣告一個繫結、一個佇列:
    channel.exchangeDeclare(exchangeName, exchangeType, true, false, false,null);
    channel.queueDeclare(queueName, false, false, false, null);
    channel.queueBind(queueName, exchangeName, routingkey);

  • 使用SpringAMQP的宣告,就需要使用SpringAMQP的如下模式,即宣告@Bean方式:

@Bean
public TopicExchange exchange() {
  return new TopicExchange("topicExchange", true, false);
}

@Bean
public Queue queue() {
  return new Queue("queue", true);
}

@Bean
public Binding binding() {
  return new BindingBuilder.bind(queue()).to(exchange()).with("spring.*");
}

SpringAMQP訊息模板元件-RabbitTemplate實戰

  • RabbitTemplate, 即訊息模板
    我們在與SpringAMQP整合的時候進行傳送訊息的關鍵類

  • 該類提供了豐富的傳送訊息方法,包括可靠性訊息投遞方法回撥監聽訊息介面ConfirmCallback返回值確認介面ReturnCallback等等。同樣我們需要進行注入到Spring容器中,然後直接使用。

  • 在與Spring整合時需要例項化,當時在與SpringBoot整合時,在配置檔案中新增配置即可。

配置注入:

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        //rabbitTemplate.setConfirmCallback(null);
        // rabbitTemplate.setReturnCallback(null);
        return rabbitTemplate;
    }

編寫測試類:

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSendMessage() {
        // 1 建立訊息
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.getHeaders().put("desc", "資訊描述:。。。");
        messageProperties.getHeaders().put("type", "自定義訊息型別");

        Message message = new Message("hello Rabbitmq".getBytes(), messageProperties);

        // 2 傳送訊息
        rabbitTemplate.convertAndSend("test.topic.exchange", "user.#", message, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                System.out.println("------新增額外設定--------");
                message.getMessageProperties().getHeaders().put("desc", "額外修改的資訊描述");
                message.getMessageProperties().getHeaders().put("attr", "額外新加的描述");
                return message;
            }
        });
    }

    @Test
    public void testSendMessage2() {
        // 1 建立訊息
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType("text/plain");
        
        Message message = new Message("訊息1234".getBytes(), messageProperties);

        // 2 傳送訊息
        rabbitTemplate.convertAndSend("test.topic.exchange", "user.#", message);

        rabbitTemplate.convertAndSend("test.topic.exchange", "user.#", "hello object message send");

        rabbitTemplate.convertAndSend("test.topic.exchange", "user.abc", "12234");
        
        rabbitTemplate.send("test.topic.exchange", "user.#", message);
    }

SpringAMQP訊息容器-SimpleMessageListenerContainer詳解

簡單訊息監聽容器

  • 這個類非常的強大,我們可以對他進行很多設定,對於消費者的配置項,這個類都可以滿足

  • 監聽佇列(多個佇列)、自動啟動、自動宣告功能

  • 設定事務特性、事務管理器、事務屬性、事務容量(併發)、是否開啟事務、回滾訊息等

  • 設定消費者數量、最小最大數量、批量消費配置

  • 設定訊息確認(簽收)和自動確認模式、是否重回佇列、異常捕獲handler函式

  • 設定消費者標籤生成策略、是否獨佔模式、消費者屬性等

  • 設定具體的監聽器、訊息轉換器等等。

注意:SimpleMessageListenerContainer 可以進行動態設定,比如在執行中的應用可以動態修改其消費者數量大小、接收訊息的模式等。

很多基於RabbitMQ的自定製的後端管控臺在進行動態設定的時候,也是根據這一特性實現的。所有可以看出SpringAMQP非常強大。

@Bean
    public Queue queue001() {
        Queue queue = new Queue("queue001", true);
        return queue;
    }

    @Bean
    public Queue queue002() {
        Queue queue = new Queue("queue002", true);
        return queue;
    }

    @Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002());
        container.setConcurrentConsumers(1);
        container.setMaxConcurrentConsumers(5);
        container.setDefaultRequeueRejected(false);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        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.out.println("---消費者---" + msg);
            }
        });
        return container;
    }

思考問題:SimpleMessageListenerContainer為什麼可以動態感知配置變更?

SpringAMQP訊息介面卡-MessageListenerAdapter使用

MessageListenerAdapter 即訊息監聽介面卡

MessageDelegate:

package com.pyy.spring;
public class MessageDelegate {

    /**
     * 方法名稱固定、引數型別固定
     * @param messageBody
     */
    public void hadnleMessage(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("字串方法,訊息內容:" + new String(messageBody));
    }
}

TextConverter:

package com.pyy.spring;


import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.support.converter.MessageConverter;

public class TextMessageConverter implements MessageConverter {


    @Override
    public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
        return new Message(object.toString().getBytes(), messageProperties);
    }

    @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();
    }
}

RabbitConfig:

@Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue001(), queue002());
        container.setConcurrentConsumers(1);
        container.setMaxConcurrentConsumers(5);
        container.setDefaultRequeueRejected(false);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        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.out.println("---消費者---" + msg);
//            }
//        });

        // 介面卡方式,預設方法名稱:handleMessage
        // 可以自定義方法名稱:
        // 也可以新增一個轉換器:從位元組陣列轉換為String
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        adapter.setDefaultListenerMethod("consumeMessage");// 設定預設監聽方法名稱
        adapter.setMessageConverter(new TextMessageConverter());

        container.setMessageListener(adapter);
        return container;
    }
  • 通過messgeListenerAdapter的程式碼我們可以看出如下核心屬性:
  • defaultListenerMethod預設監聽方法名稱:用於設定監聽方法名稱
  • Delegate委託物件:實際真是的委託物件,用於處理訊息

SpringAMQP訊息轉換器-MessageConverter

  • 我們在進行傳送訊息時候,正常情況下訊息體為二進位制資料方式進行傳輸,如果希望內部幫我們進行轉換,或者指定自定義的轉換器,就需要用到MessageConverter

  • 自定義常用轉換器:MessageConverter,一般來講都需要實現這個介面
    重寫下面兩個方法:
    toMessage: java物件轉換為Message
    formMessage: Message物件轉換為java物件

  • Json轉換器:Jackson2JsonMessageConverter:可以進行java物件的轉換功能

  • DefaultJackson2JavaTypeMapper對映器:可以進行java物件的對映關係

  • 自定義二進位制轉換器:比如圖片型別、PDF、PPT、流媒體

相關文章