Guava Cache

1. Áttekintés

Ebben az oktatóanyagban megnézzük a Guava Cache megvalósítás - alapvető használat, kilakoltatási irányelvek, a gyorsítótár frissítése és néhány érdekes tömeges művelet.

Végül megvizsgáljuk a gyorsítótár által küldhető eltávolítási értesítések használatát.

2. A Guava Cache használata

Kezdjük egy egyszerű példával - tegyük gyorsítótárba a nagybetűs alakját Húr példányok.

Először létrehozzuk a CacheLoader - a gyorsítótárban tárolt érték kiszámítására szolgál. Ebből a praktikusat fogjuk használni CacheBuilder a gyorsítótárunk felépítése a megadott specifikációk alapján:

@Test public void whenCacheMiss_thenValueIsComputed () {CacheLoader betöltő; loader = new CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder (). build (betöltő); assertEquals (0, cache.size ()); assertEquals ("HELLO", cache.getUnchecked ("hello")); assertEquals (1, cache.size ()); }

Figyelje meg, hogy nincs-e érték a "hello" kulcs gyorsítótárában - és így az érték kiszámításra és gyorsítótárba kerül.

Vegye figyelembe azt is, hogy a getUnchecked () művelet - ez kiszámítja és betölti az értéket a gyorsítótárba, ha még nem létezik.

3. Kilakoltatási irányelvek

Minden gyorsítótárnak el kell távolítania az értékeket valamikor. Beszéljük meg az értékek gyorsítótárból történő kiürítésének mechanizmusát - különböző kritériumok segítségével.

3.1. A kilakoltatás méret szerint

Tudunk korlátozza a gyorsítótár méretét felhasználásával maximumSize (). Ha a gyorsítótár eléri a korlátot, a legrégebbi elemeket kilakoltatják.

A következő kódban a gyorsítótár méretét 3 rekordra korlátozzuk:

@Test public void whenCacheReachMaxSize_thenEviction () {CacheLoader betöltő; loader = new CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder (). maximumSize (3) .build (betöltő); cache.getUnchecked ("első"); cache.getUnchecked ("második"); cache.getUnchecked ("harmadik"); cache.getUnchecked ("tovább"); assertEquals (3, cache.size ()); assertNull (cache.getIfPresent ("első")); assertEquals ("FORTH", cache.getIfPresent ("tovább")); }

3.2. Súly szerinti kilakoltatás

Azt is megtehetjük korlátozza a gyorsítótár méretét egyedi súlyfüggvény használatával. A következő kódban a hossz egyedi súlyfüggvényünkként:

@Test public void whenCacheReachMaxWeight_thenEviction () {CacheLoader betöltő; loader = new CacheLoader () {@Orride public String load (String key) {return key.toUpperCase (); }}; Mérleg súlyaByLength; weightByLength = new Weigher () {@Orride public int weight (String key, String value) {return value.length (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder () .maximumWeight (16) .weigher (weightByLength) .build (loader); cache.getUnchecked ("első"); cache.getUnchecked ("második"); cache.getUnchecked ("harmadik"); cache.getUnchecked ("utolsó"); assertEquals (3, cache.size ()); assertNull (cache.getIfPresent ("első")); assertEquals ("UTOLSÓ", cache.getIfPresent ("utolsó")); }

Megjegyzés: A gyorsítótár egynél több rekordot is eltávolíthat, hogy helyet biztosítson egy új nagynak.

3.3. Idõ szerinti kilakoltatás

A régi rekordok kilakoltatásán kívül használhatunk időt is. A következő példában a gyorsítótárunkat testre szabjuk távolítsa el a 2 másodpercig tétlen rekordokat:

@Test public void, amikorEntryIdle_thenEviction () az InterruptedException {CacheLoader betöltőt dobja; loader = new CacheLoader () {@Orride public String load (String key) {return key.toUpperCase (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder () .expireAfterAccess (2, TimeUnit.MILLISECONDS) .build (betöltő); cache.getUnchecked ("hello"); assertEquals (1, cache.size ()); cache.getUnchecked ("hello"); Szál.alszik (300); cache.getUnchecked ("teszt"); assertEquals (1, cache.size ()); assertNull (cache.getIfPresent ("hello")); }

Azt is megtehetjük a teljes élettartamuk alapján kiírják a nyilvántartásokat. A következő példában a gyorsítótár 2 ms elteltével eltávolítja a rekordokat:

@Test public void, amikorEntryLiveTimeExpire_thenEviction () az InterruptedException {CacheLoader betöltőt dobja; loader = new CacheLoader () {@Orride public String load (String key) {return key.toUpperCase (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder () .expireAfterWrite (2, TimeUnit.MILLISECONDS) .build (betöltő); cache.getUnchecked ("hello"); assertEquals (1, cache.size ()); Szál.alszik (300); cache.getUnchecked ("teszt"); assertEquals (1, cache.size ()); assertNull (cache.getIfPresent ("hello")); }

4. Gyenge kulcsok

Ezután nézzük meg, hogyan lehet a gyorsítótár-kulcsokat gyenge referenciákkal ellátni - lehetővé téve a szemétszedő számára, hogy összegyűjtse azokat a gyorsítótár-kulcsokat, amelyekre máshol nem hivatkoznak.

Alapértelmezés szerint mind a gyorsítótár-kulcsok, mind az értékek erős referenciákkal rendelkeznek, de a gyorsítótárunkat gyenge referenciák használatával tárolhatjuk gyenge kulcsok () mint a következő példában:

@Test public void whenWeakKeyHasNoRef_thenRemoveFromCache () {CacheLoader betöltő; loader = new CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder (). silpKeys (). build (betöltő); }

5. Lágy értékek

Használatával engedélyezhetjük, hogy a szemétszedő összegyűjtse a gyorsítótárazott értékeinket softValues ​​() mint a következő példában:

@Test public void whenSoftValue_thenRemoveFromCache () {CacheLoader betöltő; loader = new CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder (). softValues ​​(). build (betöltő); }

Megjegyzés: Számos puha hivatkozás befolyásolhatja a rendszer teljesítményét - előnyösebb használni maximumSize ().

6. Fogantyú nulla Értékek

Most nézzük meg, hogyan kell kezelni a gyorsítótárat nulla értékek. Alapértelmezés szerint, Guava Cache kivételt dob, ha megpróbálja betölteni a nulla érték - mivel nincs értelme a gyorsítótárba tenni a nulla.

De ha nulla érték jelent valamit a kódodban, akkor jól használhatod a Választható osztály, mint a következő példában:

@Test public void whenNullValue_thenOptional () {CacheLoader rakodó; loader = új CacheLoader() {@Override public Opcionális betöltés (karakterlánc kulcs) {return Optional.fromNullable (getSuffix (kulcs)); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder (). build (betöltő); assertEquals ("txt", cache.getUnchecked ("text.txt"). get ()); assertFalse (cache.getUnchecked ("hello"). isPresent ()); } privát String getSuffix (végső String Str) {int lastIndex = str.lastIndexOf ('.'); if (lastIndex == -1) {return null; } return str.substring (lastIndex + 1); }

7. Frissítse a gyorsítótárat

Ezután nézzük meg, hogyan frissíthetjük a gyorsítótár értékeinket.

7.1. Manuális frissítés

Egyetlen kulcsot manuálisan frissíthetünk a segítségével LoadingCache.refresh (kulcs).

Karakterlánc értéke = loadingCache.get ("kulcs"); loadingCache.refresh ("kulcs");

Ez kényszeríteni fogja a CacheLoader betölteni az új értéket a kulcs.

Az új érték sikeres betöltéséig az előző értéke kulcs visszaadja a get (kulcs).

7.2. Automatikus frissítés

Tudjuk használni CacheBuilder.refreshAfterWrite (időtartam) a gyorsítótárazott értékek automatikus frissítéséhez.

@Test public void whenLiveTimeEnd_thenRefresh () {CacheLoader betöltő; loader = new CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder () .refreshAfterWrite (1, TimeUnit.MINUTES) .build (betöltő); }

Fontos ezt megérteni refreshAfterWrite (időtartam) csak kulcsot készít választható a frissítéshez a megadott időtartam után. Az érték valójában csak akkor frissül, ha egy megfelelő bejegyzést lekérdez get (kulcs).

8. Töltse be előre a gyorsítótárat

A. Használatával több rekordot is beilleszthetünk a gyorsítótárunkba putAll () módszer. A következő példában több rekordot adunk a gyorsítótárunkba az a használatával Térkép:

@Test public void whenPreloadCache_thenUsePutAll () {CacheLoader loader; loader = new CacheLoader () {@Orride public String load (String key) {return key.toUpperCase (); }}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder (). build (betöltő); Térképtérkép = új HashMap (); map.put ("első", "ELSŐ"); map.put ("második", "MÁSODIK"); cache.putAll (térkép); assertEquals (2, cache.size ()); }

9. RemovalNotification

Néha meg kell tennie bizonyos műveleteket, amikor egy rekordot eltávolít a gyorsítótárból; szóval, beszéljük meg RemovalNotification.

Regisztrálhatunk a RemovalListener értesítéseket kap a rekord eltávolításáról. Az eltávolítás okához is hozzáférhetünk - a getCause () módszer.

A következő mintában a RemovalNotification akkor érkezik, amikor a gyorsítótár negyedik eleme a mérete miatt:

@Test public void whenEntryRemovedFromCache_thenNotify () {CacheLoader betöltő; loader = new CacheLoader () {@Orride public String load (final String key) {return key.toUpperCase (); }}; RemovalListener hallgató; listener = new RemovalListener () {@Orride public void onRemoval (RemovalNotification n) {if (n.wasEvicted ()) {String reason = n.getCause (). name (); assertEquals (RemovalCause.SIZE.toString (), oka); }}}; LoadingCache gyorsítótár; cache = CacheBuilder.newBuilder () .maximumSize (3) .removalListener (figyelő) .build (betöltő); cache.getUnchecked ("első"); cache.getUnchecked ("második"); cache.getUnchecked ("harmadik"); cache.getUnchecked ("utolsó"); assertEquals (3, cache.size ()); }

10. Megjegyzések

Végül íme néhány további gyors megjegyzés a guava gyorsítótár megvalósításáról:

  • menetbiztos
  • az értékeket manuálisan beillesztheti a gyorsítótárba a put (kulcs, érték)
  • segítségével mérheti a gyorsítótár teljesítményét CacheStats ( találati arány(), missRate (), ..)

11. Következtetés

A Guava Cache sok használati esetét átéltük ebben az oktatóanyagban - az egyszerű használattól kezdve az elemek kilakoltatásáig, a gyorsítótár frissítéséig és előzetes betöltéséig, valamint az eltávolítási értesítésekig.

Szokás szerint az összes példa megtalálható a GitHub-on.