如何缓存asyncio协程
我正在使用aiohttp在Python 3.4中创建一个简单的HTTP请求,如下所示:
response = yield from aiohttp.get(url)
应用程序一次又一次地请求相同的URL,所以很自然地我想缓存它。 我的第一次尝试是这样的:
@functools.lru_cache(maxsize=128)
def cached_request(url):
return aiohttp.get(url)
第一次调用cached_request
可以正常工作,但在以后的调用中,我最终得到了None
而不是响应对象。
我对asyncio比较陌生,所以我尝试了很多asyncio.coroutine
装饰器, yield from
和其他一些东西的组合,但没有一个可以工作。
那么缓存协程是如何工作的呢?
我自己写了一个简单的缓存修饰器:
def async_cache(maxsize=128):
cache = {}
def decorator(fn):
def wrapper(*args):
key = ':'.join(args)
if key not in cache:
if len(cache) >= maxsize:
del cache[cache.keys().next()]
cache[key] = yield from fn(*args)
return cache[key]
return wrapper
return decorator
@async_cache()
@asyncio.coroutine
def expensive_io():
....
这种工作。 但许多方面可能可以改进。 例如:如果在第一次调用返回之前第二次调用缓存的函数,它将再次执行。
也许有点晚,但我已经开始了一个可能有所帮助的新软件包:https://github.com/argaen/aiocache。 贡献/意见总是受欢迎的。
一个例子:
import asyncio
from collections import namedtuple
from aiocache import cached
from aiocache.serializers import PickleSerializer
Result = namedtuple('Result', "content, status")
@cached(ttl=10, serializer=PickleSerializer())
async def async_main():
print("First ASYNC non cached call...")
await asyncio.sleep(1)
return Result("content", 200)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
print(loop.run_until_complete(async_main()))
print(loop.run_until_complete(async_main()))
print(loop.run_until_complete(async_main()))
print(loop.run_until_complete(async_main()))
请注意,作为额外的,它可以使用Pickle序列化将任何python对象缓存到redis中。 如果你只是想处理内存,你可以使用SimpleMemoryCache
后端:)。
要与协程一起使用functools.lru_cache
,以下代码有效。
class Cacheable:
def __init__(self, co):
self.co = co
self.done = False
self.result = None
self.lock = asyncio.Lock()
def __await__(self):
with (yield from self.lock):
if self.done:
return self.result
self.result = yield from self.co.__await__()
self.done = True
return self.result
def cacheable(f):
def wrapped(*args, **kwargs):
r = f(*args, **kwargs)
return Cacheable(r)
return wrapped
@functools.lru_cache()
@cacheable
async def foo():
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.text()
以下是线程安全的
class ThreadSafeCacheable:
def __init__(self, co):
self.co = co
self.done = False
self.result = None
self.lock = threading.Lock()
def __await__(self):
while True:
if self.done:
return self.result
if self.lock.acquire(blocking=False):
self.result = yield from self.co.__await__()
self.done = True
return self.result
else:
yield from asyncio.sleep(0.005)
链接地址: http://www.djcxy.com/p/53223.html