SpringBootとキャッシュライブラリCaffeineでローカルキャッシュサーバを作成する

エキサイト株式会社メディア事業部エンジニアの佐々木です。2024年アドベントカレンダー2日目を担当させていただきます。

サーバーローカルでのキャッシュはHashMap等で自作してもいいのですが、キャッシュ期限を自作するのは結構面倒なので、Caffeineを使用します。

github.com

前提

## Java

openjdk version "21.0.1" 2023-10-17 LTS
OpenJDK Runtime Environment Corretto-21.0.1.12.1 (build 21.0.1+12-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.1.12.1 (build 21.0.1+12-LTS, mixed mode, sharing)
## Gradle

------------------------------------------------------------
Gradle 8.4
------------------------------------------------------------

Build time:   2023-10-04 20:52:13 UTC
Revision:     e9251e572c9bd1d01e503a0dfdf43aedaeecdc3f
## SpringBoot

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.0)

設定

build.gradleはこのように設定する。

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    implementation "com.github.ben-manes.caffeine:caffeine:3.1.8"  // キャッシュライブラリ Caffeineの読み込み
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

SpringBootの設定用のコードは下記のように実装する。

@Configuration
public class CacheConfig {

    /**
     * キャッシュの設定を行う.
     */
    @Bean
    public CaffeineCacheManager caffeineCacheManager(){
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        Arrays.stream(CacheType.values()).forEach(e -> {
            Cache<Object, Object> cache = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(e.getTtl())).build();
            cacheManager.registerCustomCache(e.getName(), cache);
        });
        return cacheManager;    
}

enum CacheType {  // キーごとにTTLを設定する用の enumを作成する

    MIN_1(CacheType.MIN_1_KEY, 60)  // 1分のキャッシュ設定
    , SECOND_30(CacheType.SECOND_30_KEY, 30); // 30秒のキャッシュ設定

    public static final String MIN_1_KEY = "MIN_1";
    public static final String SECOND_30_KEY = "SECOND_30";

    @Getter
    private final String name;
    @Getter
    private final long ttl;
    CacheType(String name, long ttl) {
        this.name = name;
        this.ttl = ttl;
    }
}

SpringBootのController/Serviceの実装は下記のようになる。

interface DemoService {

    Object get5SecondsExpirationData() throws InterruptedException;

    Object get10SecondsExpirationData() throws InterruptedException;
}

@RestController
@RequestMapping("")
@RequiredArgsConstructor
public class DemoController {

    private final DemoService demoService;

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");

    @GetMapping("cacheExpiration5Seconds")
    public Object cacheExpiration5Seconds() throws InterruptedException {
        return LocalTime.now().format(formatter) + ":" + demoService.get5SecondsExpirationData();
    }

    @GetMapping("cacheExpiration10Seconds")
    public Object cacheExpiration10Seconds() throws InterruptedException {
        return LocalTime.now().format(formatter) + ":" + demoService.get10SecondsExpirationData();
    }
}

@Service
class DemoServiceImpl implements DemoService {

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");

    @Override
    @Cacheable(CacheType.SECOND_10_KEY)  // 10秒キャッシュ指定
    public Object get10SecondsExpirationData() throws InterruptedException {
        Thread.sleep(10000);   // スリープ10秒
        return "expiration 10 seconds \n";
    }

    @Override
    @Cacheable(CacheType.SECOND_5_KEY)  // 5秒キャッシュ指定
    public Object get5SecondsExpirationData() throws InterruptedException {
        Thread.sleep(5000);  // スリープ5秒
        return "expiration 5 seconds \n";
    }
}

Service層のメソッドにアノテーションでそれぞれのキャッシュ時間を指定します。タイプセーフにキャッシュ指定できるようにしています。 検証用にService層内のメソッドが実行される場合は、それぞれ5秒、10秒待たされる実装になっています。

検証

5秒キャッシュの方のAPIを実行してみます。

5秒キャッシュ

キャッシュの有効期限が過ぎたタイミングで、5秒待たされるような挙動になっています。

まとめ

今回はキャッシュライブラリのCaffeineを使用して、サーバーローカルなキャッシュ機構を作成してみました。SpringBootではこのようなライブラリを使用しなければ、HashMapを使用したキャッシュが作られますが、有効期限などの設定は自前で実装することになるので、これだけの設定で作れるは便利かと思います。サーバーローカルなキャッシュを設定とリモートサーバーキャッシュの両方を使用する設定は次回ブログに書きます。

最後に

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

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com