圖文詳解:阿里寵兒【小兔】RabbitMQ的養成攻略

大家好,我是小羽

今天給大家帶來的的是關於小兔RabbitMQ的養成攻略,RabbitMQ 中的 Rabbit兔子的意思,就是形容跑的和兔子一樣快。是一款輕量級的,支援多種消息傳遞協議的高可用的消息隊列。RabbitMQ 是由 Erlang 語言編寫的,而 Erlang 語言就是一款天生適合高並發的語言。

是不是都對小兔很喜歡呢,可愛的小兔在我們工作中可是扮演者大哥大的角色,說它是阿里現在的寵兒一點不為過了。RabbitMQ 前面小兔我介紹過了,那麼MQ代表的是什麼意思呢?其實了解的都知道,Message Queue 的簡寫,用官方的話說 RabbitMQ 是一款開源的消息隊列系統。下面跟著小羽一起來看看這隻小兔是如何養成的吧。

前言

現在市場上主流的MQ有很多,比如 ActiveMQ、RabbitMQ、RocketMQ、Kafka、ZeroMQ 等。

阿里巴巴最初也是使用 ActiveMQ ,不過隨著業務的不斷發展,ActiveMQ IO 模組出現瓶頸,後來阿里巴巴通過一系列優化但是還是不能很好的解決,之後阿里巴巴把注意力放到了主流消息中間件 kafka 上面,但是 kafka 並不能滿足他們的要求,尤其是低延遲和高可靠性

所以 RocketMQ 是站在巨人的肩膀上(kafka),又對其進行了優化讓其更滿足互聯網公司的特點。

RabbitMQ 作為一款非常流行的消息中間件,其有著非常豐富的特性和優勢:高可靠性、路由靈活、集群擴張性高、高可用、支援多種協議、支援多種客戶端和有著豐富的插件系統

RocketMQ目前在阿里集團被廣泛應用於交易、充值、流計算、消息推送、日誌流式處理、binglog分發等場景。


概念

RabbitMQ 是一個由 Erlang 語言開發的 AMQP 的開源實現。

AMQP :Advanced Message Queue,高級消息隊列協議。它是應用層協議的一個開放標準,為面向消息的中間件設計,基於此協議的客戶端與消息中間件可傳遞消息,並不受產品、開發語言等條件的限制。

RabbitMQ 最初起源於金融系統,用於在分散式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。

使用場景

應用解耦

拿我們經常說的訂單系統來舉例,一般都會訂單系統調用庫存系統的介面。如下:

這種處理方案會引發很多問題,當庫存系統無法訪問,我們訂單系統的減庫存就會失敗。

當我們使用了消息隊列後,用戶下單後,訂單系統進行持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功;訂閱下單的消息,採用拉/推的方式,獲取下單資訊,庫存系統根據下單資訊,進行庫存操作。

這樣的即使下單時庫存系統出現問題,也不影響正常下單,因為下單後,訂單系統已經寫入消息隊列,其他操作就不關心了,實現訂單系統與庫存系統的應用解耦

非同步處理

拿我們的用戶註冊來舉例,用戶註冊之後需要發註冊郵件和註冊簡訊。如下:

串列模式

並行模式

當我們使用了消息隊列,就不再是必須的業務邏輯,只需進行非同步處理。如下:

流量削峰

拿我們經常提到的秒殺來舉例,想必大家都經歷過天貓淘寶的雙11吧,當這個時候,我們會在特定的那個時間,比如晚上凌晨0點的每秒需求會突然增大,如果不進行系統結構升級,是經不起這麼多的請求的,直接會系統崩潰。

當我們使用了消息隊列之後,在這個高峰期的時候,很多用戶進來,比如每秒有 5 千個請求,我們只需要將這 5 千個請求放在消息隊列里,系統每秒處理 2 千個請求,會從消息隊列里拉取出對應的數量,每秒只會處理這 2 千個請求。這樣在秒殺持續的這個時間段內,會有幾十萬或者更多的請求都放在消息隊列里。因為畢竟秒殺只是會在短暫的那一段時間,等它過去之後,每秒可能就只有幾十,幾百個請求進入消息隊列。但是系統還會按照每秒 2 千個請求的速度去處理。所以,秒殺結束,系統會把那些剩下的消息都消費掉

主要特點

可靠性(Reliability):RabbitMQ 使用一些機制來保證可靠性,如持久化、傳輸確認、發布確認

靈活的路由(Flexible Routing):在消息進入隊列之前,通過 Exchange 來路由消息的。對於典型的路由功能,RabbitMQ 已經提供了一些內置
Exchange 來實現。針對更複雜的路由功能,可以將多個 Exchange 綁定在一起,也通過插件機制實現自己的 Exchange 。

消息集群(Clustering):多個 RabbitMQ 伺服器可以組成一個集群,形成一個邏輯 Broker

高可用(Highly Available Queues):隊列可以在集群中的機器上進行鏡像,使得在部分節點出問題的情況下隊列仍然可用。

多種協議(Multi-protocol):RabbitMQ 支援多種消息隊列協議,比如 STOMP、MQTT 等等。

多語言客戶端(Many Clients):RabbitMQ 幾乎支援所有常用語言,比如 Java、.NET、Ruby 等等。

管理介面(Management UI):RabbitMQ 提供了一個易用的用戶介面,使得用戶可以監控和管理消息 Broker 的許多方面。

跟蹤機制(Tracing):如果消息異常,RabbitMQ 提供了消息跟蹤機制,使用者可以找出發生了什麼。

插件機制(Plugin System):RabbitMQ 提供了許多插件,來從多方面進行擴展,也可以編寫自己的插件。

架構模型

Message

消息,消息是不具名的,它由消息頭和消息體組成。消息體是不透明的,而消息頭則由一系列的可選屬性組成,這些屬性包括 routing-key(路由鍵)、priority(相對於其他消息的優先權)、delivery-mode(指出該消息可能需要持久性存儲)等。

Publisher

消息的生產者,也是一個向交換器發布消息的客戶端應用程式。

Exchange

交換器,用來接收生產者發送的消息並將這些消息路由給伺服器中的隊列。

Binding

綁定,用於消息隊列和交換器之間的關聯。一個綁定就是基於路由鍵將交換器和消息隊列連接起來的路由規則,所以可以將交換器理解成一個由綁定構成的路由表

Queue

隊列,是RabbitMQ的內部對象,用於存儲消息消息的容器,也是消息的終點。一個消息可投入一個或多個隊列。消息一直在隊列裡面,等待消費者連接到這個隊列將其取走。

Connection

網路連接,比如一個 TCP 連接。

Channel

信道,多路復用連接中的一條獨立的雙向數據流通道。信道是建立在真實的 TCP 連接內地虛擬連接,AMQP 命令都是通過信道發出去的,不管是發布消息、訂閱隊列還是接收消息,這些動作都是通過信道完成。因為對於作業系統來說建立和銷毀 TCP 都是非常昂貴的開銷,所以引入了信道的概念,以復用一條 TCP 連接。

Consumer

消息的消費者,表示一個從消息隊列中取得消息的客戶端應用程式

Virtual Host

虛擬主機,表示一批交換器、消息隊列和相關對象。虛擬主機是共享相同的身份認證和加密環境的獨立伺服器域

Broker

表示消息隊列伺服器實體

Exchange 類型

Exchange 分發消息時根據類型的不同分發策略有區別,目前共四種類型:direct、fanout、topic、headers 。headers 匹配 AMQP 消息的 header 而不是路由鍵,此外 headers 交換器和direct 交換器完全一致,但性能差很多,目前幾乎用不到了,所以直接看另外三種類型:

Direct 鍵分布

Direct:消息中的路由鍵(routing key)如果和 Binding 中的 binding key 一致,交換器就將消息發到對應的隊列中。它是完全匹配、單播的模式。

我們以 routingKey=」error」 發送消息到 Exchange ,則消息會路由到 Queue1(amqp.gen-S9b…,這是由RabbitMQ自動生成的Queue名稱)和 Queue2(amqp.gen-Agl…);如果我們以 routingKey=」info」或routingKey=」warning」 來發送消息,則消息只會路由到 Queue2。如果我們以其他 routingKey 發送消息,則消息不會路由到這兩個 Queue 中。

Fanout

Fanout:每個發到 fanout 類型交換器的消息都會分到所有綁定的隊列上去。很像子網廣播,每檯子網內的主機都獲得了一份複製的消息。fanout 類型轉發消息是最快的

生產者 Publisher 發送到 Exchange 的所有消息都會路由到圖中的兩個 Queue ,並最終被兩個消費者(Consumer1與Comsumer2)消費

topic 交換器

topic 交換器:topic 交換器通過模式匹配分配消息的路由鍵屬性,將路由鍵和某個模式進行匹配,此時隊列需要綁定到一個模式上。它將路由鍵和綁定鍵的字元串切分成單詞,這些單詞之間用點隔開。它同樣也會識別兩個通配符:符號 「#」 和符號 「」 。# 匹配 0 個或多個單詞,匹配不多不少一個單詞。

我們 routingKey=」quick.orange.rabbit」 的消息會同時路由Q1Q2,routingKey=」lazy.orange.fox」 的消息會路由到Q1,routingKey=」lazy.brown.fox」 的消息會路由到Q2,routingKey=」lazy.pink.rabbit」 的消息會路由到Q2(只會投遞給Q2一次,雖然這個 routingKey 與Q2的兩個 bindingKey 都匹配);routingKey=」quick.brown.fox」、routingKey=」orange」、routingKey=」quick.orange.male.rabbit」 的消息將會被丟棄,因為它們沒有匹配任何 bindingKey

安裝步驟

一般來說安裝 RabbitMQ 之前要安裝 Erlang ,可以去 Erlang 官網下載。接著去 RabbitMQ 官網下載安裝包,之後解壓縮即可。根據作業系統不同官網提供了相應的安裝說明:Windows、Debian / Ubuntu、RPM-based Linux、Mac

下載地址

https://www.rabbitmq.com/releases/rabbitmq-server/

下載

wget https://www.rabbitmq.com/releases/rabbitmq-server/v3.6.15/rabbitmq-server-generic-unix-3.6.15.tar.xz

安裝

tar -xvf rabbitmq-server-generic-unix-3.6.15.tar.xz

配置

vim /etc/profile
最後加上 export PATH=$PATH:/opt/rabbitmq/rabbitmq_server-3.6.15/sbin 根據自己的安裝路徑來

使修改生效

source /etc/profile

修改 hosts

vim/etc/hosts

啟動

cd /opt/rabbitmq/rabbitmq_server-3.6.15/sbin/
./rabbitmq-server -detached 注意需root用戶啟動

查看是否啟動成功

./rabbitmqctl status

出現這個就成功了,如果想遠程訪問

./rabbitmq-plugins enable rabbitmq_management
就可以訪問了(非本機訪問請關閉防火牆)

訪問

Springboot 項目演示

springboot 集成 RabbitMQ 非常簡單,如果只是簡單的使用配置非常少, springboot 提供了 spring-boot-starter-amqp 對消息各種支援。

配置 pom 文件,主要是添加 spring-boot-starter-amqp 的支援

程式碼演示

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

配置 application.properties 文件,配置 rabbitmq 的安裝地址、埠以及賬戶資訊

程式碼演示

spring.application.name=spirng-boot-rabbitmq
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin

配置隊列

程式碼演示

package com.zpc.rabbitmq
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration
@Configuration
public class RabbitConfig {
    @Bean
    public Queue queue() {
        return new Queue("q_hello");
    }
}

發送者

程式碼演示

package com.zpc.rabbitmq
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component
import java.text.SimpleDateFormat;
import java.util.Date
@Component
public class HelloSender {
    @Autowired
    private AmqpTemplate rabbitTemplate
    public void send() {
        String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//24小時制
        String context = "hello " + date;
        System.out.println("Sender : " + context);
        //簡單對列的情況下routingKey即為Q名
        this.rabbitTemplate.convertAndSend("q_hello", context);
    }
}

接收者

程式碼演示

package com.zpc.rabbitmq
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component
@Component
@RabbitListener(queues = "q_hello")
public class HelloReceiver 
    @RabbitHandler
    public void process(String hello) {
        System.out.println("Receiver  : " + hello);
    }
}

測試

程式碼演示

package com.zpc.rabbitmq
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)
@SpringBootTest
public class RabbitMqHelloTest 
    @Autowired
    private HelloSender helloSender
    @Test
    public void hello() throws Exception {
        helloSender.send();
    }
}

內建集群

RabbitMQ 最為出色的功能之一就是內建集群,這個功能設計的目的是允許消費者和生產者在節點崩潰的情況下繼續運行,以及通過添加更多的節點來線性擴展消息通訊吞吐量。RabbitMQ 內部利用 Erlang 提供的分散式通訊框架 OTP 來滿足上述需求,使客戶端在失去一個 RabbitMQ 節點連接的情況下,還是能夠重新連接到集群中的任何其他節點繼續生產、消費消息

RabbitMQ 集群中常用概念

隊列元數據:包括隊列名稱和它們的屬性,比如是否可持久化,是否自動刪除

交換器元數據:交換器名稱類型屬性

綁定元數據:內部是一張表格記錄如何將消息路由到隊列

vhost 元數據:為 vhost 內部的隊列、交換器、綁定提供命名空間和安全屬性

技術選型

在我們平時開發中對各種消息隊列 MQ 選型的思考

如果用戶訪問量在 ActiveMQ 的可承受範圍內,而且確實主要是基於解耦和非同步來用的,可以考慮 ActiveMQ ,也比較貼近 Java 工程師的使用習慣,但是ActiveMQ 現在停止維護了,同時ActiveMQ 並發不高,所以業務量一定的情況下可以考慮使用。

RabbitMQ 作為一個純正血統的消息中間件,有著高級消息協議 AMQP 的完美結合,在消息中間件中地位無可取代,但是 erlang 語言阻止了我們去深入研究和掌控,對公司而言,底層技術無法控制,但是確實是開源的,有比較穩定的支援,活躍度也高。

對自己公司技術實力有絕對自信的,可以用 RocketMQ ,但是 RocketMQ 誕生比較晚,並且更新迭代很快,這個意味著在使用過程中有可能會遇到很多坑,所以如果你們公司 Java 技術不是很強,不推薦使用。

中小型公司,技術實力較為一般,技術挑戰不是特別高,用 ActiveMQ、RabbitMQ 是不錯的選擇;大型公司,基礎架構研發實力較強,用 RocketMQ 是很好的選擇

如果是大數據領域的實時計算、日誌採集等場景,用 Kafka 是業內標準的,絕對沒問題,社區活躍度很高,幾乎是全世界這個領域的事實性規範。

性能上來看,使用文件系統的消息中間件(Kafka、RokcetMq)性能是最好的,所以基於文件系統存儲的消息中間件是發展趨勢。(從存儲方式和效率來看文件系統>KV存儲>關係型資料庫)

最後

消息隊列呢,其實都大同小異,用法基本都是一樣的,只是各個開源消息隊列的側重點稍有不同,我們應該根據我們自己的項目需求來決定我們應該選取什麼樣的消息隊列來為我們的項目服務,這個項目選型的工作一般都是研發前期,組長就已經決定好了用哪個來做,一般是輪不到我們來做的,但是面試的時候可能會考察相關知識,所以這幾種消息隊列我們都應該對其了解並能靈活使用