使用asyncio更新一些数据及时通过aiohttp呈现?
我试图写一段代码来学习Python asyncio。其基本思想是:使用asyncio更新一些数据及时通过aiohttp呈现?
使用“简单的” Web服务器(aiohttp)一些数据呈现给用户
数据返回给用户将会改变及时
这里代码:
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()
:-(
- 为什么
userfeed
没有更新? - 关于这个
timely date update
函数,因为更新机制可能更复杂,以后说可能涉及异步IO等待,有没有更好的方法,而不是使用while True: await asyncio.sleep(3)
中的data_updater()
来得到“更精确”的定时器?
主要的问题是,你忘了声明global userfeed
两个data_updater
和web_handle
功能。因此,根据how python resolves scopes,在web_handle
中指的是您定义的全局变量,并且data_updater
指的是由语句userfeed = [x for x ...
创建的本地变量。在这种情况下使用全局变量显式为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秒服务器端更新(你可以把后面的print
声明data_updater
进行验证)。
似乎我犯了一个愚蠢的ABC错误:(非常感谢您的详细解释,真的很感谢 – user340307
很高兴帮助! – mgc
你想推动通知浏览你的网页的用户(没有任何他的行动)?如果是这样,你应该看看* websocket *技术,它在'aiohttp'中以一种非常简单的方式实现(有[示例](https://github.com/KeepSafe/aiohttp/blob/master/ examples/web_ws.py)在源代码中)。否则,你应该使用你的['app'对象](http://aiohttp.readthedocs.io/en/stable/faq.html#id3)(它提供了一个'dict'接口)来在函数之间传递'userfeed'变量并避免使其成为全球性的。 – mgc
@ mgc,谢谢你的回复。我不希望数据在用户的浏览器中自动刷新,只需一个客户端拉模型就足够了 - 每当用户通过浏览器或wget等工具打开URL时,最新的数据就会显示出来。这里的混淆是,为什么全局变量没有被更新(在'web_handler()'print内部显示它总是[],但是'data _updater()'打印它会改变)? async def与普通def不同,它会以某种方式缓存上下文吗? – user340307
查看我的答案的一个示例,因为在此上下文中使用全局变量明显不鼓励[documentation](http://aiohttp.readthedocs.io/en/stable/web.html#data-sharing-aka-no-单身-请)。但是,如果你真的需要使用全局'userfeed'来工作,只需在'data_updated'和'web_handle'函数中添加'global userfeed',它就可以工作(这样函数就知道'userfeed'指向一个变量位于全局范围内,在您的示例中,每个函数都有自己的本地'userfeed')。 – mgc