一个Redis Cache实现
来源:互联网 发布:淘宝网男装卫衣 编辑:程序博客网 时间:2024/06/07 15:48
需求
应用中需要通过HTTP调用远程的数据,但是这个获取过程需要执行较长时间,而且这个数据本身的变化也不频繁,这种情况最适合用一个cache来优化。
前两年在做短链接实现的时候,曾经用最好的语言PHP做过一个Redis cache实现《一个简单的Redis应用(修订版)》,但那个毕竟是一个特定的实现,而且我现在需要的是python版。
这次的目标是需要实现一个比较通用的cache,支持各种数据类型,有超时更新机制,超时更新需要有锁(防止前文那个例子里发生过的问题)。
代码(py3)
#!/usr/bin/env python# -*- coding: utf-8 -*-import hashlibimport picklefrom functools import wrapsfrom redis import Redisimport logging__author__ = 'raptor'logger = logging.getLogger(__name__)class RedisCache(object): MAX_EXPIRES = 86400 SERIALIZER = pickle LOCKER = set() def __init__(self, name, host='localhost', port=6379, db=0, max_expires=MAX_EXPIRES): self.db = Redis(host=host, port=port, db=db) self.name = name self.max_expires = max_expires def _getkey(self, *keys): return ":".join([self.name] + list(keys)) def _get_data(self, key): result = self.db.get(key) return None if result == b'None' else result def get(self, key): result = self._get_data(self._getkey(key)) return self.SERIALIZER.loads(result) if result is not None else result def set(self, key, value, ex=None): k = self._getkey(key) v = self.SERIALIZER.dumps(value) if ex is None: self.db.set(k, v) else: self.db.setex(k, v, ex) def delete(self, key): self.db.delete(self._getkey(key)) @staticmethod def build_key(name, *args, **kwargs): m = hashlib.md5() m.update(name.encode('utf-8')) m.update(pickle.dumps(args)) m.update(pickle.dumps(kwargs)) return m.hexdigest() def cached(self, key, func, ex=None): if ex is None: ex = self.max_expires min_ttl = self.max_expires - ex # ex <= 0 : force refresh data key = ":".join([self.name, key]) result = self._get_data(key) if key not in self.LOCKER: self.LOCKER.add(key) try: ttl = self.db.ttl(key) if ttl is None or ttl < min_ttl: result = func() if result is not None: result = self.SERIALIZER.dumps(result) self.db.setex(key, result, self.max_expires) finally: self.LOCKER.remove(key) try: result = self.SERIALIZER.loads(result) if result is not None else None except: pass return resultdef redis_cached(db, ex=None): def decorator(fn): @wraps(fn) def wrapper(*args, **kwargs): key = RedisCache.build_key(fn.__name__, *args, **kwargs) return db.cached(key, lambda: fn(*args, **kwargs), ex) return wrapper return decorator
用法
RedisCache本身也可以当一个Redis数据库对象使用,比如:
db = RedisCache('tablename', max_expires=3600) # tablename是一个是自定义的key前缀,可以用于当作表名使用。# 最大超时时间(max_expires)仅供cached使用,使用set时,如果不指定超时时间则永不超时db.set('aaa', {'key': 1234}, 7200) # value可以是作何可序列化数据类型,比如字典,不指定超时则永不超时db.get('aaa')['key'] # 结果为1234db.delete('aaa')
但这个不是重点,重点是cached功能。对于慢速函数,加上db.cached以后,可以对函数调用的结果进行cache,在cache有效的情况下,大幅提高函数在反复调用时的性能。
下面是一个例子,具体见代码中的注释:
db = RedisCache('tablename')def func(url, **kwargs): result = requests.get("?".join([url, urlencode(kwargs)])) return resulturl = 'https://www.baidu.com/s't = time()func(url, wd="测试")print(time()-t) # 较慢t = time()db.cached('test_cache', lambda: func(url, wd="测试"), 10)print(time()-t) # 第一次运行仍然较慢t = time()db.cached('test_cache', lambda: func(url, wd="测试"), 10)print(time()-t) # 从redis里读取cache很快sleep(11) # 等待到超时t = time()db.cached('test_cache', lambda: func(url, wd="测试"), 10)print(time()-t) # 超时后会再次执行func更新cachet = time()db.cached('test_cache_new', lambda: func(url, wd="新的测试"))print(time()-t) # 不同的调用参数用不同的key作cachet = time()
因为对于不同的函数调用参数,函数可能有不同的返回结果,所以应该用不同的key进行cache。为简单起见,可以把函数签名做一个HASH,然后以此为KEY进行cache。最后把这个操作做成一个decorator,这样,只需要给函数加上这个decorator即可自动提供所需要的cache支持。
最终的简单用法如下:
db = RedisCache('tablename')@redis_cached(db, 10)def func(url, **kwargs): result = requests.get("?".join([url, urlencode(kwargs)])) return resultt = time()func(url, wd="测试")print(time()-t)t = time()func(url, wd="测试")print(time()-t)sleep(11)t = time()func(url, wd="测试")print(time()-t)t = time()func(url, wd="新的测试")print(time()-t)
是不是简单得多了。
阅读全文
0 0
- 一个Redis Cache实现
- C++实现一个LRU Cache
- redis实现cache系统原理(一)
- redis实现cache系统实践(二)
- Spring Cache 集成 Redis 简单实现
- Spring Cache+Redis实现自定义注解缓存
- cache-redis
- 一个基于Cache+Repository+DAO的实现
- 如何用C++实现一个LRU Cache
- LRU cache的一个简单实现
- 使用C++实现一个LRU cache
- 设计并实现一个LRU Cache
- 如何设计实现一个LRU Cache?
- 设计并实现一个LRU Cache (java)
- java如何实现一个LRU Cache
- 如何用C++实现一个LRU Cache
- 如何设计实现一个LRU Cache?
- 如何用C++实现一个LRU Cache
- 7.5 数据降维
- idea2016 monokai配色
- 7.9 聚类模型评估
- 7.10 参数调优
- 爬虫+模拟登录
- 一个Redis Cache实现
- ubuntu 备份
- 文章标题
- Kotlin官方参考整理——03.类和对象1
- GuavaCache简介
- springMVC 拦截器
- 访问图像数据
- Java+Selenium3方法篇31-利用Robot类截图
- 第二周作业