用python编写http server(3)-实现协程的服务器

一般的异步服务器基本都是NIO模式,将回调函数和fd注册在select/poll/epoll上面,当io事件发生时,执行回调函数,因此需要个loop来不断循环阻塞-执行。
查看asyncio的代码,可以看出基本符合NIO模式,但python通过生成器,避免了回调地狱,使得开发者可以用一种几乎同步的思维来写异步代码。
asyncio基本的class有 io_loop、future、task等。下面参考asyncio和tornado,自己编写个协程机制

首先是io_loop,io_loop应该不断的检查selector,当有io事件时执行回调函数

class IOLoop():
    def __init__(self):
        self.selector = selectors.DefaultSelector()
        self.alive = True
        self.pipe = os.pipe()

    def add_handler(self, fileobj, events, data):
        self.selector.register(fileobj, events, data)

    def remove_handler(self, fileobj):
        self.selector.unregister(fileobj)

    def run_forever(self):
        self.add_handler(self.pipe[0], selectors.EVENT_READ, self._wake_up)
        while self.alive:
            events = self.selector.select()
            for key, mask in events:
                if key.data:
                    key.data()

    def _wake_up(self):
        os.read(self.pipe[0], 1)

    def stop(self):
        # 简单化的退出逻辑
        self.alive = False
        os.write(self.pipe[1], b'.')

原版的io_loop远比这个复杂,但我们只是实现最基本的功能

然后是future,定义一个对象,代表以后会完成的结果。

class Future():

    def __init__(self):
        self._result = None
        self._callbacks = []

    def add_done_callback(self, fn):
        self._callbacks.append(fn)

    def set_result(self, result):
        self._result = result
        self._schedule_callbacks()

    def _schedule_callbacks(self):
        callbacks = self._callbacks[:]
        if not callbacks:
            return
        self._callbacks[:] = []
        for callback in callbacks:
            callback()

    def __iter__(self):
        yield self
        return self._result

最后是一个task,用来管理协程,不断触发协程执行下一步。

class Task:
    def __init__(self, coro):
        self.coro = coro
        self._step()

    def _step(self):
        try:
            future = self.coro.send(None)
        except StopIteration:
            return
        future.add_done_callback(self._step)

我们编写这样一个worker,其中关键函数是一个生成器

    def accept(self):
        f = Future()

        def on_accept():
            try:
                self.client, addr = self.sock.accept()
                print("connetc from ", addr)
            except BlockingIOError:
                return
            self.io_loop.remove_handler(self.sock)
            f.set_result(None)
        self.io_loop.add_handler(self.sock, selectors.EVENT_READ, on_accept)
        yield from f

用task来管理生成器:

    Task(worker.run())
    io_loop = get_event_loop()
    io_loop.run_forever()

task初始化协程,协程在需要操作io时,向io_loop注册事件和回调函数,返回future,future的callback设置为_step(当future完成时就会执行_step,使协程继续运行)。协程暂停执行,系统转而执行其他的代码,等到io事件触发时,loop执行之前的回调函数,即将future设置result。此时系统会执行future的回调函数,即task的_step,使协程继续运行。自此一个闭环形成了。
完整的代码在git:https://github.com/shallows2014/learning/blob/master/python/http_server/async_server/server.py

代码只是实现了简单的协程机制,实际会有很多问题,比如说不能创建多个task,因为task用的都是同一个sock,selector不能为同一个fd注册多次,解决方法很多,比如说,不使用协程而只是使用回调函数来处理accept,当遇到连接时创建新的对象,对象再通过协程来处理请求。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容