Guava predmemorija

1. Pregled

U ovom uputstvu ćemo pogledati Guava predmemorija implementacija - osnovna upotreba, politike deložacije, osvježavanje predmemorije i neke zanimljive skupne operacije.

Na kraju ćemo pogledati korištenje obavijesti o uklanjanju koje predmemorija može poslati.

2. Kako se koristi predmemorija Guava

Počnimo s jednostavnim primjerom - predmemoriraj veliko slovo Niz instance.

Prvo ćemo stvoriti CacheLoader - koristi se za izračunavanje vrijednosti pohranjene u predmemoriji. Od ovoga ćemo se poslužiti priručnikom CacheBuilder za izgradnju naše predmemorije koristeći zadane specifikacije:

@Test public void whenCacheMiss_thenValueIsComputed () {CacheLoader loader; loader = novi CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder (). build (loader); assertEquals (0, cache.size ()); assertEquals ("POZDRAV", cache.getUn Check ("zdravo")); assertEquals (1, cache.size ()); }

Primijetite kako u predmemoriji nema vrijednosti za naš ključ "zdravo" - i tako se vrijednost izračunava i predmemorira.

Također imajte na umu da koristimo getUncked () operacija - ovo izračunava i učitava vrijednost u predmemoriju ako već ne postoji.

3. Politike deložacije

Svaka predmemorija u određenom trenutku treba ukloniti vrijednosti. Razgovarajmo o mehanizmu izbacivanja vrijednosti iz predmemorije - koristeći različite kriterije.

3.1. Deložacija prema veličini

Možemo ograničite veličinu naše predmemorije koristeći maximumSize (). Ako predmemorija dosegne ograničenje, najstarije će stavke biti izbačene.

U sljedećem kodu ograničimo veličinu predmemorije na 3 zapisa:

@Test public void whenCacheReachMaxSize_thenEviction () {CacheLoader loader; loader = new CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder (). maximumSize (3) .build (loader); cache.getUn провеreno ("prvo"); cache.getUn провеreno ("drugo"); cache.getUn провеreno ("treće"); cache.getUn провеreno ("dalje"); assertEquals (3, cache.size ()); assertNull (cache.getIfPresent ("prvi")); assertEquals ("FORTH", cache.getIfPresent ("dalje")); }

3.2. Deložacija težinom

Možemo i mi ograničite veličinu predmemorije pomoću prilagođene funkcije težine. U sljedećem kodu koristimo duljina kao naša prilagođena funkcija težine:

@Test public void whenCacheReachMaxWeight_thenEviction () {CacheLoader loader; loader = novi CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Vaga za vaganjeByLength; weightByLength = new Weigher () {@Preuzmi javni int weight (String key, String value) {return value.length (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder () .maximumWeight (16) .weigher (weightByLength) .build (loader); cache.getUn провеreno ("prvo"); cache.getUn провеreno ("drugo"); cache.getUn провеreno ("treće"); cache.getUn провеreno ("posljednje"); assertEquals (3, cache.size ()); assertNull (cache.getIfPresent ("prvi")); assertEquals ("LAST", cache.getIfPresent ("last")); }

Napomena: Predmemorija može ukloniti više zapisa kako bi se ostavilo mjesta za novi veliki.

3.3. Deložacija po vremenu

Osim što veličinu koristimo za deložaciju starih zapisa, možemo koristiti i vrijeme. U sljedećem primjeru prilagođavamo našu predmemoriju ukloniti zapise koji su mirovali 2 ms:

@Test public void whenEntryIdle_thenEviction () baca InterruptedException {CacheLoader loader; loader = new CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder () .expireAfterAccess (2, TimeUnit.MILLISECONDS) .build (loader); cache.getUn провеreno ("zdravo"); assertEquals (1, cache.size ()); cache.getUn провеreno ("zdravo"); Navoj.spavanje (300); cache.getUn провеreno ("test"); assertEquals (1, cache.size ()); assertNull (cache.getIfPresent ("zdravo")); }

Možemo i mi evidencija o deložaciji na temelju njihovog ukupnog vremena uživo. U sljedećem primjeru, predmemorija će ukloniti zapise nakon 2 ms pohrane:

@Test public void whenEntryLiveTimeExpire_thenEviction () baca InterruptedException {CacheLoader loader; loader = novi CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder () .expireAfterWrite (2, TimeUnit.MILLISECONDS) .build (loader); cache.getUn провеreno ("zdravo"); assertEquals (1, cache.size ()); Navoj.spavanje (300); cache.getUn провеreno ("test"); assertEquals (1, cache.size ()); assertNull (cache.getIfPresent ("zdravo")); }

4. Slabi ključevi

Dalje, pogledajmo kako učiniti da naši ključevi predmemorije imaju slabe reference - omogućavajući sakupljaču smeća da prikuplja ključeve predmemorije na koje se drugdje ne poziva.

Prema zadanim postavkama i ključevi i vrijednosti predmemorije imaju jake reference, ali možemo učiniti da naša predmemorija pohranjuje ključeve pomoću slabih referenci slabi tipke () kao u sljedećem primjeru:

@Test public void whenWeakKeyHasNoRef_thenRemoveFromCache () {CacheLoader loader; loader = novi CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder (). slabKeys (). build (loader); }

5. Meke vrijednosti

Skupljačem smeća možemo dopustiti da pomoću nas prikupi naše predmemorirane vrijednosti softValues ​​() kao u sljedećem primjeru:

@Test public void whenSoftValue_thenRemoveFromCache () {CacheLoader loader; loader = novi CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder (). softValues ​​(). build (loader); }

Napomena: Mnogo mekih referenci može utjecati na performanse sustava - poželjnije je koristiti maximumSize ().

6. Ručka null Vrijednosti

Sada, da vidimo kako se rukuje predmemorijom null vrijednosti. Prema zadanim postavkama, Guava predmemorija bacit će iznimke ako pokušate učitati a null vrijednost - jer nema smisla predmemorirati a null.

Ali ako null vrijednost znači nešto u vašem kodu, tada možete dobro iskoristiti Neobvezno klase kao u sljedećem primjeru:

@Test public void whenNullValue_thenOptional () {CacheLoader utovarivač; loader = novi CacheLoader() {@Preuzmi javno Neobavezno učitavanje (ključ niza) {return Optional.fromNullable (getSuffix (ključ)); }}; LoadingCache predmemorija; cache = CacheBuilder.newBuilder (). build (loader); assertEquals ("txt", cache.getUn Check ("text.txt"). get ()); assertFalse (cache.getUn Check ("zdravo"). isPresent ()); } privatni niz getSuffix (završni niz str) {int lastIndex = str.lastIndexOf ('.'); if (lastIndex == -1) {return null; } return str.substring (lastIndex + 1); }

7. Osvježite predmemoriju

Dalje, pogledajmo kako osvježiti vrijednosti predmemorije.

7.1. Ručno osvježavanje

Jednu tipku možemo ručno osvježiti uz pomoć LoadingCache.refresh (ključ).

Vrijednost niza = loadingCache.get ("ključ"); loadingCache.refresh ("ključ");

To će prisiliti CacheLoader za učitavanje nove vrijednosti za ključ.

Dok se nova vrijednost uspješno ne učita, prethodna vrijednost ključ vratit će dobiti (ključ).

7.2. Automatsko osvježavanje

Možemo koristiti CacheBuilder.refreshAfterWrite (trajanje) za automatsko osvježavanje predmemoriranih vrijednosti.

@Test public void whenLiveTimeEnd_thenRefresh () {Učitavač CacheLoader; loader = novi CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder () .refreshAfterWrite (1, TimeUnit.MINUTES) .build (loader); }

Važno je to razumjeti refreshAfterWrite (trajanje) čini samo ključ prihvatljiv za osvježavanje nakon navedenog trajanja. Vrijednost će se zapravo osvježiti tek kada se zatraži odgovarajući unos dobiti (ključ).

8. Predučitajte predmemoriju

U našu predmemoriju možemo umetnuti više zapisa putAll () metoda. U sljedećem primjeru dodajemo više zapisa u našu predmemoriju pomoću a Karta:

@Test public void whenPreloadCache_thenUsePutAll () {CacheLoader loader; loader = novi CacheLoader () {@Preuzmi javno učitavanje niza (ključ niza) {return key.toUpperCase (); }}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder (). build (loader); Karta karte = novi HashMap (); map.put ("prvi", "PRVI"); map.put ("drugi", "DRUGI"); cache.putAll (karta); assertEquals (2, cache.size ()); }

9. Obavijest o uklanjanju

Ponekad trebate poduzeti neke radnje kad se zapis ukloni iz predmemorije; pa, razgovarajmo RemovalNotification.

Možemo registrirati a RemovalListener da biste dobili obavijesti o uklanjanju zapisa. Također imamo pristup uzroku uklanjanja - putem getCause () metoda.

U sljedećem uzorku, a RemovalNotification dobiva se kada četvrti element u predmemoriji zbog svoje veličine:

@Test public void whenEntryRemovedFromCache_thenNotify () {Učitavač CacheLoader; loader = new CacheLoader () {@Override public String load (final String key) {return key.toUpperCase (); }}; Slušač RemovalListener; listener = novi RemovalListener () {@Preuzmi javnu prazninu onRemoval (RemovalNotification n) {if (n.wasEvicted ()) {Uzrok niza = n.getCause (). name (); assertEquals (RemovalCause.SIZE.toString (), uzrok); }}}; Predmemorija LoadingCache; cache = CacheBuilder.newBuilder () .maximumSize (3) .removalListener (listener) .build (loader); cache.getUn провеreno ("prvo"); cache.getUn провеreno ("drugo"); cache.getUn провеreno ("treće"); cache.getUn провеreno ("posljednje"); assertEquals (3, cache.size ()); }

10. Bilješke

Na kraju, evo nekoliko dodatnih brzih napomena o implementaciji predmemorije Guava:

  • zaštićen je nitima
  • vrijednosti možete ručno umetnuti u predmemoriju pomoću put (ključ, vrijednost)
  • možete izmjeriti performanse svoje predmemorije pomoću CacheStats ( hitRate (), missRate (), ..)

11. Zaključak

U ovom smo priručniku prošli puno slučajeva upotrebe predmemorije Guava - od jednostavne upotrebe do deložacije elemenata, osvježavanja i prethodnog učitavanja predmemorije i obavijesti o uklanjanju.

Kao i obično, svi se primjeri mogu naći na GitHubu.