【SpringBoot2.0系列09】SpringBoot之rabbi

neuyu發表於2021-09-09

實現

1.1 rabbitmq簡介

RabbitMQ是由Erlang語言編寫的實現了高階訊息佇列協議(AMQP)的開源訊息代理軟體(也可稱為 面向訊息的中介軟體)。支援Windows、Linux/Unix、MAC OS X作業系統和包括JAVA在內的多種程式語言。

AMQP,即Advanced Message Queuing Protocol,一個提供統一訊息服務的應用層標準高階訊息佇列協議,是應用層協議的一個開放標準,為面向訊息的中介軟體設計。基於此協議的客戶端與訊息中介軟體可傳遞訊息,並不受 客戶端/中介軟體 不同產品,不同的開發語言等條件的限制
使用rabbitmq主要三種分發模式

1.1.1 工作佇列模式(Work Queue)

避免立即做一個資源密集型任務,必須等待它完成,而是把這個任務安排到稍後再做。我們將任務封裝為訊息並將其傳送給佇列。後臺執行的工作程式將彈出任務並最終執行作業。當有多個worker同時執行時,任務將在它們之間共享。


圖片描述

image.png

1.1.2 分發模式(Fanout Exchange)

一個生產者,多個消費者,每一個消費者都有自己的一個佇列,生產者沒有將訊息直接傳送到佇列,而是傳送到了交換機,每個佇列繫結交換機,生產者傳送的訊息經過交換機,到達佇列,實現一個訊息被多個消費者獲取的目的。需要注意的是,如果將訊息傳送到一個沒有佇列繫結的exchange上面,那麼該訊息將會丟失,這是因為在rabbitMQ中exchange不具備儲存訊息的能力,只有佇列具備儲存訊息的能力。


圖片描述

image.png


圖片描述

image.png

1.1.3  萬用字元模式(Topic Exchange)

這種模式新增了一個路由鍵,生產者釋出訊息的時候新增路由鍵,消費者繫結佇列到交換機時新增鍵值,這樣就可以接收到需要接收的訊息。
符號“#”匹配一個或多個詞,符號“*”匹配不多不少一個詞


圖片描述

image.png

圖片描述

image.png

1.2、安裝rabbitmq

1.2.1 window

因為rabbitmqerlang實現,所以我們需要先下載安裝erlang,然後再下載rabbitmq

1.2.2 mac

在mac系統中可以直接使用brew安裝,它會幫我們自動安裝管理依賴。

brew update
brew install rabbitmq

這樣,我們就可以使用rabbit-server啟動Rabbit服務了。

1.2.3 centos

在centos中可以使用yum安裝

sudo yum install rabbitmq

1.3 springboot整合

首先新建一個專案名為rabbit-producer 訊息生產者工程
並且新增依賴。

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

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

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

    </dependencies>

在yml配置rabbitmq地址

# rabbitmq配置spring:    rabbitmq:      addresses: 127.0.0.1
      username: guest
      password: guest:

同理建立rabbit-consumer 訊息消費者工程

1、普通工作佇列模式

首先在rabbit-producer工程中新建RabbitConfig檔案,用於配置我們rabbitmq相關的資源
程式碼如下

package com.yukong.rabbitproducer;import org.springframework.amqp.core.*;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/**
 * @author yukong
 * @date 2018/8/22
 * @description rabbitmq配置類
 */@Configurationpublic class RabbitConfig {    /**
     * 定義佇列名
     */
    private final static String STRING = "string";    /**
     * 定義string佇列
     * @return
     */
    @Bean
    public Queue string() {        return new Queue(STRING);
    
}

定義了名為string的佇列。然後我們建立生產者RabbitProducer

package com.yukong.rabbitproducer;import org.springframework.amqp.core.AmqpTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import javax.xml.ws.Action;import java.text.SimpleDateFormat;import java.util.Date;/**
 * @author yukong
 * @date 2018/8/22
 * @description rabbit訊息生產者
 */@Componentpublic class RabbitProducer {    @Autowired
    private AmqpTemplate rabbitTemplate;    public void stringSend() {
        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        System.out.println("[string] send msg:" + dateString);  
      // 第一個引數為剛剛定義的佇列名稱
        this.rabbitTemplate.convertAndSend("string", dateString);
    }
}

這裡注入一個AmqpTemplate來發布訊息
接下來我們需要在rabbit-consumer工程配置一下消費者。
建立StringConsumer

package com.yukong.rabbitmqconsumer;import org.springframework.amqp.core.AmqpTemplate;import org.springframework.amqp.rabbit.annotation.RabbitHandler;import org.springframework.amqp.rabbit.annotation.RabbitListener;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;/**
 * @author yukong
 * @date 2018/8/22
 * @description rabbitmq消費者 @RabbitListener(queues = "simpleMsg") 監聽名simpleMsg的佇列
 */@Component@RabbitListener(queues = "string")public class StringConsumer {    @Autowired private AmqpTemplate rabbitmqTemplate;    /**
     * 訊息消費
     * @RabbitHandler 代表此方法為接受到訊息後的處理方法
     */
    @RabbitHandler
    public void recieved(String msg) {
        System.out.println("[string] recieved message:" + msg);
    }

}

每一個註解的作用程式碼裡面的註釋說的很詳細了我就不重複說了。
然後我們來測試,
首先在生產者工程新建一個測試類,用於生產訊息。
程式碼如下

package com.yukong.rabbitproducer;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublic class RabbitProducerApplicationTests {    @Autowired
    private RabbitProducer producer;    @Test
    public void testStringSend() {        for (int i = 0; i < 10; i++) {
            producer.stringSend();
        }
    }

}

首先啟動生產者工程的測試類。然後再啟動消費者工程。

圖片描述

image.png


訊息生產成功,一共十條。
啟動消費者工程。

圖片描述

image.png


消費者成功消費訊息。
2、 fanout模式
fanout屬於廣播模式,只要跟它繫結的佇列都會通知並且接受到訊息。
我們同理在RabbitConfig中配置一下fanout模式的佇列跟交換機。


//=================== fanout 模式  ====================

    @Bean
    public Queue fanoutA() {        return new Queue("fanout.a");
    }    @Bean
    public Queue fanoutB() {        return new Queue("fanout.b");
    }    @Bean
    public Queue fanoutC() {        return new Queue("fanout.c");
    }    /**
     * 定義個fanout交換器
     * @return
     */
    @Bean
    FanoutExchange fanoutExchange() {        // 定義一個名為fanoutExchange的fanout交換器
        return new FanoutExchange("fanoutExchange");
    }    /**
     * 將定義的fanoutA佇列與fanoutExchange交換機繫結
     * @return
     */
    @Bean
    public Binding bindingExchangeWithA() {        return BindingBuilder.bind(fanoutA()).to(fanoutExchange());
    }    /**
     * 將定義的fanoutB佇列與fanoutExchange交換機繫結
     * @return
     */
    @Bean
    public Binding bindingExchangeWithB() {        return BindingBuilder.bind(fanoutB()).to(fanoutExchange());
    }    /**
     * 將定義的fanoutC佇列與fanoutExchange交換機繫結
     * @return
     */
    @Bean
    public Binding bindingExchangeWithC() {        return BindingBuilder.bind(fanoutC()).to(fanoutExchange());
    }

在程式碼中我們配置了三個佇列名、一個fanout交換機,並且將這三個佇列繫結到了fanout交換器上。只要我們往這個交換機生產新的訊息,那麼這三個佇列都會收到。
接下來,我們在RabbitProducer 中新增fanout的生產方法。

public void fanoutSend() {
        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        System.out.println("[fanout] send msg:" + dateString);        // 注意 第一個引數是我們交換機的名稱 ,第二個引數是routerKey 我們不用管空著就可以,第三個是你要傳送的訊息
        this.rabbitTemplate.convertAndSend("fanoutExchange", "", dateString);
    }

同理我們需要在消費者工程新建三個消費者的類
程式碼分別如下

@Component@RabbitListener(queues = "fanout.a")public class FanoutAConsumer {    @Autowired
    private AmqpTemplate rabbitmqTemplate;    /**
     * 訊息消費
     * @RabbitHandler 代表此方法為接受到訊息後的處理方法
     */
    @RabbitHandler
    public void recieved(String msg) {
        System.out.println("[fanout.a] recieved message:" + msg);
    }
}
@Component@RabbitListener(queues = "fanout.b")public class FanoutBConsumer {    @Autowired
    private AmqpTemplate rabbitmqTemplate;    /**
     * 訊息消費
     * @RabbitHandler 代表此方法為接受到訊息後的處理方法
     */
    @RabbitHandler
    public void recieved(String msg) {
        System.out.println("[fanout.b] recieved message:" + msg);
    }
}
@Component@RabbitListener(queues = "fanout.c")public class FanoutCConsumer {    @Autowired
    private AmqpTemplate rabbitmqTemplate;    /**
     * 訊息消費
     * @RabbitHandler 代表此方法為接受到訊息後的處理方法
     */
    @RabbitHandler
    public void recieved(String msg) {
        System.out.println("[fanout.c] recieved message:" + msg);
    }
}

然後編寫一個名為testFanout()的方法啟動我們的fanout生產方法,

   @Test
    public void testFanoutSend() {
        producer.fanoutSend();
    }

圖片描述

image.png

然後重啟消費者工程


圖片描述

image.png


三個佇列的消費都成功接收到訊息。
3、topic模式,
同樣,配置topic佇列跟交換器,注意的是這裡需要多配置一個bindingKey

 //#################topic模式########################

    @Bean
    public Queue topiocA() {        return new Queue("topic.a");
    }    @Bean
    public Queue topicB() {        return new Queue("topic.b");
    }    @Bean
    public Queue topicC() {        return new Queue("topic.c");
    }    /**
     * 定義個topic交換器
     * @return
     */
    @Bean
    TopicExchange topicExchange() {        // 定義一個名為fanoutExchange的fanout交換器
        return new TopicExchange("topicExchange");
    }    /**
     * 將定義的topicA佇列與topicExchange交換機繫結
     * @return
     */
    @Bean
    public Binding bindingTopicExchangeWithA() {        return BindingBuilder.bind(topiocA()).to(topicExchange()).with("topic.msg");
    }    /**
     * 將定義的topicB佇列與topicExchange交換機繫結
     * @return
     */
    @Bean
    public Binding bindingTopicExchangeWithB() {        return BindingBuilder.bind(topicB()).to(topicExchange()).with("topic.#");
    }    /**
     * 將定義的topicC佇列與topicExchange交換機繫結
     * @return
     */
    @Bean
    public Binding bindingTopicExchangeWithC() {        return BindingBuilder.bind(topicC()).to(topicExchange()).with("topic.*.z");
    }
  • topicA的key為topic.msg 那麼他只會接收包含topic.msg的訊息

  • topicB的key為topic.#那麼他只會接收topic開頭的訊息

  • topicC的key為topic.*.Z那麼他只會接收topic.B.z這樣格式的訊息
    同理在RabbitProducer完成topic生產方法

public void topicTopic1Send() {
        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        dateString = "[topic.msg] send msg:" + dateString;
        System.out.println(dateString);        // 注意 第一個引數是我們交換機的名稱 ,第二個引數是routerKey topic.msg,第三個是你要傳送的訊息
        // 這條資訊將會被 topic.a  topic.b接收
        this.rabbitTemplate.convertAndSend("topicExchange", "topic.msg", dateString);
    }    public void topicTopic2Send() {
        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        dateString = "[topic.good.msg] send msg:" + dateString;
        System.out.println(dateString);        // 注意 第一個引數是我們交換機的名稱 ,第二個引數是routerKey ,第三個是你要傳送的訊息
        // 這條資訊將會被topic.b接收
        this.rabbitTemplate.convertAndSend("topicExchange", "topic.good.msg", dateString);
    }    public void topicTopic3Send() {
        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        dateString = "[topic.m.z] send msg:" + dateString;
        System.out.println(dateString);        // 注意 第一個引數是我們交換機的名稱 ,第二個引數是routerKey ,第三個是你要傳送的訊息
        // 這條資訊將會被topic.b、topic.b接收
        this.rabbitTemplate.convertAndSend("topicExchange", "topic.m.z", dateString);
    }

然後在消費者工程新建佇列佇列的消費類

@Component@RabbitListener(queues = "topic.a")public class TopicAConsumer {    @Autowired
    private AmqpTemplate rabbitmqTemplate;    /**
     * 訊息消費
     * @RabbitHandler 代表此方法為接受到訊息後的處理方法
     */
    @RabbitHandler
    public void recieved(String msg) {
        System.out.println("[topic.a] recieved message:" + msg);
    }
}
@Component@RabbitListener(queues = "topic.b")public class TopicBConsumer {    @Autowired
    private AmqpTemplate rabbitmqTemplate;    /**
     * 訊息消費
     * @RabbitHandler 代表此方法為接受到訊息後的處理方法
     */
    @RabbitHandler
    public void recieved(String msg) {
        System.out.println("[topic.b] recieved message:" + msg);
    }
}
@Component@RabbitListener(queues = "topic.c")public class TopicCConsumer {    @Autowired
    private AmqpTemplate rabbitmqTemplate;    /**
     * 訊息消費
     * @RabbitHandler 代表此方法為接受到訊息後的處理方法
     */
    @RabbitHandler
    public void recieved(String msg) {
        System.out.println("[topic.c] recieved message:" + msg);
    }
}

同理為topic新建測試方法

 @Test
    public void testTopic() {
        producer.topicTopic1Send();
        producer.topicTopic2Send();
        producer.topicTopic3Send();
    }

圖片描述

image.png


訊息成功發出。
啟動消費者工程,看看訊息是不是按照規則被髮送訊息


圖片描述

image.png



作者:餘空啊
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4550/viewspace-2816369/,如需轉載,請註明出處,否則將追究法律責任。

相關文章