模块间通信机制分析之Ryu篇

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

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

 [[148660]]

_CONTEXTS

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

_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方式来传递信息则需将架构设计成两层以内。若希望不受此限制,开发者可以自己修改其源码解除这个限制。

app\_manager.lookup\_service\_brick()

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

这种做法区别于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处理事件。示例如下:

[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是最普通的模块见通信,可以实现订阅发布模式的多模块协同工作场景,实现模块之间解耦;采用公共文件作为信息的中转站是***的选择,效率比较低,适用于全局信息的传递。以上的几种方式是笔者在实验过程中总结的通信方式,若有错误指出,敬请指出,万分感谢。

责任编辑:何妍 来源: SDNLAB
相关推荐

2014-01-06 17:13:59

ApacheMesos

2019-11-04 11:06:36

kubernetes通信组件

2017-08-07 20:18:11

Android线程handler

2015-09-23 16:14:03

Ryu拓扑结构

2015-07-27 14:57:32

OpenFlow协议Ryu

2021-09-17 14:43:54

鸿蒙HarmonyOS应用

2023-09-11 09:02:31

图形编辑器模块间的通信

2019-02-26 10:33:24

快应用

2021-12-06 14:52:08

动画Android补间动画

2024-02-20 19:53:57

网络通信协议

2022-04-24 15:07:09

GPS模块on函数鸿蒙

2011-08-18 10:59:57

iPhone开发消息通信NSNotificat

2023-06-08 08:21:08

多线程编程线程间通信

2020-07-28 08:54:39

内核通信Netlink

2021-02-26 08:17:57

操作系统信号量

2016-12-12 12:55:24

模块分析解决

2010-01-05 10:00:48

Linux进程间通信

2021-01-18 05:11:14

通信Nodejs进程

2022-01-12 14:45:26

鸿蒙HarmonyOS应用

2024-03-19 08:02:28

集群GaussDB指标
点赞
收藏

51CTO技术栈公众号