Ryu代码解析(二)

转载:http://geekwei.com/2014/12/01/ryu-source-analysis-2md/


Ryu事件处理函数的挂接方式分析

Ryu支持用户自定义事件处理函数,当该事件发生时,用户定义的处理函数会被自动的调用。那么这个机制具体是如何实现的呢?本篇博客就针对该问题,做一个简单的梳理,才疏学浅,欢迎指正!

首先来看RYU文档中的那个第一个APP程序:

from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls

class L2Switch(app_manager.RyuApp):
    def __init__(self, *args, **kwargs):
        super(L2Switch, self).__init__(*args, **kwargs)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        msg = ev.msg
        dp = msg.datapath
        ofp = dp.ofproto
        ofp_parser = dp.ofproto_parser

        actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
        out = ofp_parser.OFPPacketOut(
        datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
        actions=actions)
        dp.send_msg(out)


这段代码中有两个关键的地方,第一是L2Switch类继承了app_manager.RyuAPP类(这是自定义APP要求的)。第二个使用set_ev_cls装饰器装饰了我们自定义的事件处理函数packet_in_handler.

首先,我们来研究一下set_ev_cls装饰器到底做了些什么事情? set_ev_cls函数定义如下:

def set_ev_cls(ev_cls, dispatchers=None):
    def _set_ev_cls_dec(handler):
        if 'callers' not in dir(handler):
            handler.callers = {}
        for e in _listify(ev_cls):
            handler.callers[e] = _Caller(_listify(dispatchers), e.__module__)
        return handler
    return _set_ev_cls_dec

首先这个装饰器是带参数的装饰器,在使用@set_ev_cls修饰packet_in_hander函数是,实际的过程是:

packet_in_handler=set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)(packet_in_handler)

其实就是将packet_in_handler作为参数传入了_set_ev_cls_dec函数,做了一些事情之后,又把packet_in_handler返回回来。那么_set_ev_cls_dec函数做了什么呢,其实很简单。

 if 'callers' not in dir(handler): 
            handler.callers = {}
        for e in _listify(ev_cls):
            handler.callers[e] = _Caller(_listify(dispatchers), e.__module__)

首先判断handler函数中有没有callers属性,函数还可以有属性?是的,举个例子

def foo():
    print "I am a function"

foo.attr = 1
print foo.attr

结果会输出1.

因为一个handler可能会被多个装饰器装饰,所以先判断有没有该属性,没有的话新建一个字典。ev_cls就是传进来的ofp_event.EventOFPPacketIn类,OPF事件类是在不同的OF协议中实现的(如在OF1.4文件ofproto_1_4_parser中)。这里他把事件类转化为list,这里不懂为什么要转为为list,难道还有多个子事件?不过怎么样,这里将在caller属性中添加一个元素,key是事件类型,value是一个_Caller对象,该对象主要就是记下dispatchers,和这个事件的模块名。

class _Caller(object):
    """Describe how to handle an event class.
    """
    def __init__(self, dispatchers, ev_source):
        """Initialize _Caller.
        :param dispatchers: A list of states or a state, in which this
                            is in effect.
                            None and [] mean all states.
        :param ev_source: The module which generates the event.
                          ev_cls.__module__ for set_ev_cls.
                          None for set_ev_handler.
        """
        self.dispatchers = dispatchers
        self.ev_source = ev_source

到这里set_ev_cls装饰器就分析完了,回想一下它到底做了什么?其实很简单,就是给handler函数添加了一个属性callers,然后在callers中保存了该handler关心的事件类型和dispatchers。

其实到这里,并没有和系统运行过程中的消息路由关联起来,我们接着往下看。

还记得在代码分析(一)中讲的main函数么,该函数在加载了app之后,调用了instantiate_apps()函数,该函数对每个app进行初始化。

def instantiate_apps(self, *args, **kwargs):
    for app_name, cls in self.applications_cls.items():
        self._instantiate(app_name, cls, *args, **kwargs)

    self._update_bricks()
    self.report_bricks()

    threads = []
    for app in self.applications.values():
        t = app.start()
        if t is not None:
            threads.append(t)
    return threads

在该函数中,首先调用_instantiate实例化app,并且调用regeister_app()函数,

def register_app(app):
    assert isinstance(app, RyuApp)
    assert app.name not in SERVICE_BRICKS
    SERVICE_BRICKS[app.name] = app
    register_instance(app)

该函数有两行关键代码,第一将app加入 SERVICE_BRICKS中, SERVICE_BRICKS是一个全局变量,记录着所有的app实例,之后的代码中会用到它,暂且记住。之后调用register_instance(app)函数(该函数在handler.py中定义)
def register_instance(i):
for _k, m in inspect.getmembers(i, inspect.ismethod):

        # LOG.debug('instance %s k %s m %s', i, _k, m)
        if _has_caller(m):
            for ev_cls, c in m.callers.iteritems():
                i.register_handler(ev_cls, m)

def register_handler(self, ev_cls, handler):
    assert callable(handler)
    self.event_handlers.setdefault(ev_cls, [])
    self.event_handlers[ev_cls].append(handler)

该函数首先使用inspect遍历该app中的所有方法,然后通过_has_caller方法判断该方法是否有“caller”属性,上文中介绍了使用装饰器函数set_ev_cls时为每一个handler函数添加了caller属性,因此这里就是遍历所有的handler函数,将callers中保存的事件及函数(ev_cls,m)保存在app类的event_handlers属性中。

接下来调用_update_bricks函数, 这个函数非常重要

 def _update_bricks(self):
    for i in SERVICE_BRICKS.values():
        for _k, m in inspect.getmembers(i, inspect.ismethod):
            if not hasattr(m, 'callers'):
                continue
            for ev_cls, c in m.callers.iteritems():
                if not c.ev_source:
                    continue

                brick = _lookup_service_brick_by_mod_name(c.ev_source)
                if brick:
                    brick.register_observer(ev_cls, i.name,
                                            c.dispatchers)

                # allow RyuApp and Event class are in different module
                for brick in SERVICE_BRICKS.itervalues():
                    if ev_cls in brick._EVENTS:
                        brick.register_observer(ev_cls, i.name,
                                                c.dispatchers)

这里有一个技巧,其实c.ev_source如果是的module那么就是ofp_event,而OFPHandler类的名字正好就是'ofp_event'(在下文中会看到其实就是OpenFlowController),所以这里的brick就是OFPHandler,然后将将每种ev_cls的类型和app名字注册到该类中,其实本质上就是OFPHandler作为了一个消息源。这个函数非常重要,将每个app的handler与消息源OFPHandler建立了联系

还是没将其与系统消息路由挂起来啊!接着往下看

在main函数中ryu会将所有app都启动,启动后就进入_event_loop循环
def _event_loop(self):
while self.is_active or not self.events.empty():
ev, state = self.events.get()
if ev == self._event_stop:
continue
handlers = self.get_handlers(ev, state)
for handler in handlers:
handler(ev)。

其中一个特殊的app是opf_handler app,其重写了start函数,其实调用start后就是启动OpenFlowController类,OpenFlowController启动后,ryu开始监听来自交换机的新连接,当有一个新连接时,就调用datapath_connection_factory函数创建一个新的datapath对象,对象中保存着对端交换机的通信socket和address。如果交换机发来新的消息,该datapath对象在_recv_loop方法中就会获取该消息,

def _recv_loop(self):
    buf = bytearray()
    required_len = ofproto_common.OFP_HEADER_SIZE

    count = 0
    while self.is_active:
        ret = self.socket.recv(required_len)
        if len(ret) == 0:
            self.is_active = False
            break
        buf += ret
        while len(buf) >= required_len:
            (version, msg_type, msg_len, xid) = ofproto_parser.header(buf)
            required_len = msg_len
            if len(buf) < required_len:
                break

            msg = ofproto_parser.msg(self,
                                     version, msg_type, msg_len, xid, buf)
            # LOG.debug('queue msg %s cls %s', msg, msg.__class__)
            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)

            buf = buf[required_len:]
            required_len = ofproto_common.OFP_HEADER_SIZE

            # We need to schedule other greenlets. Otherwise, ryu
            # can't accept new switches or handle the existing
            # switches. The limit is arbitrary. We need the better
            # approach in the future.
            count += 1
            if count > 2048:
                count = 0
                hub.sleep(0)

首先通过lambda表达式定义了一个函数dispatchers,该函数获取handler的callers属性中ev事件的dispatchers。

接着使用生成器,首先opf_brick定义如下:

 self.ofp_brick = ryu.base.app_manager.lookup_service_brick('ofp_event')

还记得我们上边提到的SERVICE_BRICK么,lookup_service_brick('ofp_event')函数

def lookup_service_brick(name):
    return SERVICE_BRICKS.get(name)

ofp_brick就是ofp_hander对象,然后调用send_event_to_observers函数,将事件发送到关心该事件和该状态(由dispachers判断)的类中。

def send_event_to_observers(self, ev, state=None):
    """
    Send the specified event to all observers of this RyuApp.
    """

    for observer in self.get_observers(ev, state):
        self.send_event(observer, ev, state)

这里就用到了上文中提到的通过_update_brick函数填充的ovservers,然后通过send_event函数发送到每个ovserver的app的event队列中,然后每个app在其_event_loop中就可以获取该事件了!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值