springboot研究五:springboot整合rabbitmq

rabbitmq是當下非常流行的消息隊列,本文主要介紹springboot中如何配置使用rabbitmq。

文中代碼基於springboot2.1.6,源代碼見文末地址。

1.爲了自己玩方便,可以用docker安裝rabbitmq,見專欄內文章

《docker安裝rabbitmq》

2.相關配置

spring.rabbitmq.host=192.168.59.128
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
#這個如果不配置,就會默認找"/"
spring.rabbitmq.virtual-host=my_vhost
#指定心跳超時,單位秒,0爲不指定;默認60s
spring.rabbitmq.requested-heartbeat=20
#是否啓用【發佈確認】
spring.rabbitmq.publisher-confirms=true
#是否啓用【發佈返回】
spring.rabbitmq.publisher-returns=true
#連接超時,單位毫秒,0表示無窮大,不超時
spring.rabbitmq.connection-timeout=10

3.pom依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

4.rabbitmq有4種exchange

Exchange type Default pre-declared names
Direct exchange (Empty string) and amq.direct
Fanout exchange amq.fanout
Topic exchange amq.topic
Headers exchange amq.match (and amq.headers in RabbitMQ)

a.direct exchange使用routing key進行消息傳輸,如下圖,routing key其實就是queue和exchange的綁定。適用於多工作者協同工作的場景。

綁定代碼如下:代碼中queue名稱和routing key名稱都是"direct"

@Configuration

public class DirectRabbitConfig {    
    @Bean
    public Queue direct() {
        return new Queue("direct");
    }

    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange("directExchange");
    }

    @Bean
    public Binding directBindingExchange(Queue direct, DirectExchange directExchange) {
        return BindingBuilder.bind(direct).to(directExchange).with("direct");
    }
}

sender如下:

@Service
public class DirectSenderService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private AmqpTemplate rabbitTemplate;

    public void sendString(String message) {
        logger.info("direct sender : " + message);
        rabbitTemplate.convertAndSend("directExchange", "direct", message);
    }

    public void sendObject(Object message) {
        String messageStr = JSONObject.toJSONString(message);
        logger.info(messageStr);
        rabbitTemplate.convertAndSend("directExchange", "direct", messageStr);
    }
}

 

receiver:

@RabbitHandler
@RabbitListener(queues = {"direct"})
public void processDirect(Message message) {
    logger.info("Receiver direct: {}", new String(message.getBody()));
}

 

b.fanout exchange就是廣播模式,把消息路有給所有的綁定隊列,可以適用於羣聊天的場景。

配置代碼如下:其中有3個隊列綁定一個fanout exchange

@Configuration
public class FanoutRabbitConfig {

    @Bean
    public Queue queueA(){
        return new Queue("fanout.a");
    }

    @Bean
    public Queue queueB(){
        return new Queue("fanout.b");
    }

    @Bean
    public Queue queueC(){
        return new Queue("fanout.c");
    }

    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanoutExchange");
    }

    @Bean
    public Binding bindingExchangeA(Queue queueA, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queueA).to(fanoutExchange);
    }

    @Bean
    public Binding bindingExchangeB(Queue queueB, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queueB).to(fanoutExchange);
    }

    @Bean
    public Binding bindingExchangeC(Queue queueC, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queueC).to(fanoutExchange);
    }
}

 

sender:

@Service
public class FanoutSenderService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private AmqpTemplate rabbitTemplate;

    public void send(String message) {
        logger.info("fanout sender : {}", message);
        rabbitTemplate.convertAndSend("fanoutExchange","", message);
    }
}

receiver:

    @RabbitHandler
    @RabbitListener(queues = {"fanout.a", "fanout.b", "fanout.c"})
    public void processFanout1(Message message) {
        logger.info("Receiver fanout: {}", new String(message.getBody()));
    }

c.topic exchange通過routing key和通配符來路由消息,適用於發佈訂閱場景。

配置代碼:

@Configuration
public class TopicRabbitConfig {

    @Bean
    public Queue queueMessage() {
        return new Queue("topic.message");
    }

    @Bean
    public Queue queueMessage2() {
        return new Queue("topic.message2");
    }

    /**
     * 將隊列綁定到Topic交換器
     * @return
     */
    @Bean
    public TopicExchange exchange() {
        return new TopicExchange("topicExchange");
    }

    /**
     * 將隊列綁定到Topic交換器
     * @param queueMessage
     * @param exchange
     * @return
     */
    @Bean
    public Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
        return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
    }

    /**
     * 將隊列綁定到Topic交換器 採用#的方式
     * @param exchange
     * @param queueMessage2
     * @return
     */
    @Bean
    Binding bindingExchangeMessage2(TopicExchange exchange, Queue queueMessage2) {
        return BindingBuilder.bind(queueMessage2).to(exchange).with("topic.#");
    }
}

sender:

@Service
public class TopicSenderService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private AmqpTemplate rabbitTemplate;

    public void send1(String message) {
        logger.info("topic sender1 : " + message);
        rabbitTemplate.convertAndSend("topicExchange", "topic.message", message);
    }

    public void send2(String message) {
        logger.info("topic sender2 : " + message);
        rabbitTemplate.convertAndSend("topicExchange", "topic.message2", message);
    }
}

接受

@RabbitHandler
    @RabbitListener(queues = {"topic.message"})
    public void processTopic(Message message) {
        logger.info("Receiver topic: {}", new String(message.getBody()));
    }

    @RabbitHandler
    @RabbitListener(queues = {"topic.message2"})
    public void processTopic2(Message message) {
        logger.info("Receiver topic2: {}", new String(message.getBody()));
}

 

d.header exchange忽略routing key參數,用header來取代

配置

@Configuration
public class HeadersRabbitConfig {

    @Bean
    public Queue headerQueue() {
        return new Queue("headerQueue");
    }

    @Bean
    public Queue headerQueue2() {
        return new Queue("headerQueue2");
    }

    @Bean
    public HeadersExchange headerExchange() {
          return new HeadersExchange("headerExchange");
    }

    @Bean
    public HeadersExchange headerExchange2() {
          return new HeadersExchange("headerExchange2");
    } 

    @Bean
    public Binding bindingExchange(Queue headerQueue, HeadersExchange headerExchange) {
        Map<String,Object> headerValues = new HashMap<>(3);
        headerValues.put("param1", "value1");
        headerValues.put("param2", "value2");
        return BindingBuilder.bind(headerQueue).to(headerExchange).whereAll(headerValues).match();
    }

    @Bean
    public Binding bindingExchange2(Queue headerQueue2, HeadersExchange headerExchange2) {
        Map<String,Object> header = new HashMap<>(3);
        header.put("param1", "value1");
        header.put("param2", "value2");
        return BindingBuilder.bind(headerQueue2).to(headerExchange2).whereAny(header).match();
    }
}

發送:

@Service
public class HeadersSenderService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private AmqpTemplate rabbitTemplate;

    public void headerSend(Map<String, Object> head, String msg){
        logger.info("header send message: "+msg);
        rabbitTemplate.convertAndSend("headerExchange", "headerQueue", getMessage(head, msg));
    }

    public void headerSend2(Map<String, Object> head, String msg){
        logger.info("header1 send message: "+msg);
        rabbitTemplate.convertAndSend("headerExchange2", "headerQueue2", getMessage(head, msg));
    }

    private Message getMessage(Map<String, Object> head, Object msg){
        MessageProperties messageProperties = new MessageProperties();
        for (Map.Entry<String, Object> entry : head.entrySet()) {
            messageProperties.setHeader(entry.getKey(), entry.getValue());
        }
        MessageConverter messageConverter = new SimpleMessageConverter();
        return messageConverter.toMessage(msg, messageProperties);
    }

}

接收:

@RabbitHandler
    @RabbitListener(queues = {"headerQueue"})
    public void processHeaders(Message message) {
        logger.info("Receiver header: {}", new String(message.getBody()));
    }

    @RabbitHandler
    @RabbitListener(queues = {"headerQueue2"})
    public void processHeaders1(Message message) {
        logger.info("Receiver header2: {}", new String(message.getBody()));
    }

5.測試,測試代碼寫在RabbitMqController中,啓動Application即可進行url測試。見源碼。

說明:

a.topic exchange,瀏覽器輸入http://localhost:8082/mq/topic後,topic.#的routing key收到了2條消息,topic.message的routing key收到了1條,可以看出通配符的作用

b.headers exchange:瀏覽器輸入http://localhost:8082/mq/headers,發送了4條消息,但是第1條沒有收到。因爲headerExchange綁定時使用了whereAll,headerExchange2綁定時使用了whereAny。

 

參考:https://www.jianshu.com/p/de0c00359771

 

源代碼github地址:

https://github.com/jinjunzhu/springboot-rabbitmq

歡迎關注個人公衆號:jinjunzhu

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章