如果同一個請求會被多次調用,每次調用都會消耗很多資源,並且每次返回的內容都相同,就該使用緩存了
自定義緩存裝飾器
在使用Flask-Cache擴展實現緩存功能之前,我們先來自己寫個視圖緩存裝飾器,方便我們來理解視圖緩存的實現。首先,我們要有一個緩存,Werkzeug框架中的提供了一個簡單的緩存對象SimpleCache,它是將緩存項存放在Python解釋器的內存中,我們可以用下面的代碼獲取SimpleCache的緩存對象:
from werkzeug.contrib.cache import SimpleCache cache = SimpleCache()
如果你要使用第三方的緩存服務器,比如Memcached,Werkzeug框架也提供了它的wrapper:
from werkzeug.contrib.cache import MemcachedCache cache = MemcachedCache(['127.0.0.1:11211'])
此后就可以使用cache對象的”set(key, value, timeout)”和”get(key)”方法來存取緩存項了。注意”set()”方法的第三個參數”timeout”是緩存過期時間,默認為0,也就是永不過期。
def cached(timeout=5 * 60, key='view_%s'): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): cache_key = key % request.path value = cache.get(cache_key) if value is None: value = f(*args, **kwargs) cache.set(cache_key, value, timeout=timeout) return value return decorated_function return decorator
裝飾器的兩個參數分別是緩存的過期時間,默認是5分鍾;緩存項鍵值的前綴,默認是”view_”。然后我們寫個視圖,並使用此裝飾器:
@app.route('/hello') @app.route('/hello/<name>') @cached() def hello(name=None): print 'view hello called' return render_template('hello.html', name=name)
我們試下訪問這個視圖,對於同樣的URL地址,第一次訪問時,控制台上會有”view called”輸出,第二次就不會了。如果過5分鍾后訪問,”view called”才會再次輸出。
安裝和啟用Flask-Cache
了解了緩存裝飾器的內部實現,我們就可以開始介紹Flask的緩存擴展,Flask-Cache。首先使用pip將其安裝上:
pip install flask-cache
然后創建一個Flask-Cache的實例:
from flask import Flask from flask_cache import Cache app = Flask(__name__) cache = Cache(app, config={'CACHE_TYPE': 'simple'})
上例中,我們使用了’simple’類型緩存,其內部實現就是Werkzeug中的SimpleCache。我們也可以使用第三方的緩存服務器,比如Redis,代碼如下:
cache = Cache(app, config={'CACHE_TYPE': 'redis', # Use Redis 'CACHE_REDIS_HOST': 'abc.com', # Host, default 'localhost' 'CACHE_REDIS_PORT': 6379, # Port, default 6379 'CACHE_REDIS_PASSWORD': '111', # Password 'CACHE_REDIS_DB': 2} # DB, default 0
其內部實現是”werkzeug.contrib.cache”包下的”RedisCache”,所以說Flask-Cache就是基於Werkzeug框架的Cache庫實現的。
應用中使用緩存
同自定義緩存裝飾器一樣,我們可以用cache對象的”cached()”方法來裝飾視圖函數,以達到緩存視圖的目的:
@app.route('/hello') @app.route('/hello/<name>') @cache.cached(timeout=300, key_prefix='view_%s', unless=None) def hello(name=None): print 'view hello called' return render_template('hello.html', name=name)
“cache.cached()”裝飾器有三個參數:
- timeout:過期時間,默認為None,即永不過期
- key_prefix:緩存項鍵值的前綴,默認為”view/%s”
- unless:回調函數,當其返回True時,緩存不起作用。默認為None,即緩存有效
除了裝飾視圖函數,”cache.cached()”裝飾器也可以用來裝飾普通函數:
@cache.cached(timeout=50, key_prefix='get_list') def get_list(): print 'method get_list called' return ['a','b','c','d','e'] @app.route('/list') def list(): return ', '. join(get_list())
我們訪問”/list”地址時,第一次控制台上會有”method called”輸出,第二次就不會了,說明緩存起效了。裝飾普通函數時必須指定明確的”key_prefix”參數,因為它不像視圖函數,可以使用請求路徑”request.path”作為緩存項的鍵值。如果函數帶參數,對於不同的參數調用,都會使用同一緩存項,即返回結果一樣。Flask-Cache還提供了另一個裝飾器方法”cache.memoize()”,它與”cache.cached()”的區別就是它會將函數的參數也放在緩存項的鍵值中:
@cache.memoize(timeout=50) def create_list(num): print 'method create_list called' l = [] for i in range(num): l.append(str(i)) return l @app.route('/list/<int:num>') def list(num): return ', '.join(create_list(num))
我們再次訪問”/list”地址,對於不同的參數,”method called”會一直在控制台上打印出,而對於相同的參數,第二次就不會打印了。所以對於帶參數的函數,你要使用”cache.memoize()”裝飾器,而對於不帶參數的函數,它同”cache.cached()”基本上一樣。”cache.memoize()”裝飾器也有三個參數,”timeout”和”unless”參數同”cache.cached()”一樣,就是第二個參數”make_name”比較特別。它是一個回調函數,傳入的是被裝飾的函數名,返回是一個字符串,會被作為緩存項鍵值的一部分,如果不設,就直接使用函數名。
刪除緩存
對於普通緩存,你可以使用”delete()”方法來刪除緩存項,而對於”memoize”緩存,你需要使用”delete_memoized”方法。如果想清除所有緩存,可以使用”clear()”方法。
cache.delete('get_list') # 刪除'get_list'緩存項 cache.delete_many('get_list', 'view_hello') # 同時刪除'get_list'和'view_hello'緩存項 cache.delete_memoized('create_list', 5) # 刪除調用'create_list'函數並且參數為5的緩存項 cache.clear() # 清理所有緩存
Jinja2模板中使用緩存
其實在Jinja2模板中,我們還可以使用”{% cache %}”語句來緩存模板代碼塊:
{% cache 50, 'temp' %} <p>This is under cache</p> {% endcache %}
這樣”{% cache %}”和”{% endcache %}”語句中所包括的內容就會被緩存起來。”{% cache %}”語句的第一個參數是”timeout”過期時間,默認為永不過期;第二個參數指定了緩存項的鍵值,如果不設,鍵值就是”模板文件路徑”+”緩存塊的第一行”。上例中,我們設了鍵值是”temp”,然后在代碼中,我們可以這樣獲取緩存項實際的鍵值:
from flask_cache import make_template_fragment_key
key = make_template_fragment_key('temp')
打印出來看看,你會發現實際的鍵值其實是”_template_fragment_cache_temp”。如果你要刪除該緩存項,記得要傳入實際的鍵值,而不是模板上定義的’temp’。