我們知道對Openstack的各個(gè)組件(nova,cinder,neutron等)來說,跨組件交互時(shí)使用的是RestAPI相互調(diào)用公共接口,組件內(nèi)部各個(gè)進(jìn)程間通信時(shí)使用RPC消息通信,從而實(shí)現(xiàn)各組件、各進(jìn)程之間的解耦。Openstack RPC(Remote Producer Call)機(jī)制基于AMQP(Advanced Message Queuing Protocol)協(xié)議,搭配各種消息服務(wù)器(RabbitMQ,Qpid等)實(shí)現(xiàn)各個(gè)組件內(nèi)部進(jìn)程間的消息傳遞。
AMQP
AMQP是一個(gè)提供統(tǒng)一消息服務(wù)的應(yīng)用層標(biāo)準(zhǔn)協(xié)議,基于此協(xié)議的客戶端與消息中間件可傳遞消息,并不受客戶端/中間件不同產(chǎn)品、不同開發(fā)語言等條件的限制,實(shí)現(xiàn)異步通信!
- RPC.call:發(fā)送請求到消息隊(duì)列,等待返回最終結(jié)果。
- RPC.cast:發(fā)送請求到消息隊(duì)列,不需要等待最終返回的結(jié)果。
圖2.AMQP模型
在AMQP模型中,幾個(gè)主要功能模塊連接成一個(gè)處理鏈完成預(yù)期的功能:
- Publisher:消息發(fā)送者,將消息發(fā)送到 Exchange。
- Message:由Header和Body組成,Header是由Publisher添加的各種屬性的集合,包括Message是否被持久化、由哪個(gè)Message Queue接受(Routing Key)、優(yōu)先級是多少等。而Body是真正需要傳輸?shù)臄?shù)據(jù),它是對Server不可見的二進(jìn)制數(shù)據(jù)流,在傳輸過程中不受影響……
- Exchange:接收發(fā)布應(yīng)用程序發(fā)送的消息,并根據(jù)Routing Key和Binding信息、Exchange Type等參數(shù)將這些消息路由到消息隊(duì)列。
- Binding:關(guān)聯(lián)Exchange和Message Queue,提供路由規(guī)則。
- Message Queue:存儲消息,直到這些消息被消費(fèi)者安全處理完為止。
- Consumer:消息接受者,從 Message Queue 獲取消息。
使用這個(gè)模型我們可以很容易的模擬出存儲轉(zhuǎn)發(fā)隊(duì)列和主題訂閱這些典型的消息中間件概念。
AMQP是非對稱的,客戶端生產(chǎn)和消費(fèi)消息,服務(wù)器存儲和路由這些消息。一個(gè)AMQP服務(wù)器類似于郵件服務(wù)器,Exchange類似于消息傳輸代理(email里的概念),Message Queue類似于郵箱。Binding定義了每一個(gè)傳輸代理中的消息路由表,發(fā)布者將消息發(fā)給特定的傳輸代理,然后傳輸代理將這些消息路由到郵箱中,消費(fèi)者從這些郵箱中取出消息。
AMQP模型中不同類型的Exchange對應(yīng)不同的routing算法:
- Direct Exchange:Point-to-Point 消息模式,Direct Exchange 根據(jù) Routing Key 進(jìn)行精確匹配,只有對應(yīng)的 Message Queue 會接收到消息。
- Topic Exchange:Publish-Subscribe(Pub-sub)消息模式,Topic Exchange 根據(jù) Routing Key 進(jìn)行模式匹配,只要符合模式匹配的 Message Queue 都會收到消息。
- Fanout Exchange:廣播消息模式,F(xiàn)anout Exchange 將消息轉(zhuǎn)發(fā)到所有綁定的 Message Queue。
Openstack RPC
Openstack RPC實(shí)現(xiàn)了AMQP協(xié)議中的請求應(yīng)答和數(shù)據(jù)處理等中間流程,并提供了幾種發(fā)送AMQP消息的方法:
- RPC.call:發(fā)送請求到消息隊(duì)列,等待返回最終結(jié)果。
- RPC.cast:發(fā)送請求到消息隊(duì)列,不需要等待最終返回的結(jié)果。
圖3.Openstack中的RPC流程
Openstack每個(gè)組件都會連接消息服務(wù)器,一個(gè)組件可能是一個(gè)消息發(fā)送者Invoker(如API、Scheduler),也可能是一個(gè)消息接收者Worker(如compute、volume、network)。Invoker發(fā)送消息有兩種方式:同步調(diào)用rpc.call和異步調(diào)用rpc.cast,Worker接受并根據(jù)rpc.call的信息返回消息。
- Topic Publisher:該對象在進(jìn)行rpc.call或rpc.cast時(shí)創(chuàng)建,每個(gè)對象都會連接同一個(gè)topic類型的交換器,消息發(fā)送完畢后對象被回收。
- Direct Publisher:該對象在進(jìn)行rpc.call調(diào)用時(shí)創(chuàng)建,用于向消息發(fā)送者返回響應(yīng)。該對象會根據(jù)接收到的消息屬性連接一個(gè)direct類型的交換器。
- Direct Consumer:該對象在進(jìn)行rpc.call調(diào)用時(shí)創(chuàng)建,用于接收響應(yīng)消息。每一個(gè)對象都會通過一個(gè)隊(duì)列連接一個(gè)direct類型的交換器(隊(duì)列和交換器以UUID命名)。
- Topic Consumer:該對象在內(nèi)部服務(wù)初始化時(shí)創(chuàng)建,在服務(wù)過程中一直存在。用于從隊(duì)列中接收消息,調(diào)用消息屬性中指定的函數(shù)。該對象通過一個(gè)共享隊(duì)列或一個(gè)私有隊(duì)列連接一個(gè)topic類型的交換器。每一個(gè)內(nèi)部服務(wù)都有兩個(gè)topic consumer,一個(gè)用于rpc.cast調(diào)用(此時(shí)連接的是binding-key為“topic”的共享隊(duì)列);另一個(gè)用于rpc.call調(diào)用(此時(shí)連接的是binding-key為“topic.host”的私有隊(duì)列)。
- Topic Exchange:topic類型交換器,每一個(gè)消息代理節(jié)點(diǎn)只有一個(gè)topic類型的交換器。
- Direct Exchange:direct類型的交換器,存在于rpc.call調(diào)用過程中,對于每一個(gè)rpc.call的調(diào)用,都會產(chǎn)生該對象的一個(gè)實(shí)例。
- Queue Element:消息隊(duì)列。可以共享也可以私有。routing-key為“topic”的隊(duì)列會在相同類型的服務(wù)中共享(如多個(gè)compute節(jié)點(diǎn)共享一個(gè)routing-key為“topic”的隊(duì)列)。
name:control_exchange的名稱由各個(gè)服務(wù)conf文件里的“control_exchange”項(xiàng)指定,默認(rèn)名稱為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向消息隊(duì)列服務(wù)發(fā)送RPC請求,同時(shí)初始化一個(gè)Direct Consumer等待回復(fù)消息。
- Topic Exchange “control_exchange”根據(jù)routing key將消息分發(fā)到相應(yīng)隊(duì)列后,對應(yīng)的Consumer接收消息并觸發(fā)Worker任務(wù)。
- Worker任務(wù)完成后,初始化一個(gè)Direct Publisher向消息服務(wù)器發(fā)送一條回復(fù)消息。
- Direct Exchange將消息分派到對應(yīng)的queue給等待的Direct Consumer接收處理。
RPC Casts
rpc.cast和call相比只是少了返回消息的部分,其余類似。
圖4.rpc.cast消息流程
圖4.rpc.cast消息流程
Notifications
Openstack中除了rpc.call和rpc.cast這兩種用于組件內(nèi)部各進(jìn)程間消息處理的功能外,還可以發(fā)送相應(yīng)資源的操作通知到notifications隊(duì)列供計(jì)費(fèi)和監(jiān)控等組件使用。
Openstack中除了rpc.call和rpc.cast這兩種用于組件內(nèi)部各進(jìn)程間消息處理的功能外,還可以發(fā)送相應(yīng)資源的操作通知到notifications隊(duì)列供計(jì)費(fèi)和監(jiān)控等組件使用。
要開啟組件的notify功能,需要設(shè)置服務(wù)conf文件中的notify driver配置項(xiàng),支持多種通知發(fā)送方式,messaging和messagingv2發(fā)送rpc消息通知到指定的“topics”,“driver”默認(rèn)空不開啟任何通知,“topics”不設(shè)定時(shí)仍使用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:審計(jì)類消息
- info:正常操作消息
- warn:告警類操作消息
- error:錯(cuò)誤類操作消息
- critical:嚴(yán)重錯(cuò)誤
- sample:sample消息
以create_volume為例,配置rabbitmq及messagingv2開啟通知時(shí)的流程如下:
圖5.notify流程
- 服務(wù)初始化時(shí)啟動的Consumer監(jiān)聽到隊(duì)列消息,分析并觸發(fā)Manager類的對應(yīng)方法。
- Manager初始化并啟動任務(wù)流flow。
- flow中的NotifyVolumeActionTask調(diào)用utils模塊中的notify_about_xxx方法,該方法初始化oslo.messaging庫中的Notifier類,并調(diào)用info方法。
- Notifier.info方法根據(jù)conf文件里的notify driver名(driver = messagingv2)和messaging driver配置(rpc_backend = rabbit)動態(tài)加載MessagingV2Driver類和RabbitDriver驅(qū)動(命名空間對應(yīng)關(guān)系在項(xiàng)目代碼文件setup.cfg中的entry points段指定)。
- RabbitDriver獲取服務(wù)器連接并調(diào)用kombu庫完成消息發(fā)送。
結(jié)束語
消息通訊機(jī)制是Openstack整個(gè)工作流程中非常重要的一部分,充分理解它的工作原理對認(rèn)識和理解Openstack的設(shè)計(jì)理念,各組件間關(guān)系及組件內(nèi)部處理過程等都有非常重要的意義。在實(shí)際使用中還可以結(jié)合具體環(huán)境搭配的不同消息服務(wù)器、命令行及可視化工具等進(jìn)行概念對照以加深理解。
作者簡介
竇銳元,烽火云計(jì)算高級軟件工程師,五年Openstack相關(guān)開發(fā)設(shè)計(jì)工作經(jīng)驗(yàn),對Openstack整個(gè)生命周期的多種組織架構(gòu)和部署方式有深刻理解,目前專注于存儲特性設(shè)計(jì)開發(fā)和社區(qū)相關(guān)工作。