我們知道對Openstack的各個組件(nova,cinder,neutron等)來說,跨組件交互時使用的是RestAPI相互調用公共接口,組件內部各個進程間通信時使用RPC消息通信,從而實現各組件、各進程之間的解耦。Openstack RPC(Remote Producer Call)機制基于AMQP(Advanced Message Queuing Protocol)協議,搭配各種消息服務器(RabbitMQ,Qpid等)實現各個組件內部進程間的消息傳遞。
AMQP
AMQP是一個提供統一消息服務的應用層標準協議,基于此協議的客戶端與消息中間件可傳遞消息,并不受客戶端/中間件不同產品、不同開發(fā)語言等條件的限制,實現異步通信。
- RPC.call:發(fā)送請求到消息隊列,等待返回最終結果。
- RPC.cast:發(fā)送請求到消息隊列,不需要等待最終返回的結果。

圖2.AMQP模型

- Publisher:消息發(fā)送者,將消息發(fā)送到 Exchange。
- Message:由Header和Body組成,Header是由Publisher添加的各種屬性的集合,包括Message是否被持久化、由哪個Message Queue接受(Routing Key)、優(yōu)先級是多少等。而Body是真正需要傳輸的數據,它是對Server不可見的二進制數據流,在傳輸過程中不受影響……
- Exchange:接收發(fā)布應用程序發(fā)送的消息,并根據Routing Key和Binding信息、Exchange Type等參數將這些消息路由到消息隊列。
- Binding:關聯Exchange和Message Queue,提供路由規(guī)則。
- Message Queue:存儲消息,直到這些消息被消費者安全處理完為止。
- Consumer:消息接受者,從 Message Queue 獲取消息。
使用這個模型我們可以很容易的模擬出存儲轉發(fā)隊列和主題訂閱這些典型的消息中間件概念。
AMQP是非對稱的,客戶端生產和消費消息,服務器存儲和路由這些消息。一個AMQP服務器類似于郵件服務器,Exchange類似于消息傳輸代理(email里的概念),Message Queue類似于郵箱。Binding定義了每一個傳輸代理中的消息路由表,發(fā)布者將消息發(fā)給特定的傳輸代理,然后傳輸代理將這些消息路由到郵箱中,消費者從這些郵箱中取出消息。
AMQP模型中不同類型的Exchange對應不同的routing算法:
- Direct Exchange:Point-to-Point 消息模式,Direct Exchange 根據 Routing Key 進行精確匹配,只有對應的 Message Queue 會接收到消息。
- Topic Exchange:Publish-Subscribe(Pub-sub)消息模式,Topic Exchange 根據 Routing Key 進行模式匹配,只要符合模式匹配的 Message Queue 都會收到消息。
- Fanout Exchange:廣播消息模式,Fanout Exchange 將消息轉發(fā)到所有綁定的 Message Queue。
Openstack RPC
Openstack RPC實現了AMQP協議中的請求應答和數據處理等中間流程,并提供了幾種發(fā)送AMQP消息的方法:
- RPC.call:發(fā)送請求到消息隊列,等待返回最終結果。
- RPC.cast:發(fā)送請求到消息隊列,不需要等待最終返回的結果。
圖3.Openstack中的RPC流程

Openstack每個組件都會連接消息服務器,一個組件可能是一個消息發(fā)送者Invoker(如API、Scheduler),也可能是一個消息接收者Worker(如compute、volume、network)。Invoker發(fā)送消息有兩種方式:同步調用rpc.call和異步調用rpc.cast,Worker接受并根據rpc.call的信息返回消息。
- Topic Publisher:該對象在進行rpc.call或rpc.cast時創(chuàng)建,每個對象都會連接同一個topic類型的交換器,消息發(fā)送完畢后對象被回收。
- Direct Publisher:該對象在進行rpc.call調用時創(chuàng)建,用于向消息發(fā)送者返回響應。該對象會根據接收到的消息屬性連接一個direct類型的交換器。
- Direct Consumer:該對象在進行rpc.call調用時創(chuàng)建,用于接收響應消息。每一個對象都會通過一個隊列連接一個direct類型的交換器(隊列和交換器以UUID命名)。
- Topic Consumer:該對象在內部服務初始化時創(chuàng)建,在服務過程中一直存在。用于從隊列中接收消息,調用消息屬性中指定的函數。該對象通過一個共享隊列或一個私有隊列連接一個topic類型的交換器。每一個內部服務都有兩個topic consumer,一個用于rpc.cast調用(此時連接的是binding-key為“topic”的共享隊列);另一個用于rpc.call調用(此時連接的是binding-key為“topic.host”的私有隊列)。
- Topic Exchange:topic類型交換器,每一個消息代理節(jié)點只有一個topic類型的交換器。
- Direct Exchange:direct類型的交換器,存在于rpc.call調用過程中,對于每一個rpc.call的調用,都會產生該對象的一個實例。
- Queue Element:消息隊列。可以共享也可以私有。routing-key為“topic”的隊列會在相同類型的服務中共享(如多個compute節(jié)點共享一個routing-key為“topic”的隊列)。
name:control_exchange的名稱由各個服務conf文件里的“control_exchange”項指定,默認名稱為openstack。
[DEFAULT] |
|
control_exchange =openstack |
(String) The default exchange under which topics are scoped. May be overridden by an exchange name specified in the transport_url option. |
RPC Calls
圖4.rpc.call消息流程

- Topic Publisher向消息隊列服務發(fā)送RPC請求,同時初始化一個Direct Consumer等待回復消息。
- Topic Exchange “control_exchange”根據routing key將消息分發(fā)到相應隊列后,對應的Consumer接收消息并觸發(fā)Worker任務。
- Worker任務完成后,初始化一個Direct Publisher向消息服務器發(fā)送一條回復消息。
- Direct Exchange將消息分派到對應的queue給等待的Direct Consumer接收處理。
RPC Casts
rpc.cast和call相比只是少了返回消息的部分,其余類似。
圖4.rpc.cast消息流程
圖4.rpc.cast消息流程

Notifications
Openstack中除了rpc.call和rpc.cast這兩種用于組件內部各進程間消息處理的功能外,還可以發(fā)送相應資源的操作通知到notifications隊列供計費和監(jiān)控等組件使用。
Openstack中除了rpc.call和rpc.cast這兩種用于組件內部各進程間消息處理的功能外,還可以發(fā)送相應資源的操作通知到notifications隊列供計費和監(jiān)控等組件使用。
要開啟組件的notify功能,需要設置服務conf文件中的notify driver配置項,支持多種通知發(fā)送方式,messaging和messagingv2發(fā)送rpc消息通知到指定的“topics”,“driver”默認空不開啟任何通知,“topics”不設定時仍使用control_exchange指定的topic。
[oslo_messaging_notifications] |
|
driver = [] |
(Multi-valued) The Drivers(s) to handle sending notifications. Possible values are messaging, messagingv2, routing, log, test, noop |
topics = notifications |
(List) AMQP topic used for OpenStack notifications. |
RPC.Notifier提供的通知消息有以下幾種:
- audit:審計類消息
- info:正常操作消息
- warn:告警類操作消息
- error:錯誤類操作消息
- critical:嚴重錯誤
- sample:sample消息
以create_volume為例,配置rabbitmq及messagingv2開啟通知時的流程如下:
圖5.notify流程


- 服務初始化時啟動的Consumer監(jiān)聽到隊列消息,分析并觸發(fā)Manager類的對應方法。
- Manager初始化并啟動任務流flow。
- flow中的NotifyVolumeActionTask調用utils模塊中的notify_about_xxx方法,該方法初始化oslo.messaging庫中的Notifier類,并調用info方法。
- Notifier.info方法根據conf文件里的notify driver名(driver = messagingv2)和messaging driver配置(rpc_backend = rabbit)動態(tài)加載MessagingV2Driver類和RabbitDriver驅動(命名空間對應關系在項目代碼文件setup.cfg中的entry points段指定)。
- RabbitDriver獲取服務器連接并調用kombu庫完成消息發(fā)送。
結束語
消息通訊機制是Openstack整個工作流程中非常重要的一部分,充分理解它的工作原理對認識和理解Openstack的設計理念,各組件間關系及組件內部處理過程等都有非常重要的意義。在實際使用中還可以結合具體環(huán)境搭配的不同消息服務器、命令行及可視化工具等進行概念對照以加深理解。
作者簡介
竇銳元,烽火云計算高級軟件工程師,五年Openstack相關開發(fā)設計工作經驗,對Openstack整個生命周期的多種組織架構和部署方式有深刻理解,目前專注于存儲特性設計開發(fā)和社區(qū)相關工作。