Flaskアプリケーションでビューを楽にキャッシュする方法はないかと探していた時、同僚にFlask-Cachingを紹介されました。
Flask-Cachingを使ってRedisにキャッシュする方法について整理します。
Flask-Caching
Flask-Cachingは以下の特徴があり、Flaskアプリケーションに容易に導入できます (公式ドキュメント) 。
- デコレータで簡単にキャッシュの設定・定義ができる
- Redis、memcached、ファイルなど、複数の種類のキャッシュミドルウェアを同じインタフェースで利用できる
Redisの構築 (準備)
キャッシュ先として利用するRedis環境をDockerで構築しておきます。
> docker run -d -p 6379:6379 redis --requirepass pass1234
サンプルアプリケーションの実装 (準備)
必要なパッケージをインストールしておきます。
> pip install flask==1.0.2 Flask-Caching==1.4.0 redis==2.10.6
今回題材とするFlaskアプリケーションの実装は以下です。
/<key>/<value>
にPOSTリクエストすると、dictオブジェクトに保存されます (ここではグローバル変数DICTIONARY
、実際のサービスではDBなどが使われるでしょう)/<key>
にGETリクエストすると、永続化層からkeyの値を取得して返します
FLASK_APP=run.py ./venv/bin/flask run
で起動します。
from flask import Flask DICTIONARY = {} # DBでもなんでも良いです app = Flask(__name__) @app.route('/<key>') def get_value(key): print(f'get_value({key})') return f'key={key}, value={DICTIONARY[key]}' @app.route('/<key>/<value>', methods=['POST']) def set_value(key, value): print(f'set_value({key},{value})') DICTIONARY[key] = value return 'OK'
Flask-Cachingの導入
それではFlask-Cachingをアプリケーションへ導入します。目的は/<key>
へのGETアクセスのレスポンスをキャッシュすることです。ポイントは2点です。
- Cacheインスタンスを作成します
- 上で構築したRedisサーバの設定をconfigに渡し、Flaskインスタンス (app) と紐付けます
- 設定項目の一覧は、こちら
- キャッシュしたいハンドラをデコレータ (
@cache.cached()
) で指定します
from flask import Flask from flask_caching import Cache DICTIONARY = {} # DBでもなんでも良いです app = Flask(__name__) # Cacheインスタンスの作成 cache = Cache(app, config={ 'CACHE_TYPE': 'redis', 'CACHE_DEFAULT_TIMEOUT': 60, 'CACHE_REDIS_HOST': 'localhost', 'CACHE_REDIS_PORT': 6379, 'CACHE_REDIS_PASSWORD': 'pass1234', 'CACHE_REDIS_DB': '0' }) @app.route('/<key>') @cache.cached(timeout=30) # 30秒間レスポンスをキャッシュする def get_value(key): print(f'get_value({key})') return f'key={key}, value={DICTIONARY[key]}' # 以下略
起動して、/key1/1
にPOSTしてデータを作り、/key1
にGETすると値1が取得できます。このときキャッシュが行われます。
最初のアクセスではprintで出力されますが、2回目以降のアクセスでは出力されません。30秒 (timeoutの設定値) が経過後に、GETリクエストするとキャッシュは捨てられるのでDICTIONARYから取得してビューが生成されます (print文も出力されます) 。
GET後にRedisに接続して中を見ると、キーflask_cache_view//key1
に対して、値!\x80\x03X\x11\x00\x00\x00key=key1, value=1q\x00.
が作られていることがわかります。
キーは、デフォルトではflask_cache_view/
+ パスとなっています。この内、flask_cache_
はCacheインスタンス作成時の設定項目の一つ、CACHE_KEY_PREFIXで変更できます。
127.0.0.1:6379> KEYS * 1) "flask_cache_view//key1" 127.0.0.1:6379> GET flask_cache_view//key1 "!\x80\x03X\x11\x00\x00\x00key=key1, value=1q\x00."
cachedデコレータにはいくつかパラメータがあります。
timeout
: キャッシュの有効期間 (秒)key_prefix
: その名の通りキーのプリフィックスで、デフォルトでは"view/リクエストパス"query_string
: Trueの場合、クエリパラメータが異なるリクエストは、クエリパラメータを含むハッシュ値をキーとしてキャッシュされます (デフォルトはFalseで、クエリパラメータが異なっても同一のキーが使われる)
キャッシュの削除
次はキャッシュを削除することを考えます。
上のままでは、GETしてキャッシュをした後30秒間は常にキャッシュの値が返されます。30秒以内にPOSTして値が更新されたとしても、30秒が経過するまで更新後の値を取得できません (古い値が返されます) 。
キャッシュの削除はCacheオブジェクトのdeleteメソッドで行います。POSTでキャッシュを削除するように変更したset_valueメソッドを示します。
Redisのキーは flask_cache_view//
+key という形式となっています。flask_cache_
(CACHE_KEY_PREFIX) の部分はdeleteメソッド内で補完されますので、cache.delete(f'view//{key}')
とすることでGETで設定されたキーを削除できます。
@app.route('/<key>/<value>', methods=['POST']) def set_value(key, value): print(f'set_value({key},{value})') DICTIONARY[key] = value cache.delete(f'view//{key}') return 'OK'
まとめ
FlaskでレスポンスをRedisにキャッシュする簡単な方法として、Flask-Cachingを紹介しました。