Rabbimq 安裝配置詳解

一、rabbitmq 實現原理

   Rabbitmq 是實現AMQP(高級消息隊列協議)的消息中間件的一種,最初起源於金融系統,用於在分佈式系統中存儲轉發消息,在易用性,擴展性、高可用性等方面表現不俗。消息中間件主要用於組件之間的解耦,消息發送者無需知道消息使用的存在,反之亦然:

  AMQP當中有四個概念非常重要:虛擬主機(virtual host),交換機(exchange)、隊列(node1)和綁定(binding)。一個虛擬主機持有一個交換機、隊列和綁定。爲什麼需要那麼多虛擬主機呢?很簡單,rabbitmq 當中,用戶只能在虛擬主機的力度進行權限控制。因此,如果需要禁止A組訪問B組的交換機、隊列、綁定,必須爲A和B分別創建一個虛擬主機。每個RabbitMQ服務器都有一個默認的虛擬主機"/"。

 Producer要產生消息必須要創建一個Exchange,Exchange用於轉發消息,但是他不會做存儲,如果沒有Node1 Bind到Exchange的話,他會直接丟掉Producer發送過來的消息,當然如果消息總是發送過去就被直接丟掉就沒有什麼意思了,一個consumer想要接受消息的話,就要創建一個node1,並把這個node1 bind 到指定的exchange上,然後exchange會把消息轉發到node1那裏,node1會負責存儲消息,Consumer可以通過主動Pop或者是Subscribe 之後被動回調的方式來從node1中取得消息。

  使用rabbitmq Server需要:

   1 Erlang語言包

   2 RabbitMQ 安裝包

二、rabbitmq 概念和特性

 2.1  交換機

     1 接收消息,轉發消息到綁定的隊列。四種類型: direct,topic,headersand fanout

        Direct:轉發消息到routigkey 指定的隊列

        Topic:按規則轉發消息

        Headers:

        Fanout: 轉發消息到所有綁定的隊列

     2  如果沒有綁定在交換機上,則發送到該交換機上的消息會丟失。

     3 一個交換機可以綁定多個隊列,一個隊列可以被多個交換機綁定

     4. topic類型交換器通過模式匹配分析消息的routing-key屬性。它將routing-key和binding-key的字符串切分成單詞。這些單詞之間用點隔開。它同樣也會識別兩個通配符:#匹配0個或者多個單詞,*匹配一個單詞。例如,binding key:*.stock.#匹配routing key:usd.stcok和eur.stock.db,但是不匹配stock.nana。

還有一些其他的交換器類型,如header、failover、system等,現在在當前的RabbitMQ版本中均未實現。

5. 因爲交換器是命名實體,聲明一個已經存在的交換器,但是試圖賦予不同類型是會導致錯誤。客戶端需要刪除這個已經存在的交換器,然後重新聲明並且賦予新的類型。

6. 交換器的屬性:

  •  持久性:如果啓用,交換器將會在server重啓前都有效。

  •  自動刪除:如果啓用,那麼交換器將會在其綁定的隊列都被刪除掉之後自動刪除掉自身。

  •  惰性:如果沒有聲明交換器,那麼在執行到使用的時候會導致異常,並不會主動聲明


2.2 隊列(node1):

  1. 隊列是RabbitMQ內部對象,存儲消息。相同屬性的node1可以重複定義。

  2. 臨時隊列。channel.node1Declare(),有時不需要指定隊列的名字,並希望斷開連接時刪除隊列。

  3. 隊列的屬性:

  •    持久性:如果啓用,隊列將會在server重啓前都有效。

  •    自動刪除:如果啓用,那麼隊列將會在所有的消費者停止使用之後自動刪除掉自身。

  •    惰性:如果沒有聲明隊列,那麼在執行到使用的時候會導致異常,並不會主動聲明。

  • 排他性:如果啓用,隊列只能被聲明它的消費者使用。

這些性質可以用來創建例如排他和自刪除的transient或者私有隊列。這種隊列將會在所有鏈接到它的客戶端斷開連接之後被自動刪除掉。它們只是短暫地連接到server,但是可以用於實現例如RPC或者在AMQ上的對等通信。4. RPC的使用是這樣的:RPC客戶端聲明一個回覆隊列,唯一命名(例如用UUID),並且是自刪除和排他的。然後它發送請求給一些交換器,在消息的reply-to字段中包含了之前聲明的回覆隊列的名字。RPC服務器將會回答這些請求,使用消息的reply-to作爲routing key(默認綁定器會綁定所有的隊列到默認交換器,名稱爲“amp.交換器類型名”)發送到默認交換器。注意這僅僅是慣例而已,可以根據和RPC服務器的約定,它可以解釋消息的任何屬性(甚至數據體)來決定回覆給誰。


2.3 消息傳遞:

  1. 消息在隊列中保存,以輪詢的方式將消息發送給監聽消息隊列的消費者,可以動態的增加消費者以提高消息的處理能力。

  2. 爲了實現負載均衡,可以在消費者端通知RabbitMQ,一個消息處理完之後纔會接受下一個消息。

    channel.basic_qos(prefetch_count=1)

    注意:要防止如果所有的消費者都在處理中,則隊列中的消息會累積的情況。

  3. 消息有14個屬性,最常用的幾種:

     deliveryMode:持久化屬性

     contentType:編碼

     replyTo:指定一個回調隊列

     correlationId:消息id

      

  4. 消息生產者可以選擇是否在消息被髮送到交換器並且還未投遞到隊列(沒有綁定器存在)和/或沒有消費者能夠立即處理的時候得到通知。通過設置消息的mandatory和/或immediate屬性爲真,這些投遞保障機制的能力得到了強化。

  5. 此外,一個生產者可以設置消息的persistent屬性爲真。這樣一來,server將會嘗試將這些消息存儲在一個穩定的位置,直到server崩潰。當然,這些消息肯定不會被投遞到非持久的隊列中。

2.4 高可用性(HA):

  1. 消息ACK,通知RabbitMQ消息已被處理,可以從內存刪除。如果消費者因宕機或鏈接失敗等原因沒有發送ACK(不同於ActiveMQ,在RabbitMQ裏,消息沒有過期的概念),則RabbitMQ會將消息重新發送給其他監聽在隊列的下一個消費者。

  channel.basicConsume(node1name,noAck=false, consumer);

  2. 消息和隊列的持久化。定義隊列時可以指定隊列的持久化屬性(問:持久化隊列如何刪除?)

channel.node1Declare(node1name,durable=true, false, false, null);

發送消息時可以指定消息持久化屬性:

channel.basicPublish(exchangeName,routingKey,

            MessageProperties.PERSISTENT_TEXT_PLAIN,

            message.getBytes());

這樣,即使RabbitMQ服務器重啓,也不會丟失隊列和消息。

  3. publisherconfirms

  4. master/slave機制,配合Mirrored Node1,這種情況下,publisher會正常發送消息和接收消息的confirm,但對於subscriber來說,需要接收Consumer Cancellation Notifications來得到主節點失敗的通知,然後re-consume from the node1,此時要求client有處理重複消息的能力。注意:如果node1在一個新加入的節點上增加了一個slave,此時slave上沒有此前node1的信息(目前還沒有同步機制)。

(通過命令行或管理插件可以查看哪個slave是同步的:

rabbitmqctllist_node1s name slave_pids synchronised_slave_pids)

    當一個slave重新加入mirrored-node1時,如果node1是durable的,則會被清空。

2.5  集羣(cluster):

  1. 不支持跨網段(如需支持,需要shovel或federation插件)

  2. 可以隨意的動態增加或減少、啓動或停止節點,允許節點故障

  3. 集羣分爲RAM節點和DISK節點,一個集羣最好至少有一個DISK節點保存集羣的狀態。

  4. 集羣的配置可以通過命令行,也可以通過配置文件,命令行優先。

三、Rabbit集羣高可用集羣

     Rabbitt 是用erlang開發的,集羣非常方便,因爲erlang 天生就是一門分佈式語言,但其本身並不支持負責均衡。

     Rabbitt模式大概分爲三種:單一模式、普通模式、鏡像模式

     1 單一模式: 最簡單的情況,並非集羣模式

     2 普通模式:默認的集羣模式

       對於Node1來說,消息實體只存在於其中一個節點,A、B兩個節點,僅有相同的元素數據,即隊列結構。

       當消息進入A節點的Node1中後,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行消息傳輸,把A中的消息實體去出經過B發送給consumer。

       所以consumer應儘量連接每一個節點,從中取消息。即對於同一個邏輯列,要在多個節點建立物理node1。否則無論consumer連接A或B,出口總在A,會產生瓶頸。

       該模式存在一個問就是當A節點故障後,B節點無法取到A節點中還爲消費的消息實體。

       如果做了消息持久化,那麼A節點恢復,然後纔可被消費。

      3 鏡像模式: 把需要的隊列做成鏡像隊列,存在於多個節點,屬於RabbitMQ的HA方案

     該模式解決了上述問題,其實質和普通模式不同之處在於,消息實體會主動在鏡像節點時間同步,而不是在consumer取數據時臨時拉取。

     該模式帶來了副作用也很明顯,除了降低系統性能外,如果鏡像隊列數量過多,加之量大的消息進入,集羣內部的網絡帶寬將會被這種同步通訊大大的消耗掉。所以在對可靠性要求較高的場合適用

      4 集羣中的基本概念:

      RabbitMQ 的集羣節點包括內存節點、磁盤節點。

        內存節點就是將數據存放在內存

        磁盤節點就是將數據存放在磁盤上。

        如果在投遞消息時,打開了消息的持久化,那麼即使是內存節點,數據還是安全的存放在磁盤。

        一個rabbitmq集羣中可以共享user、vhost、node1、exchange等,所有的數據和狀態都是必須在所有節點上覆制的,一個例外是,那些當前只屬於創建它的節點消息隊列,儘管他們可見且可被所有節點讀取。Rabbitmq節點可以是動態的加入到集羣中,一個節點他可以加入到集羣中,也可以從集羣環集羣會進行一個基本的負載均衡


集羣中有兩種節點:

   1 內存節點: 只保存狀態到內存(一個例外的情況是:持久的node1的持久的內容將被保存到disk)

   2 磁盤節點:保存狀態到內存和磁盤上,內存節點雖然不寫入到磁盤,但是他比磁盤節點要好。集羣中,只需要一次磁盤節點來保存狀態就足夠了,如果集羣中只用內存節點,那麼不能停止他們,否則所有的狀態,消息等都會消失。


四  rabbit 安裝

1 環境:

    OS Version:Centos 6.6
    Rabbitmqversion:  rabbitmq-server-3.5.3.tar.gz
    Erlang:   otp_src_17.5.tar.gz
    Simplejson: simplejson-3.6.5.tar.gz

2 安裝erlang

    #tar  xf   otp_src_17.5.tar.gz
    #cd otp_src_17.5
    #./configure ---prefix=/usr/local/erlang
   *********************************************************************
   **********************  APPLICATIONS DISABLED  *****************           *********************************************************************
   odbc           : ODBC library - link check faile
   *********************************************************************
   *********************************************************************
   **********************  APPLICATIONS INFORMATION  *******************
    *********************************************************************
   wx             : wxWidgets not found, wx will NOTbe usable
   *********************************************************************
   #make  && make instal


3  安裝simplejson

   #tar  xf simplejson-3.6.5.tar.gz
   #cd simplejson-3.6.5
   #python setup.py install

   

4 安裝rabbitmq

# tar xf rabbitmq-server-3.5.3.tar.gz
#cd rabbitmq-server-3.5.3
#make
#export TARGET_DIR=/usr/local/rabbitmq/
#export SBIN_DIR=/usr/localrabbitmq/sbin
#export MAN_DIR=/usr/local/rabbitmq/man
#make install

5 配置rabbitmq

#cd /usr/local/rabbtmq/sbin
#cat rabbitmq-default | grep –v ^#| grep –v ^$
SYS_PREFIX=
ERL_DIR=
SASL_BOOT_FILE=start_sasl
CONFIG_FILE=${SYS_PREFIX}/conf/rabbitmq/rabbitmq
LOG_BASE=${SYS_PREFIX}/logs/rabbitmq
MNESIA_BASE=${SYS_PREFIX}/data/rabbitmq/mnesia
ENABLED_PLUGINS_FILE=${SYS_PREFIX}/conf/rabbitmq/enabled_plugins
PLUGINS_DIR="${RABBITMQ_HOME}/plugins"
CONF_ENV_FILE=${SYS_PREFIX}/apps/conf/rabbitmq/rabbitmq.conf

6 啓動rabbitmq

#cd/usr/local/rabbitmq/sbin/rabbitmq-server
#創建用戶
# ./rabbitmqctl add_user frank frank123
#User爲用戶名,tag爲角色名(administration,monitoring,policymaker,management)可以給多個角色使用逗號分隔
#./rabbitmqctl  set_user_tags  vci administrator
### 設置用戶權限
#./rabbitmqctl  set_permissions  -p /  vci  .* .*  .*
### 查看(指定hostpath)所有用戶的權限信息
#./rabbitmqctl  list_permissions  [-p  /]
### 查看指定用戶的權限信息
#: ./rabbitmqctllist_user_permissions vci
### 清除用戶的權限信息
#:./rabbitmqctl  clear_permissions  [-p VHostPath]  vci
### 刪除一個用戶
#./rabbitmqctl delete_user Username
### 修改用戶的密碼
#:./rabbitmqctl  change_password  Username Newpassword
### 查看當前用戶列表
#:./rabbitmqctl  list_users
### 添加用戶遠程訪問
#: vi/etc/rabbitmq/rabbitmq.config
[ 
{rabbit,[{tcp_listeners, [5672]}, {loopback_users, ["vci"]}]} 
].

五 rabbitmq 集羣安裝

 1 安裝rabbitmq 的管理插件

    #cd /usr/local/rabbitmq/sbin
   #./rabbitmq-plugins enablerabbitmq_management

 2 在/etc/hosts添加一下內容

192.168.1.115 node1
192.168.1.116 node2
192.168.1.200 node3

  3 設置Erlang cookie

   Rabbitmq的集羣是依賴於erlang的集羣來工作的,所以必須先構建起erlang的集羣環境。Erlang的集羣中各節點是通過一個magic cookie來實現的,這個cookie存放在/var/lib/rabbitmq/.erlang.cookie 中,文件是400的權限。所以必須保證各節點cookie保持一致,否則節點之間就無法通信。

-r--------.1 rabbitmq rabbitmq 20 3月 500:00 /var/lib/rabbitmq/.erlang.cookie

將其中一臺節點上的.erlang.cookie值複製下來保存到其他節點上。或者使用scp的方法也可,但是要注意文件的權限和屬主屬組。

我們這裏將node1中的cookie 複製到 node2、node3中,先修改下node2、node3中的.erlang.cookie權限

#chmod777  /var/lib/rabbitmq/.erlang.cookie

將node1的/var/lib/rabbitmq/.erlang.cookie這個文件,拷貝到node2、node3的同一位置(反過來亦可),該文件是集羣節點進行通信的驗證密鑰,所有節點必須一致。拷完後重啓下RabbitMQ。

複製好後別忘記還原.erlang.cookie的權限,否則可能會遇到錯誤

#chmod400 /var/lib/rabbitmq/.erlang.cookie

設置好cookie後先將三個節點的rabbitmq重啓

#rabbitmqctl stop
#rabbitmq-server start

   4 停止所有節點RabbitMq服務,然後使用detached參數獨立運行,這步很關鍵,尤其增加節點停止節點後再次啓動遇到無法啓動都可以參照這個順序

   

  node1# rabbitmqctl stop
  node2# rabbitmqctl stop
  node3# rabbitmqctl stop
  node1# rabbitmq-server -detached
  node2# rabbitmq-server -detached
  node3# rabbitmq-server –detached

   5  分別查看下每個節點

    

node1# rabbitmqctl cluster_status
          Cluster status of node rabbit@node1...
[{nodes,[{disc,[rabbit@node1]}]},
{running_nodes,[rabbit@node1]},
{partitions,[]}]
..        .done.
node2# rabbitmqctl cluster_status
          Cluster status of node rabbit@node2...
[{nodes,[{disc,[rabbit@node2]}]},
{running_nodes,[rabbit@node2]},
{partitions,[]}]
...         done.
node3# rabbitmqctl cluster_status
          Cluster status of node rabbit@node3...
[{nodes,[{disc,[rabbit@node3]}]},
{running_nodes,[rabbit@node3]},
{partitions,[]}]
...        done.

6 將node2、node3作爲內存節點與queue連接起來,在node2上,執行如下命令: 

node2# rabbitmqctl stop_app
node2#rabbitmqctl join_cluster --ram rabbit@queue  
node2#rabbitmqctl start_app
node3#rabbitmqctl stop_app
node3#rabbitmqctl join_cluster --ram rabbit@queue  (上方已經將node2與queue連接,也可以直接將node3與node2連接,同樣而已加入集羣中)
node3#rabbitmqctl start_app

上述命令先停掉rabbitmq應用,然後調用cluster命令,將node2連接到,使兩者成爲一個集羣,最後重啓rabbitmq應用。在這個cluster命令下,node2、node3是內存節點,queue是磁盤節點(RabbitMQ啓動後,默認是磁盤節點)。

queue 如果要使node2或node3在集羣裏也是磁盤節點,join_cluster 命令去掉--ram參數即可

      #rabbitmqctl join_clusterrabbit@queue  

只要在節點列表裏包含了自己,它就成爲一個磁盤節點。在RabbitMQ集羣裏,必須至少有一個磁盤節點存在。

8     往任意一臺集羣節點裏寫入消息隊列,會複製到另一個節點上,我們看到兩個節點的消息隊列數一致:(如何發送消息參見)

# rabbitmqctllist_queues -p hrsystem
Listing queues …
test_queue 10000
…done.
# rabbitmqctllist_queues -p hrsystem
Listing queues …
test_queue 10000
…done.
# rabbitmqctllist_queues -p hrsystem
Listing queues …
test_queue 10000
…done.

-p參數爲vhost名稱

這樣RabbitMQ集羣就正常工作了,

這種模式更適合非持久化隊列,只有該隊列是非持久的,客戶端才能重新連接到集羣裏的其他節點,並重新創建隊列。假如該隊列是持久化的,那麼唯一辦法是將故障節點恢復起來。 

爲什麼RabbitMQ不將隊列複製到集羣裏每個節點呢?這與它的集羣的設計本意相沖突,集羣的設計目的就是增加更多節點時,能線性的增加性能(CPU、內存)和容量(內存、磁盤)。理由如下:

1. storage space:If every cluster node had a full copy of every queue, adding nodes wouldn’tgive you more storage capacity. For example, if one node could store 1GB ofmessages, adding two more nodes would simply give you two more copies of thesame 1GB of messages.

2. performance:Publishing messages would require replicating those messages to every clusternode. For durable messages that would require triggering disk activity on allnodes for every message. Your network and disk load would increase every timeyou added a node, keeping the performance of the cluster the same (or possiblyworse).

當然RabbitMQ新版本集羣也支持隊列複製(有個選項可以配置)。比如在有五個節點的集羣裏,可以指定某個隊列的內容在2個節點上進行存儲,從而在性能與高可用性之間取得一個平衡。

鏡像模式配置

上面配置RabbitMQ默認集羣模式,但並不保證隊列的高可用性,儘管交換機、綁定這些可以複製到集羣裏的任何一個節點,但是隊列內容不會複製,雖然該模式解決一部分節點壓力,但隊列節點宕機直接導致該隊列無法使用,只能等待重啓,所以要想在隊列節點宕機或故障也能正常使用,就要複製隊列內容到集羣裏的每個節點,需要創建鏡像隊列。

我們看看如何鏡像模式來解決複製的問題,從而提高可用性

9 設置鏡像隊列策略

#./rabbitmqctlset_policy ha-all "^" '{"ha-mode":"all"}'

### 將所有隊列設置爲鏡像隊列,即隊列會被複制到各個節點,各個節點狀態保持一直。

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