標籤:
rabbitmq作為成熟的企業訊息中介軟體,實現了應用程式間介面調用的解耦,提高系統的輸送量。
下面介紹下rabbitmq的一些基本概念:
- message acknowledgment: 訊息確認,解決訊息確認問題,只有收到ack之後才能從訊息系統中刪除。
- message durability: 消 息持久化,當rabbitmq退出或崩潰後,會把queue中的訊息持久化。但注意,RabbitMQ並不能百分之百保證訊息一定不會丟失,因為為了提 升效能,RabbitMQ會把訊息暫存在記憶體緩衝中,直到達到閥值才會批量持久化到磁碟,也就是說如果在持久化到磁碟之前RabbitMQ崩潰了,那麼就 會丟失一小部分資料,這對於大多數情境來說並不是不可接受的,如果確實需要保證任務絕對不丟失,那麼應該使用事務機制
- exchange: 映射關係,實現訊息名和隊列之間的映射,根據訊息名將訊息發送到相應的隊列中。
- 常見的映射模式:
- direct:轉寄訊息到routigKey指定的隊列
- topic:按規則轉寄訊息(最靈活)
- headers:
- fanout:轉寄訊息到所有綁定隊列
-
- routing:exchange和queue之間綁定的媒介,成為routing key
在elong,我們開發了一套基於rabbitmq的訊息系統,可以實現訊息的可靠傳輸,提供了簡單的restful api, 減少業務使用rabbitmq的學習成本。
下面說下這套系統
jmsg的主要組成部分,在說之前,需要首先串連資料庫結構: 1.MessageConfig 發送端配置,訊息->Queue映射關係 CREATE TABLE `MessageConfig` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `MessageName` varchar(200) NOT NULL, --訊息名稱 `ExchangeName` varchar(200) NOT NULL, --訊息名和隊列的映射關係 `Priority` varchar(50) DEFAULT NULL, -- exchange與queue之前綁定的媒介 `UseDelayRetry` bit(1) DEFAULT NULL, — 是否使用重試 `DelayTime` int(11) DEFAULT NULL, —延遲多長時間重試 `MaxRetryCount` int(11) DEFAULT ‘3’, —最大重試次數 `_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`ID`), UNIQUE KEY `IX_MessageName` (`MessageName`)) ENGINE=InnoDB AUTO_INCREMENT=409 DEFAULT CHARSET=utf8;
欄位解釋:MessageName: 訊息名稱ExchangeName: exchange名稱Priority: 優先順序,一個業務線可以根據不同優先順序有多個隊列UseDelayRetry:是否使用重試DelayTime: 延遲多長時間重試MaxRetryCount: 最大重試次數 表資料 2.MessageConsumersConfig 表: 消費端配置,訊息->接收方配置 CREATE TABLE `MessageConsumersConfig` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `MessageName` varchar(200) NOT NULL, — 訊息名 `Url` varchar(400) NOT NULL, — 訊息消費的url `TimeOut` int(11) DEFAULT ‘10’, PRIMARY KEY (`ID`)) ENGINE=InnoDB AUTO_INCREMENT=266 DEFAULT CHARSET=utf8; MessageName: 訊息名Url: 訊息消費urlTimeout: 消費逾時時間 CREATE TABLE `QueueSetting` ( `ID` bigint(20) NOT NULL AUTO_INCREMENT, `QueueName` varchar(50) DEFAULT NULL, `QOS` int(11) DEFAULT NULL, `ParallelCount` int(11) DEFAULT NULL, `LastUpdateTime` datetime DEFAULT NULL, `LastUpdateUserName` varchar(50) DEFAULT NULL, PRIMARY KEY (`ID`)) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8; rabbimq 配置
serverIP : 伺服器ip
Port:服務連接埠號碼
UserName: 使用者名稱
PassWord: 密碼
MaxPoolSize: 最大串連池大小
RequestedHeartbeat: 請求心跳檢查時間(s)
RequestedConnectionTimeout: 請求連線逾時時間
FailedLogBaseDir: 失敗日誌儲存目錄
ConnectionTimeOut: 串連保持時間(ms)
SendTimeOut: 發送逾時(s)
ReceiveTimeOut: 接收逾時時間(s)
SendLogBaseDir(發送日誌目錄)
1 jmsg-client
訊息發送用戶端,提供發送訊息的介面
流程圖:
其中比較重要的是RabbitConnectPool(單例建立串連池),該類中比較重要的屬性和方法
_max: //可以建立的最大串連數
_created: 已經使用的串連數
_used: 已經使用的串連數
_sendTimeOut: 發送串連請求逾時時間
_receiveTimeOut: 接收串連成功的逾時時間
_clientExpires: 串連到期時間
_connectionTimeOut: 連線逾時
_qos:
重要的方法:
getSendingConnection(): 擷取一個發送端的串連, 如果不是強制,就從串連池中擷取串連,否則強制建立一個串連
getNextProxy:() 從串連池中過去串連(返回RabbitSendProxy), 如果超過最大串連數,則建立新串連, 否則加鎖擷取 proxy(pollProxy),如果返回為空白,這等待,直到擷取串連為止
pollProxy(): 擷取串連, 從proxyqueue中poll,如果串連不可用,這_created–, 然後_used++, 如果建立條數 < 最大數, 這擷取新串連(newProxy(), create++, _used++
returnToPool(): 返回到串連池,
getNewProxy(): 三次重試, getProxy(), 稍候再試0.1s
getProxy(): 通過原廠模式產生串連
RabbitProxy: 用戶端串連rabbitmq 代理介面,做為串連池中的單元串連代理,可以由發送端和接收端繼承
主要屬性:
isAvailable: 是否可用,預設true
createTime: 建立時間,
DisposeListener: 串連池關閉需要執行的介面
connectionTimeout: 連線逾時時間, 目前時間-createTime <= connectionTimeout可用
receiveTimeOut: 接收逾時時間
Connection: 最主要的類,com.rabbitmq.client.Connection 串連
qos: 伺服器一次可以傳輸的訊息條數
Channel : 管道,串連建立管道,進行資料轉送
ConnectionFactory: 串連工廠,建立rabbitmq串連
主要操作:
isAvailable(): 串連是否可用
dispose(): 關閉串連 需要關閉channel和connection
RabbitSendProxy 發送端代理, 預設開通channel confirsSelect,即確認機制
send(): 發送方法
流程:轉換成byte數組->檢查訊息長度(小於64K) -> 快取資料,等待確認->發送(basicPublish) -> 在接收到後刪除快取資料
下面說下如何保證資料一定能發送到rabbit queue中:
為瞭解決發送失敗的問題,解決的思路無非是訊息持久化,採用檔案做持久化是比較好的選擇。
具體的實現是訊息失敗後,放入blockingqueue作為資料換出的地方,定期從queue中讀取資料存放區檔案,開啟定時任務讀取資料,重新send到queue中。
2 jmsg-server
作用: 從rabbitmq中讀取訊息,通過http介面調用消費者
資料庫
jmg-server的流程:
- 從資料庫拿到該機器需要處理的queue,初始化rabbitmq串連池
- 遍曆queuelist,註冊監聽器,對每個queue擷取的訊息處理
- 對每個queue開啟MessageReceiver線程,監聽該queue資料
- messageReceiver 開啟線程池,qos是線程池大小
- messageReceiver是一個迴圈,不斷擷取rabbitmq server串連
- 擷取到資料後,開啟線程進行處理MessageProcessTask, 該任務主要是尋找ImessageListener的實作類別,調用receive方法
- 接收到訊息處理:
消 息校正 -> 擷取訊息配置,找到消費者-> 判斷沒有正在處理 -> 訊息還沒有處理成功or 沒有達到最大處理失敗次數 – > 首次接收的訊息入庫- > 廣播訊息到接收方 -> 處理成功,記錄messageLog,修改狀態; 處理失敗,發送到rabbitmq-server,等待下次處理.
3 rabbitmq-server
採用叢集的方式搭建, 通過nginx對外提供統一的url
叢集中一些重要的概念:
network partition: 網路中斷,一般是子網之間的裝置中斷,這樣在不同子網的裝置通訊會出現問題
搭建叢集:
abbitmq的叢集是依附於erlang的叢集來工作的,所以必須先構建起erlang的叢集景象。Erlang的叢集中各節點是經由過程一個magic cookie來實現的,這個cookie存放在 $home/.erlang.cookie 中(像我的root使用者安裝的就是放在我的root/.erlang.cookie中),檔案是400的許可權。所以必須包管各節點cookie對峙一致,不然節點之間就無法通訊。
方案1: 普通叢集
erlang 通過cookie來決定是否能和另外一個節點通訊,通常的做法是在一個機器上產生cookie檔案,拷貝到叢集中的其他機器。
叢集可以通過單邏輯broker的方式來串連多個機器。各機器間通過Erlang訊息傳遞來通訊,因此,叢集內所有節點都必須有相同的Erlang cookie。叢集內機器間的網路連接必須是可信的,且所有機器必須運行相同版本的Erlang和RabbitMQ。
虛擬機器、交換器、使用者和許可權會自動鏡像到叢集內所有節點。隊列可能位於單節點上,或者鏡像到多個節點上。用戶端串連到叢集內任何節點都能看到叢集內所有隊列。
步驟
1
rabbit1$ rabbitmq-server -detachedrabbit2$ rabbitmq-server -detachedrabbit3$ rabbitmq-server -detached
2 加入以rabbit3為叢集,叢集名為[email protected],則需要在rabbit1和rabbit2上執行下面操作,加入[email protected],
rabbit2$ rabbitmqctl stop_appStopping node [email protected] ...done.rabbit2$ rabbitmqctl join_cluster [email protected]Clustering node [email protected] with [[email protected]] ...done.rabbit2$ rabbitmqctl start_appStarting node [email protected] ...done.
3 同樣在rabbit3,上操作,加入[email protected]
rabbit3$ rabbitmqctl stop_appStopping node [email protected] ...done.rabbit3$ rabbitmqctl join_cluster [email protected]Clustering node [email protected] with [email protected] ...done.rabbit3$ rabbitmqctl start_appStarting node [email protected] ...done.
方案2:鏡像隊列
上述配置的RabbitMQ預設叢集模式,但並不包管隊列的高可用性,儘管互換機、綁定這些可以複製到 叢集裡的任何一個節點,然則隊列內容不會複 制,固然該模式解決一項目組節點壓力,但隊列節點宕機直接導致該隊列無法應用,只能守候重啟,所以要想在隊列節點宕機或故障也能正常應用,就要複製隊列內 容到叢集裡的每個節點,須要建立鏡像隊列。
Federation允許一個broker上的交換器接收發布到另一個broker(這個broker可能是單獨的機器或者叢集)上的交換器的訊息。為了節點間能夠通過AMQP(帶上SSL選項)通訊,組成federation的兩個交換器之間必須授予適當的使用者和許可權。
組成federation的交換器之間通過單向點對點連接。預設情況下,在federation串連上,訊息僅僅被轉寄一次,但是這樣可增加更多、更複雜的路由拓撲。
在federation串連上,有些訊息可能不會被轉寄;如果一條訊息到達federated交換器後不能被路由到某個隊列,則它不會被轉寄。
你可以在Internet上通過federation串連各個broker來pub/sub訊息。
方案3: shovel
相比federation,工作在更低一層,shovel簡單從一個broker的一個queue中消費訊息,並傳遞到下一個broker的exchange上
the shovel simply consumes messages from a queue on one broker, and forwards them to an exchange on another.
參考資料:
1 http://lynnkong.iteye.com/blog/16996842 http://blog.chinaunix.net/topic/surpershi/
3 http://www.rabbitmq.com/documentation.html
rabbitmq在藝龍業務系統中的實踐