まえがき
・Caffeine Cache をもう一段深堀り
・通常版は 【Java】Caffeineでお手軽にローカルキャッシュをご参考下さい。
・**ローディングキャッシュ(LoadingCache)**とは…
・リクエストの際は既存のキャッシュにあるものを返し、裏側で新しいデータを読み込み格納しておくキャッシュの事
長所
・重い処理の影響をリクエストの際に受けない
・初回を起動時に読み込んでおけば、あとは良しなにしてくれる
短所
・リクエストを返すと古い(現状の値ではない)値が取得される
・↑これを対処するにはScheduledExecutorServiceとかで定期的にロードするとかでしょうか
・定期的にロードするとリソース使うので一長一短な気がします
・バッチ等でテーブルにデータを入れるとか、起点となる処理が実行された際に裏で読み込むとか、色々と方法はありそうです
環境
spring framework
java 8
使い方
・ここではランキングのキャッシュを想定
宣言
private static @NonNull LoadingCache<String, Ranking> rankingCache = null;
初期化
・通常のキャッシュと異なるのは build の際にキャッシュローダーを渡すこと
・ローダーを起動することでキャッシュのリフレッシュをしています
・refreshAfterWrite を指定することで書き込み後の時間経過後にリフレッシュするようになります
・expireAfterWrite も別に指定することが可能
@PostConstruct
public void init() {
rankingCache =
Caffeine.newBuilder()
.maximumSize(64)
.refreshAfterWrite(10, TimeUnit.MINUTES)
.build(rankingCacheLoader());
}
読み出し関数
・引数は String: param1, String: startDate, String: endDate を想定(この引数からキーを作ります)
public Ranking getCachedRankingDto(String param1, String startDate, String endDate) {
Ranking result = new Ranking();
if (param1 == null || startDate == null || endDate == null) return result;
final String[] params = { param1, startDate, endDate };
final String cacheKey = String.join("-", params);
try {
result = rankingCache.get(cacheKey, key -> {
try {
return getRankingDto(param1, startDate, endDate);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
rankingCache.invalidate(cacheKey);
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
logger.error(sw.toString());
}
return result;
}
キャッシュローダー
・読み出し関数とは逆に、キーを分割してパラメータにしています
public CacheLoader<String, Ranking> rankingCacheLoader() {
CacheLoader<String, Ranking> loader = new CacheLoader<String, Ranking>() {
@Override
public @Nullable Ranking load(@NonNull String key) throws Exception {
final String[] params = key.split("-");
if (params.length != 3) { return null; }
return getRankingDto(params[0], params[1], params[2]);
}
};
return loader;
}
実読み込み関数(キャッシュしたい重い処理)
public Ranking getRankingDto(String param1, String startDate, String endDate) {
if (param1 == null || startDate == null || endDate == null) {
return null;
}
// データ取得など重い処理
Ranking dto = ...
return dto;
}
手軽に良きキャッシュライフ(?)が手に入ります。
以上、お疲れさまでした!