消息中間件 Apache Qpid 簡介



https://www.ibm.com/developerworks/cn/opensource/os-cn-qpid1/

https://www.ibm.com/developerworks/cn/opensource/os-cn-qpid2/

Qpid 是 Apache 開發的一款面向對象的消息中間件,它是一個 AMQP 的實現,可以和其他符合 AMQP 協議的系統進行通信。Qpid 提供了 C++/Python/Java/C# 等主流編程語言的客戶端庫,安裝使用非常方便。相對於其他的 AMQP 實現,Qpid 社區十分活躍,有望成爲標準 AMQP 中間件產品。除了符合 AMQP 基本要求之外,Qpid 提供了很多額外的 HA 特性,非常適於集羣環境下的消息通信。

引子,Qpid 使用場景

通信是一個基本的需求,正如人與人之間需要交流一樣,比較大型的軟件系統也往往需要內部或者外部通信。

在系統間通信最基礎的通信方式是 socket,但 socket 比較底層。使用起來非常不易。如果需要一些高級特性,需要很多的編程負擔。

與 socket 的原始對應,企業級的應用軟件往往有着各種各樣從簡單到複雜的通信需求,表現爲不同的通信模型。常見的有:

  • 點對點:A 發消息給 B。
  • 廣播:A 發給所有其他人的消息
  • 組播:A 發給多個但不是所有其他人的消息。
  • Requester/response:類似訪問網頁的通信方式,客戶端發請求並等待,服務端回覆該請求
  • Pub-sub:類似雜誌發行,出版雜誌的人並不知道誰在看這本雜誌,訂閱的人並不關心誰在發表這本雜誌。出版的人只管將信息發佈出去,訂閱的人也只在需要的時候收到該信息。
  • Store-and-forward:存儲轉發模型類似信件投遞,寫信的人將消息寫給某人,但在將信件發出的時候,收信的人並不一定在家等待,也並不知道有消息給他。但這個消息不會丟失,會放在收信者的信箱中。這種模型允許信息的異步交換。
  • 其他通信模型。。。

除了各類不同的通信模型之外,系統間的通信還有其他一些需要考慮的問題。比如企業級應用往往有巨量的數據需要交換,對可靠性的要求也比較高。比如一個分佈式的財務處理軟件,每時每刻都有成千上萬的用戶在使用,需要產生難以想象的海量消息,每個消息可能都關乎某人的銀行賬戶等關鍵信息,如果丟失將帶來巨大損失。編寫這樣一個通信中間件不是一件容易的事情,即使編寫出來,假如需要和其他的軟件系統交互信息,又需要大量的格式轉換,接口遷移等工作。

爲了解決以上這些問題,人們開發出了很多的軟件產品和協議。從早期的 RPC,到複雜的面向消息的中間件 (MOM),再到 JMS,人們取得了很多的進步,但是這些技術還是存在各自的問題。

RPC,Corba 等技術是同步的,即調用者必須等待對方的回覆,這意味着調用者必須瞭解接收者,是一種緊耦合的模型。緊耦合意味着不靈活,而在軟件行業唯一不變的就是變化,當需求和環境發生變化時,緊耦合的應用修改代價非常高。

爲此衆多的消息中間件產品應運而生,打破了消息生產者和消費者之間的緊耦合關係。但中間件產品是由各個廠商自行定義和實現的,在整合企業級應用的時候,人們發現各種應用往往採用了不同的技術和中間件產品,要想讓這些產品互通消息,又是一件非常困難的事情。

JMS 是標準化的一種努力,但其缺點在於 JMS 是 J2EE 的標準,假如不是採用 Java 技術實現的產品,想使用 JMS 還是比較麻煩的。

因此即便到了今天人們還是希望有一款功能強大,平臺 / 語言無關,標準化的面向消息的中間件產品。

假如這正是您時時刻刻所想的問題,那麼 Qpid 便是您值得了解的一款開源軟件。它實現了可靠複雜的通信中間件,支持多種通信模型,效率高,平臺語言無關,而且實現了業界的通信標準 AMQP。


AMQP 和 Qpid

AMQP 是 Advanced Message Queuing Protocol,即高級消息隊列協議。和前面羅列的技術不同,AMQP 是一個標準化的消息中間件協議。她的理想是讓不同語言,不同系統的應用互相通信,並提供一個簡單統一的模型和編程接口。這樣,人們就可以採用各種語言和平臺來實現自己的應用,當需要和其他系統通信時,只要承認 AMQP 協議即可。

舉個不太自然的例子吧。。。

世界各地的人們由於地理和歷史的原因,使用着各種不同的語言,相互交流十分不易。AMQP 類似一架自動翻譯機,當我用中文對它說了什麼之後,假如一個英語世界的人想聽的話,可以聽到 英文版精確的一字不差的翻譯。

此外這個翻譯機還提供其他很多好處,比如中國和美國有 12 小時的時差,假如我現在希望和某個美國人通話,他必須半夜爬起來,或者我必須等到他起牀,但通過這臺機器,我說完就行了,那個美國人起牀後就會聽到的。我很放心,這句話絕不會丟掉,也絕不會走樣;

我其實可以不關心有多少人來聽,假如有更多的人都想聽,那麼他們也可以隨時聽到。

假如我只想讓部分人聽到,還可以加密認證;

假如有些人不想聽,有些人想聽,那麼這臺翻譯機也能知道誰想聽,而不會將我的聲音發給不想聽到的人。

這種交流方式和體驗,作爲一個人類我還不曾享受過,但是 AMQP 已經爲 衆多的計算機軟件提供了這種服務。

AMQP 來自 JPMorgon,最初只是這個財大氣粗的投行內部使用的消息中間件。發起人 John O'Hara 很有氣魄,他說“從 1996 年開始到 2003 我一直在等這樣一個標準,但始終沒有等到,我已經等不下去了”,並且“投行對這類標準的需求最迫切,而銀行又從來不缺乏技術專家” ,所以他自己開發了一個。我想一個人如果想成就什麼事,就需要這樣的英雄氣概吧。

因爲他的努力,AMQP 從金融界迅速推廣到整個計算機行業,參與者包括了很多 IT 巨頭。雖然今天 AMQP 依舊是一個草案,但值得我們拭目以待。

AMQP 的基本構架如下:

圖 1. AMQP 系統構架

圖 1. AMQP 系統構架

在 AMQP 模型中,消息的 producer 將 Message 發送給 Exchange,Exchange 負責交換 / 路由,將消息正確地轉發給相應的 Queue。消息的 Consumer 從 Queue 中讀取消息。

這個過程是異步的,Producer 和 Consumer 沒有直接聯繫甚至可以不知道彼此的存在。

Exchange 如何進行路由的呢?

這便依靠 Routing Key,每個消息都有一個 routing Key,而每個 Queue 都可以通過一個 Binding 將自己所感興趣的 Routing Key 告訴 Exchange,這樣 Exchange 便可以將消息正確地轉發給相應的 Queue。下表列出了這幾個關鍵概念的定義。

表 1. AMQP 的幾個概念
概念 描述
Producer A program that writes messages to an Exchange. To do this, the program creates a message, fills the message with content, gives the message a Routing Key, and sends the message to an Exchange.
Routing Key A string that the Exchangecan use to determine to which Queuesthe message should be delivered.
Exchange Accepts messages from Producersand routes them to Queuesif the message meets the criteria expressed in a binding.
Binding Defines the relationship between an Exchangeand a Queue, specifying which messages should be routed to a given Queue
Queue Holds messages and delivers them to the Consumersthat subscribe to the Queue.
Consumer A program that reads messages from a Queue. A Consumercan create, subscribe to, share, use, or destroy Queueand their Bindings(as long as it has have permission to do so).


爲了支持各種常見的通信模型,AMQP 定義了不同的 Exchange 類型,如下表所示 :

表 2. AMQP 定義的 Exchange 類型
Exchange 類型 路由行爲
Fan-Out Messages are routed to every Queue bound to the Exchange, ignoring the Routing Key
Direct A message is routed only if a Queue'sBinding Keyis the same as the message's Routing Key
Topic Similar to a Direct Exchange, but it supports multipart keys that contain multiple words separated by the "." delimiter; for instance, a message Producer can create messages with Routing Keys like usa.news, usa.weather, europe.news, and europe.weather.


AMQP 目前還是一個草案,最新版本是 0.10。

QPID 是 Apache Foundation 的一個開源項目,是一個 AMQP 實現。它提供了 C++ 和 Java 兩個版本的 broker,並支持多種語言的客戶端,它還包括一個配置工具集。

除了完全實現了 AMQP 的基本功能之外,Qpid 還提供了一些額外的特性:

  • 採用 Corosync 來保證了集羣環境下的 Fault-tolerant 特性
  • 支持 XML 類型的 Exchange,當消息格式爲 XML 時,可以利用 Xquery 進行過濾
  • 支持 plugin,用戶可以方便地增加新的功能,比如新的 exchange 類型
  • 提供了安全認證特性,任何 producer/consumer 需要和 broker 通信時,都需要提供身份認證,以便防止惡意的不相干的程序進入消息體系。QPID 的安全認證使用 SSL 協議。

使用 Qpid 編程

目前 Qpid 的最新版本是 0.10。從 0.6 版本開始,Qpid 的編程接口有了很大變化,之前的編程接口雖然繼續支持但已經過時。所以本文將掠過 0.5 的 API,直接介紹 Qpid Messaging API。

首先需要搭建一個實驗環境。

安裝

第一次看到 Qpid 的下載主頁時,我有點兒不知所措。過去,當我需要試用一個開源軟件時,在它的下載頁面上通常會看到一個 tar 包,最多不過是根據目標操作系統的不同,分成幾個 tar 包,只管下載相應的便可。但是 Qpid 的下載頁面卻有些讓我困惑,竟有衆多的按編程語言分類的 tar 包,一時之間也不知道下載哪個好。。。

如今似乎有些明白了,Qpid 是一個消息中間件,它大體分爲兩部分:broker 和 client 庫。

先來看 Client 庫,不同的編程語言,比如 Python,Ruby 等都需要其單獨的 client 包。這個很容易理解。

但 broker,Qpid 竟也有兩種實現 :C++ 和 Java。

Client 的選擇比較容易,您熟悉哪種語言便選擇哪個。但選擇 Java 的 Broker 還是 C++ 的 broker 卻會讓新手有些猶豫,相比之下選擇 KFC 還是麥當勞竟然是一件容易的事了。

java 版 broker 和 C++ 版 broker 各有優缺點,選擇也是因人而異。他們之間大部分特性相同,但也有不同,比如 Java 版的支持更多的 AMQP 版本;而 C++ 版本則支持 RDMA。QPID 社區將逐漸消除這些差異,但正如可口可樂和百事可樂共同存在這個世界上一樣,這兩個 broker 也終究會各有個的擁躉。Qpid 社區的資深開發者 Gordon Sim 回答某初學者的帖子對此應該很有幫助:http://fossplanet.com/f13/re-why-use-c-broker-versus-java-broker-71322/

本人對 java 基本上沒有什麼瞭解,對 C++ 和 python 則非常偏愛,所以打算使用 C++ 版本的 broker,client 端則採用 C++ 和 Python。

下載 Qpid broker 的安裝包,解壓,編譯,安裝:

 tar xzvf 
 ./configure 
 make 
 make install

經過以上幾步折騰,我們已經有了 C++ 的 broker 和 C++ 的 client 庫了。如果您想用 Python 編寫應用,那還需要下載 pyhonn 客戶端的 tar 包。

可喜的是 Python 無需編譯,因此所謂安裝只是設置一些環境變量而已。

這裏要提一下:如果您下載的是那個 full package,恐怕又需要費一點兒周折。該 package 包含了所有的東西 ( 各種語言的 broker 和 client),其中雖然也有 C++ 的 broker,但竟然和單獨下載的 C++ 包有所不同。到 cpp 目錄下,您看不到 configure 可執行文件。需要首先運行 bootstrap,初始化 autotools。

Bootstrap 之後,autoconfig 所需的條件便準備好了,之後便是常規的幾條安裝命令,總的來說如下:

 ./bootstrap 
 ./configure 
 make 
 make install

希望您的安裝一切順利。

啓動 broker

最簡單的啓動方式爲

 qpidd --port=60302 --no-data-dir --auth=no

以上啓動方式指定了三個主要的選項

--port 參數指定了 qpidd 監聽的端口號,默認爲 5672。

--no-data-dir 說明不需要指定數據持久化的目錄;當然您也可以指定一個目錄來存放持久化消息。

--auth=no 說明 qpidd 對所有的鏈接請求都不進行安全驗證。

其他的啓動參數在此不做詳細說明,讀者可以自行閱讀 help 信息。

管理 qpid

默認情況下,Broker 啓動之後,會自動創建一些 Exchange(交換器),對應 AMQP 標準定義的那幾個標準的 exchange 類型。分別叫做

  • amp.topic
  • amp.direct
  • amp.fanout

應用程序可以建立 queue 並綁定到這些默認的 exchange 上進行信息收發。不過在真實的應用環境下,人們往往需要更多的 exchange,queue 以及 binding 來滿足各種各樣的需求。或者在一些複雜的網絡中還需要配置 broker 的聯邦,即一個相互路由的 broker 網絡。

凡此種種都需要對 broker 進行各種配置,比如添加新的 exchange,queue,添加 broker 路由等等,這些便需要使用 Qpid 提供的各種管理工具。除配置管理之外,Qpid 的管理工具還提供了監控功能。常用的工具有三個:

  • Qpid-config
  • Qpid-route
  • Qpid-tool

Qpid-config 用來完成諸如添加 / 刪除 Queue,添加 / 刪除 Exchange 等關於 broker 內部的配置工作;Qpid-route 用來配置 broker Federation;Qpid-tool 用來對 Qpid Broker 進行實時監控。羅列他們的 help 信息對讀者沒有意義,我將在後面的例子中演示他們的一些用法。

程序代碼的基本框架

在一個採用了消息中間件的通信體系中有三個基本的角色,一個是發送消息的進程,一個是接受消息的進程,他們彼此之間通過 broker 連接起來,傳遞消息。

圖 2. 基本 Qpid 通信系統的幾個組件

圖 2. 基本 Qpid 通信系統的幾個組件

Broker 無需編寫,如前所述,Qpid 實現了兩種 Broker,您只需要根據需要啓動其中之一既可。Sender 和 Receiver 則是需要用戶編寫的應用程序。這兩類程序都有一些基本的框架,在此簡要介紹一下。

首先他們都是 client,需要和 broker 進行連接。鏈接成功後便生成一個會話 Session。基本代碼如下:

清單 1. 基本的 Qpid 程序框架

在 0.5 版本之前,QPID 編程接口和 AMQP 的基本概念一一對應,比如需要創建 queue,exchange,用 routing key 進行綁定,等等。必須對 AMQP 的模型完全理解才能自如地動手寫程序。

新的 Messaging API 將複雜的 AMQP 的細節全部都隱藏了起來,極大地簡化了編程。

應用程序從而可以將注意力專注於如何處理他們接收到或者將要發出的消息本身,將 AMPQ 模型處理的細節交給 Qpid 庫。如圖 2 所示,在一個消息通信系統中只有 3 個基本角色,除了 broker 之外,就只有一個 Sender 一個 Receiver。應用程序看不到 Exchange 或者 Queue 這些細節。與此相應,在 Qpid 的 Messaging API 編程接口中,只有兩個基本對象:Sender 和 Receiver。Sender 類,或者叫 Producer 生產者。即消息的發送方。Receiver 類自然就是信息的接收者,也叫做 Consumer。

這種抽象帶來更好的功能可擴展性:各種各樣的通信模型都經由修改 Sender 和 Receiver 的地址來實現,當需要修改通信模型時,也只需要修改 Address 和 broker 的配置,而無需修改應用的代碼。

下面將詳細介紹 Address 類。

Address 地址

寫信的時候,人們需要地址。類似地在 Qpid 中,表示消息的目的地,或者源。

Qpid Address 表示一個節點,有兩種節點:一種是 queue,另外一種是 topic。Queue 節點能夠緩存消息,直到被讀取走爲止;而 topic 節點則即時進行轉發,比如假如有 4 個 consumer 對某消息感興趣,當消息到達節點時,有 3 個 consumer 正在運行,那麼 topic 節點會將消息轉發給這 3 個 consumer,然後就將該消息丟棄。剩下的那個 consumer 再運行時,則收不到這個消息。

Qpid 的地址 Address 是一個帶格式的字符串,其語法如下:

其中 address,subject 和 key 都是字符串。

Subject 類似 email 的主題。每個消息都可以有一個主題,接收者可以通過主題對消息進行過濾。

Option 的具體含義有點兒複雜,可以參考 Qpid 的編程手冊獲取完整的描述。

瞭解了以上這些概念,就可以開始具體的編程了。和學習其他技術一樣,我們從研究例子程序開始。Qpid 源代碼包的 example 目錄下有大量的例子程序,Messaging 目錄下面是新的 Message API。我們主要研究 Message API 提供的 Spout 和 Drain 這兩個例子程序。

Spout 和 Drain 的代碼

將 Spout 的主要代碼精簡一下如下:

清單 2. Spout 代碼

可以看到 spout 首先用命令行參數 Address 初始化一個 Sender 對象,然後用 Sender 的 send 方法發送消息。

對 Drain 做一些類似的事情:

清單 3. Drain 代碼

Drain 接收消息,用命令行參數中的 Address 初始化一個 Receiver 對象,然後調用 Receiver 的 fetch() 方法接收消息。收到消息後需要調用 session 的 acknowledge() 方法確認。

點對點通信小例子

PTP 通信類似寫信。

其一,這種通信是異步的,人們把信發出去之後並不清楚何時能送到收信人的手中。在 Qpid 中,Sender 將消息發給 Broker,並不要求 Receiver 在消息發送的時候也有一個和 Broker 的鏈接並準備接受該消息。Sender 只管將消息發給 Broker,就可以放手去做其他的事情了;

其二,信是唯一的,您寫給朋友的信一定不希望其他人也收到吧。在 Qpid 的 PTP 通信中,一個 Receiver 收到消息後,該消息就被消除,其他 Receiver 不能再收到。

下面用例子來說明這種通信模型。首先要建立一個 Queue 節點。如之前在 Address 一節所講,Qpid 目前有兩種 Address,一種叫做 Queue,一種叫做 Topic,我們這裏就要用 Queue 這種節點。Queue 節點滿足前面所說的兩個重要的 PTP 通信的特徵,存儲轉發和只接收一次。

創建一個 queue:

現在我們建立了一個叫做 hello-world 的 queue。

用 spout 發送消息給地址 hello-world:

這就相當於將信發給了 hello-world。您已經看到,此時接收者 drain 還沒有啓動,但 Queue 的存儲轉發特性保證 drain 還是可以收到這條消息:

當我們打開另外一個 shell 窗口執行 drain,會發現不會再收到這條消息了。

Browse 模式 vs Consume 模式

一個有趣的的例子是如果我們修改一下 Address 的 Option,上面的通信模型就變成另外一種樣子。之前我們看到,第二次執行 ./drain hello-world 將得不到任何信息,因爲信息已經被第一次執行 ./drain 消費掉了。或者說這個 message 已經從 queue 裏面移除了。這在 Qpid 中被稱爲消費模式 (Consume)。

有時候人們可能需要另外一種模式,叫做 Browse,即瀏覽。正如我們瀏覽網頁上的新聞一樣,一條新聞並不會因爲第一個人閱讀了它之後就被消費掉,從網頁中消失了。而是一直在那裏供人瀏覽。假如我們希望實現類似這種通信模式,不需要修改 spout 和 Drain 的代碼,只需要稍微修改 Address 即可。Address 的選項 mode 可以用來設置 Browse 和 Consume 模式。如下例所示:

建立一個 queue。

發送了三條消息,接着我們用 drain 來接收這些消息吧。請注意,我們對 Address 字符串進行了小小的修改,在名字之後加了一個分號,後面用花括號添加了一個 mode 選項,並設置該地址爲 Browse 模式。

僅僅修改了AddressOption,我們就發現用spoutDrain可以實現另外一種通信模型了,這真是非常令人着迷的一個特性啊。

編寫 sub-pub 通信的例子

Pub-sub 是另一種很有用的通信模型。恐怕它的名字就源於出版發行這種現實中的信息傳遞方式吧,publisher 就是出版商,subscriber 就是訂閱者。

這種模型的特點在於:其一,消息可以根據訂閱的信息而轉發給不同的訂閱者;其二,消息並不存儲,broker 收到消息後立即將其轉發給當時正在註冊的訂閱者,假如某個訂閱者當時並沒有鏈接到 broker,那麼它就不能再收到該消息了。沒有多少人願意購買幾天前的舊報紙吧?這是和 Queue 的一個區別。

創建一個 Topic 節點:

還是用 spout 和 drain 來演示,先運行 spout:

再運行 drain

哦,什麼也沒有收到。這說明消息沒有被 broker 緩存。

Pub-sub 的主要優點在於訂閱消息的靈活性,broker 會根據消息的主題分發給不同的 subscriber。比如我們創建一個 news-service 的 exchange:

打開兩個 shell 窗口,一個運行 drain 並訂閱 news-service/sport,體育新聞;另一個訂閱 news-service/ent 娛樂新聞:

可以看到不同的消息被自動分發給不同的訂閱者。第一個 shell 接收 sport 的 drain 將打印:

可另外一個接收 news 的 drain 程序將打印 :

編寫 Request/Response 模型的應用

在很多 P2P 和 Pub-Sub 應用中,Sender 和 Reciever 可以見面也不相識。他們多數情況下根本不關心對方是否存在。然而在現實中還有一種典型的通信模型:Request/Response。這種模型由 client 和 server 兩部分組成,即人們常說的 C/S 模型。

Server 必須知道是誰發送了請求,以便回覆給正確的 Requester。這是通過解析 Requester 發過來的消息中的 ReplyTo 字段得到的。

代碼清單 4 展示的是 Server 的例子代碼。

清單 4. Server 代碼

代碼清單 5 展示的是 Client 的例子代碼。

清單 5. Client 代碼

在 client 代碼中,我們需要調用 Message 的 setReplyTo 方法,設置回覆的地址。

代碼清單展示的是 Client 的例子代碼。


小結

至此,我們看到了 Qpid 最基本的一些使用方法。演示了人們通常所使用的兩類通信模式,傳統的面向消息的中間件就實現了這兩個通信模型。但 Qpid 提供了一種更簡潔靈活的編程接口,僅通過修改 Address,無需修改代碼就可以改變應用程序的通信模型。

Qpid 是一個 AMQP 的實現,這意味這它不是一個私有的產品,使用 Qpid,您可以和其他任何符合 AMQP 協議的軟件系統進行通信。

不過假如這就是 Qpid 的全部,相信您一定也不以爲然,如果這些還不足以打動您,那麼我力圖在下一部分中向您介紹 Qpid 的一些高級特性。

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