RabbitMQ學習筆記

Ning1106發表於2020-12-10

一、簡介

  • AMQP(Advanced Message Queuing Protocol)高階訊息佇列協議:高階訊息佇列協議。它是應用層協議的一個開放標準,為面向訊息的中介軟體設計,基於此協議的客戶端與訊息中介軟體可傳遞訊息,並不受產品、開發語言等條件的限制。
  • 生產者把訊息釋出到Exchange上,訊息最終到達佇列並被消費者接收,而Binding決定交換器的訊息應該傳送到哪個佇列。
    在這裡插入圖片描述
  • RabbitMQ是基於AMQP實現,使用Erlang語言編寫的,是一個開源的訊息代理和佇列伺服器,用來通過普通協議在不同的應用之間共享資料(跨平臺跨語言)。

二、應用場景

  • 非同步處理
    · 場景說明:使用者註冊後需要傳送郵件
    · 傳統解決方式:將註冊資訊寫入資料庫後,再傳送郵件,傳送郵件完成後才返回客戶端,但是傳送郵件並不是必須的,僅僅是一個通知。
    在這裡插入圖片描述

    · 訊息佇列方式:註冊資訊寫入資料庫之後,寫入訊息佇列,然後直接返回客戶端,傳送郵件採用非同步處理,提高了系統響應效率。
    在這裡插入圖片描述

  • 應用解耦
    · 場景:網上商城購物,使用者下單購物
    · 傳統方式:訂單系統呼叫庫存系統介面,但是,由於訂單系統和庫存系統高耦合,當庫存系統出現故障時,訂單會失敗
    在這裡插入圖片描述

    · 訊息佇列:使用者下單後,訂單系統寫入資料庫,寫入訊息佇列,下單成功,返回客戶端;庫存系統訂閱下單訊息,獲取訂單資訊,進行處理,即使庫存系統故障,訊息佇列也能保證訊息的可靠投遞,不會導致訊息丟失
    在這裡插入圖片描述

  • 流量削峰
    一般應用在秒殺活動中,秒殺系統因為流量過大,會導致掛掉,在應用前端加入訊息佇列可解決該問題。

    • 可以控制活動人數,超過此一定閥值的訂單直接丟棄
    • 可以緩解短時間的高流量壓垮應用(應用程式按自己的最大處理能力獲取訂單)
      在這裡插入圖片描述
  • 日誌處理:解決大量日誌傳輸

  • 訊息通訊:訊息佇列一般都內建了高效的通訊機制,因為也可以用在純訊息通訊。比如,實現點對點訊息佇列,或者聊天室等

三、MQ有哪些常見問題?如何解決?

3.1 訊息順序問題

描述:訊息有序指的是可以按照訊息的傳送順序來消費。
解決方案:
(1) 保證生產者-MQServer-消費者是一對一對一的關係。
(2)通過合理的設計或將問題分解來規避,佇列無序並不意味著訊息無序,可以從業務層來保證訊息的順序,而不是依賴訊息系統。
總結:第一種解決方案存在缺陷,並行度會成為訊息系統的瓶頸,降低吞吐量,更多的異常(當消費端出現問題,會導致整個流程堵塞)

3.2 訊息重複問題

導致此問題的根本原因是:網路問題。
解決方案:消費端處理訊息的業務邏輯保持冪等性(使用者對同一操作發起的一次或多次請求的結果一致,不會因為多次點選而產生副作用),保證每條訊息都有唯一編號且保證訊息處理成功與去重表的日誌同時出現。利用一張日誌表來記錄已經處理成功的訊息的 ID,如果新到的訊息 ID 已經在日誌表中,那麼就不再處理這條訊息。

四、RabbitMQ基本概念

  • Broker: 簡單來說就是訊息佇列伺服器實體
  • Exchange: 訊息交換機,它指定訊息按什麼規則,路由到哪個佇列
  • Queue: 訊息佇列載體,每個訊息都會被投入到一個或多個佇列
  • Binding: 繫結,它的作用就是把exchange和queue按照路由規則繫結起來
  • Routing Key: 路由關鍵字,exchange根據這個關鍵字進行訊息投遞
  • VHost: vhost 可以理解為虛擬 broker ,即 mini-RabbitMQ server。其內部均含有獨立的 queue、exchange 和 binding 等,但最最重要的是,其擁有獨立的許可權系統,可以做到 vhost 範圍的使用者控制。當然,從 RabbitMQ 的全域性角度,vhost 可以作為不同許可權隔離的手段(一個典型的例子就是不同的應用可以跑在不同的 vhost 中)。
  • Producer: 訊息生產者,就是投遞訊息的程式
  • Consumer: 訊息消費者,就是接受訊息的程式
  • Channel: 訊息通道,在客戶端的每個連線裡,可建立多個channel,每個channel代表一個會話任務由Exchange、Queue、RoutingKey三個才能決定一個從Exchange到Queue的唯一的線路。

五、訊息怎麼路由

訊息提供者→路由→訊息佇列
訊息釋出到交換器時,訊息擁有一個路由鍵(routing key),在訊息建立時設定。通過佇列路由鍵可以把佇列繫結到交換器。當訊息到達交換器,RabbitMQ會將訊息的路由鍵和佇列路由鍵進行匹配(針對不同的交換器有不同的匹配規則)

常用的三種交換器:
  • direct: 點對點式/單播,按照路由鍵完全匹配的規則來繫結訊息
  • fanout:廣播模式,當交換器收到訊息時,將會廣播到所有繫結的佇列上
  • topic:可以使不同源頭的訊息能夠到達同一佇列。使用topic交換器時,可以使用萬用字元 * 或者 #,# 匹配0或多個字元,* 匹配一個字元。
    在這裡插入圖片描述

六、使用docker安裝rabbitMQ

  1. 拉取映象

docker pull rabbitmq

  1. 檢視映象

docker images
在這裡插入圖片描述

  1. 啟動

docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 -v pwd/data:/var/lib/rabbitmq --hostname myRabbit -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin 263c941f71ea

說明:

  • 263c941f71ea為rabbitMQ的IMAGE ID
  • -d 後臺執行容器;
  • –name 指定容器名
  • -p 指定服務執行的埠(5672:應用訪問埠;15672:控制檯web埠)
  • -v 對映目錄或檔案
  • –hostname 主機名(RabbitMQ的一個重要注意事項是它根據所謂的 “節點名稱” 儲存資料,預設為主機名)
  • -e 指定環境變數RABBITMQ_DEFAULT_VHOST:預設虛擬機器名;RABBITMQ_DEFAULT_USER:預設的使用者名稱;RABBITMQ_DEFAULT_PASS:預設使用者名稱的密碼)

七、SpringBoot整合RabbitMQ

  1. 引入依賴
		<!-- rabbitMQ -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
  1. 配置檔案

#配置rabbitmq
spring.rabbitmq.host=111.229.173.132
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

  1. 訊息使用json格式
    新建一個配置類
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description: rabbitMQ訊息使用json格式
 * @Auther: wn
 * @Date: 2020/12/8 15:29
 */
@Configuration
public class MyAMQPConfig {

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}
  1. 傳送、接收訊息
@SpringBootTest
class SbootdemoApplicationTests {

    @Autowired
    RabbitTemplate rabbitTemplate;

    /***
     * @Description 給rabbitmq傳送訊息
     * @Date 2:45 2020/12/8
     * @Param []
     * @return void
     */
    @Test
    public void sendMsg(){
        Map<String,Object> map = new HashMap<>();
        map.put("msg","THe first msg!");
        map.put("data", Arrays.asList("hello world",123,true));
        //rabbitTemplate.convertAndSend(exchange,routingKey,message);
        rabbitTemplate.convertAndSend("demo.direct","que",map);
    }

    /***
     * @Description 從rabbitmq接收訊息
     * @Date 2:45 2020/12/8
     * @Param []
     * @return void
     */
    @Test
    public void receiveMsg(){
    	//rabbitTemplate.receiveAndConvert(queueName);
        Object ob = rabbitTemplate.receiveAndConvert("que");
        System.out.println(ob.getClass());
        System.out.println(ob);
    }
}
  1. 監聽訊息佇列
    需要使用兩個註解:@EnableRabbit 和 @RabbitListener
    首先需要在啟動類上開啟RabbitMQ模式:
@EnableRabbit  // 開啟基於註解的RabbitMQ模式
@SpringBootApplication
public class SbootdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SbootdemoApplication.class, args);
    }

}

然後使用@RabbitListener註解對訊息佇列進行監控

@Service
public class BookService {

    @RabbitListener(queues = "que")
    public void reciveBook(Book book){
        System.out.println(book);
    }
}
  1. 使用管理類AMQPAdmin 對RabbitMQ進行管理
@SpringBootTest
class SbootdemoApplicationTests {

    @Autowired
    AmqpAdmin amqpAdmin;

    @Test
    public void demo(){
        //建立一個路由
        amqpAdmin.declareExchange(new DirectExchange("test.direct"));
        //建立一個訊息佇列
        amqpAdmin.declareQueue(new Queue("test.que"));
        //將建立的訊息佇列test.que基於路由鍵test繫結到路由test.direct上
        amqpAdmin.declareBinding(new Binding("test.que", Binding.DestinationType.QUEUE,"test.direct","test",null));
    }
}

在這裡插入圖片描述

相關文章