模塊間通信機(jī)制分析之Ryu篇
Ryu是一款非常輕便的SDN控制器,在科研方面得到了廣泛的應(yīng)用。相比其他控制器,受益于Python語(yǔ)言,在Ryu上開(kāi)發(fā)SDN應(yīng)用的效率要遠(yuǎn)高于其他控制器。為了解決復(fù)雜的業(yè)務(wù),有時(shí)需要在Ryu上開(kāi)發(fā)多模塊來(lái)協(xié)同工作,從而共同完成復(fù)雜的業(yè)務(wù)。本文將介紹Ryu模塊之間通信,包括Context等方式的多種通信方式。
_CONTEXTS
在RyuApp類中有一個(gè)屬性是\_CONTEXTS。\_CONTEXTS中的內(nèi)容將作為當(dāng)前模塊的服務(wù)在模塊初始化時(shí)得到加載。示例如下:
_CONTEXTS = { "Network_Aware": network_aware.Network_Aware, "Network_Monitor": network_monitor.Network_Monitor, } def __init__(self, *args, **kwargs): super(Shortest_forwarding, self).__init__(*args, **kwargs) self.name = 'shortest_forwarding' self.network_aware = kwargs["Network_Aware"] self.network_monitor = kwargs["Network_Monitor"]
在模塊啟動(dòng)時(shí),首先會(huì)將\_CONTEXTS中的模塊先啟動(dòng),在模塊的初始化函數(shù)中可以通過(guò)self.network_aware = kwargs["Network_Aware"]的形式獲得該服務(wù)模塊的實(shí)例,從而獲取到該模塊的數(shù)據(jù),并具有完全的讀寫(xiě)能力。這種模式很清晰地體現(xiàn)了模塊之間的關(guān)系。然而在Ryu的實(shí)現(xiàn)中,這個(gè)機(jī)制并不***,或者有所限制。首先,當(dāng)某個(gè)模塊作為別的模塊的服務(wù)啟動(dòng)時(shí),就無(wú)法在啟動(dòng)Ryu時(shí)手動(dòng)啟動(dòng)。這種做法應(yīng)該是出于保證模塊啟動(dòng)順序,從而順利完成多模塊啟動(dòng)而設(shè)計(jì)。另一方面,Ryu不支持多級(jí)的服務(wù)關(guān)系,如A是B的服務(wù),那么B就不能作為其他模塊的服務(wù),也即這種服務(wù)關(guān)系只有兩層。所以在設(shè)計(jì)模塊時(shí),若完全使用\_CONTEXTS方式來(lái)傳遞信息則需將架構(gòu)設(shè)計(jì)成兩層以內(nèi)。若希望不受此限制,開(kāi)發(fā)者可以自己修改其源碼解除這個(gè)限制。
app\_manager.lookup\_service\_brick()
在某些業(yè)務(wù)場(chǎng)景,我們需要使用其他模塊的數(shù)據(jù),但是又不希望將對(duì)方作為自己的服務(wù)來(lái)加載,則可以通過(guò)app\_manager.lookup\_service\_brick('module name')來(lái)獲取運(yùn)行中的某個(gè)模塊的實(shí)例,從而獲取其數(shù)據(jù)。典型案例可以參考controller/controller.py中的Datapath類。示例如下:
self.ofp_brick = ryu.base.app_manager.lookup_service_brick('ofp_event') def set_state(self, state): self.state = state ev = ofp_event.EventOFPStateChange(self) ev.state = state self.ofp_brick.send_event_to_observers(ev, state)
這種做法區(qū)別于import, import引入的是靜態(tài)的數(shù)據(jù),如某個(gè)類的函數(shù)的定義,靜態(tài)數(shù)據(jù)的定義。當(dāng)涉及到動(dòng)態(tài)的數(shù)據(jù),import則無(wú)法獲取到對(duì)應(yīng)的數(shù)據(jù)。如名為app的模塊中有一個(gè)屬性self.domain = Domain(),那么import可以獲得其類的定義,而實(shí)際上,我們需要的是運(yùn)行狀態(tài)時(shí)Domain的實(shí)例,而import無(wú)法做到這一點(diǎn)。通過(guò)app = app\_manager.lookup\_service\_brick(‘app’)可以獲得當(dāng)前的app實(shí)例,進(jìn)而通過(guò)app.domain來(lái)獲取當(dāng)前的domain實(shí)例的數(shù)據(jù)。
Event
通過(guò)事件系統(tǒng)來(lái)通信是模塊之間通信的最普通的形式。每當(dāng)交換機(jī)和Ryu建立連接,都會(huì)實(shí)例化一個(gè)Datapath對(duì)象來(lái)處理這個(gè)連接。在Datapath對(duì)象中,會(huì)將接收到的數(shù)據(jù)解析成對(duì)應(yīng)的報(bào)文,進(jìn)而轉(zhuǎn)化成對(duì)應(yīng)的事件,然后發(fā)布。注冊(cè)了對(duì)應(yīng)事件的模塊將收到事件,然后調(diào)用對(duì)應(yīng)的handler處理事件。示例如下:
[module: controller] if msg: ev = ofp_event.ofp_msg_to_ev(msg) self.ofp_brick.send_event_to_observers(ev, self.state) dispatchers = lambda x: x.callers[ev.__class__].dispatchers handlers = [handler for handler in self.ofp_brick.get_handlers(ev) if self.state in dispatchers(handler)] for handler in handlers: handler(ev) [module:simple_switch_13.py] @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): msg = ev.msg datapath = msg.datapath
編譯運(yùn)行之后,simple\_switch\_13模塊的\_packet\_in\_handler函數(shù)注冊(cè)了事件ofp\_event.EventOFPPacketIn, 當(dāng)Controller模塊中的Datapath分發(fā)ofp\_event.EventOFPPacketIn事件時(shí), 將會(huì)分發(fā)到\_packet\_in\_handler函數(shù),在Datapath中調(diào)用handler(ev)來(lái)處理事件,從而完成了信息在模塊之間的通信。
公共文件讀寫(xiě)
除了以上的形式以外,某些數(shù)據(jù)的通信則通過(guò)讀寫(xiě)公共文件完成。最典型的案例是oslo.config的使用。oslo是OpenStack的開(kāi)源庫(kù)。oslo.config提供一個(gè)全局的配置文件,同時(shí)也完成命令行的解析。通過(guò)讀寫(xiě)公共文件的內(nèi)容,可以完成信息的傳遞,如模塊A將config中CONF對(duì)象的某個(gè)參數(shù)arg的i值修改為1, B再讀取對(duì)應(yīng)的參數(shù)arg,則可以獲得數(shù)值1, 從而完成通信。面對(duì)配置信息等全局信息時(shí),公共文件的使用可以避免不同模塊之間的沖突,從而實(shí)現(xiàn)全局?jǐn)?shù)據(jù)的統(tǒng)一。但是這種做法會(huì)頻繁地讀寫(xiě)文件,效率不高。且此類數(shù)據(jù)僅適合靜態(tài)數(shù)據(jù)的傳遞,不適合存在于實(shí)例中的動(dòng)態(tài)數(shù)據(jù)。
總結(jié)
在使用Ryu開(kāi)發(fā)SDN網(wǎng)絡(luò)應(yīng)用的過(guò)程中,多模塊協(xié)同工作是非常常見(jiàn)的場(chǎng)景。使用\_CONTEXTS形式可以更清晰地體現(xiàn)模塊之間的關(guān)系,代碼架構(gòu)可讀性更高;采用app\_manager.lookup\_service\_brick()形式可以得到運(yùn)行的實(shí)例,可以達(dá)到\_CONTEXTS的效果,適用與僅需使用某模塊某小部分功能集合,模塊之間沒(méi)有明顯的服務(wù)關(guān)系的場(chǎng)景;Event是最普通的模塊見(jiàn)通信,可以實(shí)現(xiàn)訂閱發(fā)布模式的多模塊協(xié)同工作場(chǎng)景,實(shí)現(xiàn)模塊之間解耦;采用公共文件作為信息的中轉(zhuǎn)站是***的選擇,效率比較低,適用于全局信息的傳遞。以上的幾種方式是筆者在實(shí)驗(yàn)過(guò)程中總結(jié)的通信方式,若有錯(cuò)誤指出,敬請(qǐng)指出,萬(wàn)分感謝。