[Java]ローカルキャッシュを導入した話

はじめに

こんにちは、メディアプラットフォーム事業部エンジニアの岡崎です。最近、ローカルキャッシュの導入を行いました。今回はその時のことを備忘録としてブログに残します。

前提

今回の要件として一番大切なことは、できるだけユーザーがページを見ることができる状態にすることでした。

これを前提として、今までの実装を見ていきましょう。

最初、キャッシュはRedisに保存するような構成になっていました。

この場合、もしDBに障害が起きたとしても、Redisに保存しているデータがあった場合は、そこから取得することができます。

しかし、Redisに障害が起きてしまったら、データが取得できなくなり、クライアントではページを表示できなくなってしまいます。

今回は要件で、できるだけユーザーがページを見れる状態にしたかったので、他にもっといい方法がないか模索することになりました。

そこで、キャッシュしたデータを保存する場所をRedisからローカルに変更しました。

こうすることで、DBに障害が起きたとしても、ローカルサーバーにデータがある限りは、データを取得できます。

環境

openjdk version "21.0.2" 2024-01-16 LTS
OpenJDK Runtime Environment Corretto-21.0.2.13.1 (build 21.0.2+13-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.2.13.1 (build 21.0.2+13-LTS, mixed mode, sharing)
  • Spring Boot
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.2)
  • build.gradleに以下の依存関係を追加する
implementation "com.github.ben-manes.caffeine:caffeine:3.1.8"

実装

まずは、ローカルキャッシュを使うための準備を行いました。

必要なものは以下の通りです。

  • ローカル用のキャッシュマネージャー
  • Redis用のキャッシュマネージャー
  • ローカルのキャッシュキー

これらを、それぞれのファイルに実装していきました。

その時の実装例を以下に示します。

CacheConfig

ここでは、ローカルキャッシュのキャッシュマネージャーの設定を行っています。

@Configuration
public class CacheConfig {
    /**
     * ローカルキャッシュ用のキャッシュマネージャーを設定する
     *
     * @return キャッシュマネージャー
     */
    @Bean(CacheLocalKeyType.LOCAL_CACHE_MANAGER_NAME)
    public CacheManager localCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();

        Arrays.stream(CacheLocalKeyType.values()).forEach(e -> {
            Cache<Object, Object> cache = Caffeine
                    .newBuilder()
                    .expireAfterWrite(Duration.ofSeconds(e.getTtl()))
                    .build();

            cacheManager.registerCustomCache(e.getKey(), cache);
        });
        return cacheManager;
    }
}

CacheLocalKeyType

ローカルのキャッシュキーの実装例です。

public enum CacheLocalKeyType {
    WEB_GET_TOP(CacheLocalKeyType.WEB_TOP_KEY, TimeUnit.HOURS.toSeconds(1));

    @Getter
    private final String key;
    @Getter
    private final Long ttl;

    /**
     * ローカルキャッシュのキータイプ
     *
     * @param cacheName キャッシュ名
     * @param ttl キャッシュ時間
     */
    CacheLocalKeyType(String cacheName, Long ttl) {
        this.key = cacheName;
        this.ttl = ttl;
    }

    public static final String SAMPLE_KEY = "sampleKey";
    public static final String LOCAL_CACHE_MANAGER_NAME = "localCacheManager";
}

Redis.config

Redis.configにキャッシュマネージャーがない場合、実装する必要があります。

今回は、Redisのキャッシュをデフォルトの設定にしたいため、@Primaryをつけます。

    /**
     * Redisのキャッシュマネージャーを設定する
     *
     * @param redisConnectionFactory redisConnectionFactory
     * @return キャッシュマネージャー
     */
    @Bean
    @Primary
    public CacheManager cacheManager(LettuceConnectionFactory redisConnectionFactory) {
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        Arrays.stream(CacheKeyType.values()).forEach(e ->
                cacheConfigurations.put(
                        e.getKey(),
                        RedisCacheConfiguration
                                .defaultCacheConfig()
                                .entryTtl(Duration.ofSeconds(e.getTtl()))
                                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
                                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(this.serializer()))
                )
        );

        return RedisCacheManager
                .builder(redisConnectionFactory)
                .withInitialCacheConfigurations(cacheConfigurations)
                .build();
    }

以上で、ローカルキャッシュを使う準備は整いました。

それでは、実際にローカルキャッシュを使ってみましょう。実装例は以下の通りです。

@Override
// キャッシュマネージャーを指定しない場合、デフォルトの設定が使われます
@Cacheable(cacheNames = CacheKeyType.SAMPLE_KEY, cacheManager = CacheLocalKeyType.LOCAL_CACHE_MANAGER_NAME)
public String getIdList() {
    // 以下略
}

これで完成です!

最後に

今回は、ローカルキャッシュを導入したので、簡単に紹介しました。簡単に実装ができるので、ぜひローカルキャッシュの実装の導入を検討してみてください。

最後に、採用情報のお知らせです。エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

募集職種一覧はこちらになります!

www.wantedly.com