前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Ryu:模块间通信机制分析

Ryu:模块间通信机制分析

作者头像
SDNLAB
发布2018-04-03 13:25:16
1.1K0
发布2018-04-03 13:25:16
举报
文章被收录于专栏:SDNLABSDNLAB

Ryu是一款非常轻便的SDN控制器,在科研方面得到了广泛的应用。相比其他控制器,受益于Python语言,在Ryu上开发SDN应用的效率要远高于其他控制器。为了解决复杂的业务,有时需要在Ryu上开发多模块来协同工作,从而共同完成复杂的业务。本文将介绍Ryu模块之间通信,包括Context等方式的多种通信方式。

_CONTEXTS

在RyuApp类中有一个属性是\_CONTEXTS。\_CONTEXTS中的内容将作为当前模块的服务在模块初始化时得到加载。示例如下:

代码语言:javascript
复制
_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"]

在模块启动时,首先会将\_CONTEXTS中的模块先启动,在模块的初始化函数中可以通过self.network_aware = kwargs["Network_Aware"]的形式获得该服务模块的实例,从而获取到该模块的数据,并具有完全的读写能力。这种模式很清晰地体现了模块之间的关系。然而在Ryu的实现中,这个机制并不完美,或者有所限制。首先,当某个模块作为别的模块的服务启动时,就无法在启动Ryu时手动启动。这种做法应该是出于保证模块启动顺序,从而顺利完成多模块启动而设计。另一方面,Ryu不支持多级的服务关系,如A是B的服务,那么B就不能作为其他模块的服务,也即这种服务关系只有两层。所以在设计模块时,若完全使用\_CONTEXTS方式来传递信息则需将架构设计成两层以内。若希望不受此限制,开发者可以自己修改其源码解除这个限制。

代码语言:javascript
复制
app\_manager.lookup\_service\_brick()

在某些业务场景,我们需要使用其他模块的数据,但是又不希望将对方作为自己的服务来加载,则可以通过app\_manager.lookup\_service\_brick('module name')来获取运行中的某个模块的实例,从而获取其数据。典型案例可以参考controller/controller.py中的Datapath类。示例如下:

代码语言:javascript
复制
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)

这种做法区别于import, import引入的是静态的数据,如某个类的函数的定义,静态数据的定义。当涉及到动态的数据,import则无法获取到对应的数据。如名为app的模块中有一个属性self.domain = Domain(),那么import可以获得其类的定义,而实际上,我们需要的是运行状态时Domain的实例,而import无法做到这一点。通过app = app\_manager.lookup\_service\_brick(‘app’)可以获得当前的app实例,进而通过app.domain来获取当前的domain实例的数据。

Event

通过事件系统来通信是模块之间通信的最普通的形式。每当交换机和Ryu建立连接,都会实例化一个Datapath对象来处理这个连接。在Datapath对象中,会将接收到的数据解析成对应的报文,进而转化成对应的事件,然后发布。注册了对应事件的模块将收到事件,然后调用对应的handler处理事件。示例如下:

代码语言:javascript
复制
[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

编译运行之后,simple\_switch\_13模块的\_packet\_in\_handler函数注册了事件ofp\_event.EventOFPPacketIn, 当Controller模块中的Datapath分发ofp\_event.EventOFPPacketIn事件时, 将会分发到\_packet\_in\_handler函数,在Datapath中调用handler(ev)来处理事件,从而完成了信息在模块之间的通信。

公共文件读写

除了以上的形式以外,某些数据的通信则通过读写公共文件完成。最典型的案例是oslo.config的使用。oslo是OpenStack的开源库。oslo.config提供一个全局的配置文件,同时也完成命令行的解析。通过读写公共文件的内容,可以完成信息的传递,如模块A将config中CONF对象的某个参数arg的i值修改为1, B再读取对应的参数arg,则可以获得数值1, 从而完成通信。面对配置信息等全局信息时,公共文件的使用可以避免不同模块之间的冲突,从而实现全局数据的统一。但是这种做法会频繁地读写文件,效率不高。且此类数据仅适合静态数据的传递,不适合存在于实例中的动态数据。

总结

在使用Ryu开发SDN网络应用的过程中,多模块协同工作是非常常见的场景。使用\_CONTEXTS形式可以更清晰地体现模块之间的关系,代码架构可读性更高;采用app\_manager.lookup\_service\_brick()形式可以得到运行的实例,可以达到\_CONTEXTS的效果,适用与仅需使用某模块某小部分功能集合,模块之间没有明显的服务关系的场景;Event是最普通的模块见通信,可以实现订阅发布模式的多模块协同工作场景,实现模块之间解耦;采用公共文件作为信息的中转站是最后的选择,效率比较低,适用于全局信息的传递。以上的几种方式是笔者在实验过程中总结的通信方式,若有错误指出,敬请指出,万分感谢。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2015-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 SDNLAB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档