Tornado实现的核心依据

Node.js也是异步,不过通过callback来组织代码,不便于阅读。Go语言跟Python在实现异步方面比较相近,使用所谓的协程(coroutine),这样可以按照正常的逻辑思维来书写代码。

Tornado框架的两个核心基础点:一个是python的yield,一个是Linux的epoll。

关于python的yield

python的yield是python一个特别的关键字,它将程序执行的权利交给函数调用者(yield只能出现在函数中),同时保留执行的环境,当返回的generator调用send()或者next()的时候,回到此前中断的位置,继续进行执行。就是所谓协程的语法支持。send()比next()可以在继续执行时传递一个值,这让x = yield 100这种写法成为可能。同时,generator必须以捕捉StopIteration异常来表征函数的结束,所以函数返回值不能使用return语法,很显示return的值,并不会自动传给send()函数,所以函数返回值需要raise出来,这点会让人感觉很奇怪。

下面的例子诠释了stackless的协程原理:

try:
    from backports_abc import Generator as GeneratorType
except ImportError:
    from types import GeneratorType

class Data(Exception):
    def __init__(self, data):
        self.data = data

def get_data():
    x = yield [42, 43] #实际会是一些IO相关的fd监听行为
    x[0] += 100
    raise Data(x)

def run():
    x = yield get_data()
    x[1] += 100
    raise Data({'arg1': x[0], 'arg2': x[1]})

def main(func):
    a = func()
    stack = [a]
    arg = None
    while True:
        try:
            if len(stack) == 0:
                return arg
            res = stack[-1].send(arg)
            if isinstance(res, GeneratorType):
                stack.append(res)
            else:
                arg = res
        except Data as d:
            stack.pop()
            arg = d.data
        except StopIteration:
            pass

res = main(run)
print(res)

函数调用必须是堆栈模式的,所以stackless写法必须自己创建一个stack来保留协程函数各中断点之间的次序关系,这时候栈里压的是各Yield Point而不是函数地址。上面例子中,Data类是用来传返回值的,并不是真正的异常。

关于epoll

整过网络编程的话,流行的epoll是绕不过去的,因为它对于多连接中少量活跃的情形非常高效,这里的连接可以是socket fd,也可以是任何IO对应的fd,这里就不多说了。

Tornado中的ioloop就是使用epoll进行各连接读写事件监听的核心,实现于start()方法中。它是一个大的死循环,由读写事件和Timer消息进行驱动,大多数时候,它是IO waiting状态,不消耗CPU,所以它是异步的。

结合上面的yield,一层一层yield下去,最终yield到一个需要监听读写的fd,这正是epoll需要的东西。

以上两点我认为就是Tornado框架实现的基础。

发表于 2016年07月21日 17:39   评论:0   阅读:2527  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo