使用 asyncio 及时更新一些数据并通过 aiohttp 呈现?

use asyncio update some data timely and present via aiohttp?

我正在尝试编写一个片段来研究 Python asyncio。基本思路是:

  1. 使用 "simple" 网络服务器 (aiohttp) 向用户呈现一些数据

  2. 给用户的数据return会及时更改

代码如下:

import asyncio
import random
from aiohttp import web

userfeed = [] # the data suppose to return to the user via web browsers 

async def data_updater():  #to simulate data change promptly
    while True:
        await asyncio.sleep(3)
        userfeed = [x for x in range(random.randint(1, 20))]
        print('user date updated: ', userfeed)


async def web_handle(request):
    text = str(userfeed)
    #print('in handler:', text)  # why text is empty?
    return web.Response(text=text)

async def init(loop):
    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', web_handle)
    srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8000)
    print('Server started @ http://127.0.0.1:8000...')
    return srv

loop = asyncio.get_event_loop()
asyncio.ensure_future(data_updater())  
asyncio.ensure_future(init(loop))
loop.run_forever()

问题是,代码是 运行 (python 3.5),但是 userfeed 在浏览器和 web_handler() 中总是空的 :-(

  1. 为什么userfeed没有更新?
  2. 关于这个timely date update函数,因为后面更新机制可能比较复杂,说可能涉及到async IO wait,有没有更好的方法代替data_updater()中的while True: await asyncio.sleep(3)获取 "more roughly precise" 计时器?

主要问题是您忘记了 data_updaterweb_handle 函数中的语句 global userfeed。因此,根据 how python resolves scopes, in web_handle it was referring to the global variable you defined and in data_updater to a local one, created by the statement userfeed = [x for x .... Using global variables in this context is explicitly discouraged,有一个示例使用 aiohttp.web.Application 对象的 dict 接口在函数之间安全地引用您的变量。

import asyncio
import random
from aiohttp import web


async def data_updater(app):
    while True:
        await asyncio.sleep(3)
        app["userfeed"] = [x for x in range(random.randint(1, 20))]

async def web_handle(request):
    userfeed = request.app["userfeed"]
    return web.Response(text=str(userfeed))

async def init(loop, port=8000):
    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', web_handle)
    handler = app.make_handler()
    srv = await loop.create_server(
        handler, '127.0.0.1', port=port)
    return srv, app, handler

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    srv, app, handler = loop.run_until_complete(init(loop, 8000))
    app['userfeed'] = []
    asyncio.ensure_future(data_updater(app))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        srv.close()
        loop.run_until_complete(srv.wait_closed())
        loop.run_until_complete(app.shutdown())
        loop.run_until_complete(handler.finish_connections(60.0))
        loop.run_until_complete(app.cleanup())
    loop.close()

当您在 127.0.0.1:8000 刷新页面时,您应该有一些新的随机数,因为它们在服务器端每 3 秒更新一次(您可以在 [=12= 中放回 print 语句] 来验证)。