Help us understand the problem. What is going on with this article?

GuavaのLoadingCacheの実装サンプル

More than 1 year has passed since last update.

業務でguavaのLoadingCacheの実装をしたので、そのときのメモ。

3行

  • Javaでキャッシュ実装を簡単に行うことができる
  • LoadingCache<K,V>というKey-Valueでデータを保持することができる
  • JVMでデータを保持しておくため、メモリの使いすぎには注意

サンプル実装

SpringBootでローカル環境にREST APIサーバを構築します。

$ curl http://localhost:8080/pokemon\?id\=150
{"id":150,"name":"mewtwo","weight":1220,"height":20}

上記でSpringBootにGETリクエストすると、SpringBootがpokeapiにアクセスして、指定されたidのポケモンデータを取得します。1度データを取りに行くと、SpringBootはそのデータをキャッシュとして保持します。

https://pokeapi.co/api/v2/pokemon/${id}

Controllerの実装

CacheBuilderを使うことで、一度pokeapiにアクセスしたデータをキャッシュしておきます。キャッシュの保持期間やデータ数はexpireAfterAccessmaximumSizeで設定することができます。下記はmaximumSize=1なので1つのデータしかキャッシュしません。

@RestController
class PokemonController() {
    private val log = LoggerFactory.getLogger(PokemonController::class.java)
    private val restTemplate = RestTemplate()

    // キャッシュ設定
    private val pokemonCache = CacheBuilder.newBuilder()
        .maximumSize(1)                                  // キャッシュするデータ数
        .expireAfterAccess(10, TimeUnit.SECONDS)         // キャッシュする期間
        .build(object: CacheLoader<Int, Pokemon>() {    // idがKeyでPokemonがValue
            @Throws(Exception::class)
            override fun load(id: Int): Pokemon {
                return getPokemon(id)                    // このメソッドの戻り値をキャッシュ
            }
        })!!

    @RequestMapping("/pokemon")
    fun findPokemon(id: Int): Pokemon {
        val stopwatch = Stopwatch.createStarted()
        val pokemon = pokemonCache.get(id)
        stopwatch.stop()
        log.info("Elapsed time: $stopwatch")
        return pokemon
    }

    fun getPokemon(@RequestParam id: Int): Pokemon {
        return restTemplate.getForObject(
            "https://pokeapi.co/api/v2/pokemon/$id", Pokemon::class.java)
    }
}

実行結果

strestを使って、本当にキャッシュが効いているか確認します。

  1. id=151にアクセス(遅い。初回のアクセスのため)
  2. id=151にアクセス(速い。151はキャッシュされている)
  3. id=150にアクセス(遅い。150は初回アクセスのため)
  4. id=151にアクセス(遅い。キャッシュは1データだけなので、151のキャッシュは効いてないから遅い)
$ yarn strest test.yml                                                    

✔ Testing request_151_1st succeeded (0.195s)                              
✔ Testing request_151_2nd succeeded (0.007s)                              
✔ Testing request_150 succeeded (0.129s)                                  
✔ Testing request_151_3rd succeeded (0.111s)                              

[ Strest ] ✨  Done in 0.462s                                             

✨  Done in 2.24s.                                                                                                                               

Kotlinの方のログはこんな感じ。

2019-02-23 21:41:51.117  INFO 16733 --- [nio-8080-exec-2] c.e.j.demo.controller.PokemonController  : Elapsed time: 162.0 ms
2019-02-23 21:41:51.138  INFO 16733 --- [nio-8080-exec-3] c.e.j.demo.controller.PokemonController  : Elapsed time: 40.15 μs
2019-02-23 21:41:51.268  INFO 16733 --- [nio-8080-exec-4] c.e.j.demo.controller.PokemonController  : Elapsed time: 122.7 ms
2019-02-23 21:41:51.379  INFO 16733 --- [nio-8080-exec-5] c.e.j.demo.controller.PokemonController  : Elapsed time: 104.9 ms

キャッシュはJVMに保持される

なので調子に乗ってキャッシュ化しすぎると、メモリ容量を溢れる可能性があります。そういった場合はmaximumWeightでキャッシュのデータサイズを制限できるみたい。単位はbyteかな。

APIドキュメントはここ

Interface LoadingCache<K,V>
https://google.github.io/guava/releases/27.0.1-jre/api/docs/com/google/common/cache/LoadingCache.html

Class CacheBuilder<K,V>
https://google.github.io/guava/releases/27.0.1-jre/api/docs/com/google/common/cache/CacheBuilder.html

参考

https://www.baeldung.com/guava-cacheloader
https://github.com/google/guava/wiki/CachesExplained
http://www.codingpedia.org/ama/java-cache-example-with-guava

ksakiyama134
もっとネットワークの低レイヤがわかるようになりたい
https://github.com/ksakiyama
future
ITを武器とした課題解決型のコンサルティングサービスを提供します
http://future-architect.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away