作者 一个基于GAE memcache的Django cache middleware实现
Chris1919
2009-03-22 12:57
Django自带的cache framework没法直接在GAE上工作,大概要做一个custom cache backend,而我又不爱用app-engine-patch这样的东西。另外,Django cache framework有个方面没法满足我的需求,我希望网站有更新后,相关cache能够自动失效。于是自己写了个简单的cache middleware。

from google.appengine.api import users
from google.appengine.api import memcache

class CacheMiddleware(object):
    """
    This is a very simple middleware that add cache functionality
    """
    def process_request(self, request):
        request._cache_update_cache = False

        #only use cache for anonymous user
        if users.get_current_user():
            return None
        
        if request.method != 'GET':
            return None
            
        key = request.get_full_path()        
        try:
            response = memcache.get(key)
            if response is not None:
                return response
        except:
            pass
        
        request._cache_update_cache = True
        return None
          
    def process_response(self, request, response):
        if request.method == 'POST':
            memcache.flush_all()
        
        "Sets the cache, if needed."
        if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache:
            # We don't need to update the cache, just return.
            return response
        
        if request.method != 'GET':
            return response

        if not response.status_code == 200:
            return response
        
        key = request.get_full_path()        
        try:
            memcache.set(key, response)
        except:
            pass        
        
        return response
这个cache middleware只对匿名用户进行缓存。而且每当有POST请求后自动flush_all(flush_all本身的效率很好,时间复杂度为O(1)),这样会影响cache的命中率,不过对于不太频繁更新的站点来讲,这个影响很小。

由于有时候也需要对登录用户进行缓存,就写了个cache decorator,来处理对于特殊的view的缓存。
from google.appengine.api import users
from google.appengine.api import memcache

KEY_PAGE = 'PAGE_' 
COMMON_TIME_OUT = 3600

def check_page_cache(always=False):
    def decorator(method):
        def new_method(request, *args, **kwds):
            #only use cache for anonymous user
            if users.get_current_user() and not always:
                return method(request, *args, **kwds)
            
            if request.method != 'GET':
                return method(request, *args, **kwds)
        
            key = KEY_PAGE + request.get_full_path()
    
            #try to fetch data from cache
            try:
                response = memcache.get(key)
            except:
                response = None

            if response is not None:
                return response

            response = method(request, *args, **kwds)
            
            #only cache valid response
            if not response.status_code == 200:
                return response
            
            #try to fill data to cache
            try:
                memcache.set(key, response)
            finally:
                return response            
        return new_method
    return decorator
使用方法:
@check_page_cache(True)
view_function(request, ...)
    #your logic...
Bookmark and Share