让Redis 异步: 即时返回数据并异步更新数据

Redis return stale data and update data Asynchronously

常规的存取redis

#!/usr/bin/env python
# coding=utf-8
import os
import sys
import redis
from binascii import crc32
import json
import logging

root_path = [os.path.dirname(os.path.dirname(os.path.abspath(__file__)))]
sys.path += root_path


class RedisCache(object):

    redis_pool = None

    def __init__(self, hosts=['localhost'], duration=600):
        self.duration = duration
        self.redis_pool = self.redis_pool or [redis.Redis(connection_pool=redis.ConnectionPool(host=h, port=p, db=d), socket_timeout=0.5) for h,p,d in hosts]

    def _get_redis(self, key=None):
        if not key:
            return self.redis_pool[0]
        idx = crc32(key) % len(self.redis_pool)
        return self.redis_pool[idx]

    def get_value(self, key):
        v = self._get_redis(key).get(key)
        if v:
            try:
                l = json.loads(v)
            except Exception, e:
                raise e
            else:
                return l

        return None

    def set_value(self, key, value, duration=60):
        self._get_redis(key).set(key, json.dumps(value))
        self._get_redis(key).expire(key, int(duration or self.duration))

    def delete(self, key):python
        self._get_redis(key).delete(key)

Stale Redis

设置stale时段,在此期间的get请求即时返回数据之后,通过异步任务更新数据
这里用了 tornado.ioloop; 任意语言的的异步机制当然没有问题

from tornado.ioloop import IOLoop

class StaleRedisCache(RedisCache):
    def __init__(self, hosts=['localhost'], duration=600, stale=100):
        self.stale = stale
        RedisCache.__init__(self, hosts, duration)
    def get_value(self, key, callback=None, *args, **kwargs):
        l = None
        r = self._get_redis(key)
        res = r.pipeline().ttl(key).get(key).execute()
        if res[1]:
            try:
                l = json.loads(res[1])
            except Exception, e:
                raise e

        if not res[0] or res[0] < self.stale and callback:
            def func():
                value = callback and callback(*args, **kwargs)
                logging.info("set_value for key %s" % key)
                r.pipeline().set(key, json.dumps(value)).expire(key, kwargs.get('duration', self.duration)).execute()
                return value
            # redis-cli版本不同,res[0] 可能为 None(<=2.6.*) or -2(>=2.6)
            if not res[0] or res[0] == -2:
                return func()

            IOLoop.current().add_timeout(IOLoop.current().time(), func)

        return l

    # 这里已经不需要单独的 set_value 接口,因为再 get_value 中已经实现
    def set_value(self, key, value, duration=60):
        pass

使用场景

stale_cache = StaleRedisCache([('localhost', 6379, 0)])

def sc_callback:
    return sth.from.mongo or bla bla bla...

def abc:
    value = stale_cache.get_value('key1', sc_callback)

有没有很简洁呢~

你可能感兴趣的:(让Redis 异步: 即时返回数据并异步更新数据)