Guava-cache

1. Overzicht

In deze zelfstudie bekijken we de Guava-cache implementatie - basisgebruik, verwijderingsbeleid, het vernieuwen van de cache en enkele interessante bulkbewerkingen.

Ten slotte zullen we kijken naar het gebruik van de verwijderingsmeldingen die de cache kan verzenden.

2. Hoe Guava Cache te gebruiken

Laten we beginnen met een eenvoudig voorbeeld - laten we de hoofdlettervorm van Draad gevallen.

Eerst maken we het CacheLoader - gebruikt om de waarde te berekenen die is opgeslagen in de cache. Hiervan zullen we de handige gebruiken CacheBuilder om onze cache te bouwen met behulp van de gegeven specificaties:

@Test openbare leegte whenCacheMiss_thenValueIsComputed () {CacheLoader-lader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder (). build (loader); assertEquals (0, cache.size ()); assertEquals ("HELLO", cache.getUnchecked ("hallo")); assertEquals (1, cache.size ()); }

Merk op dat er geen waarde in de cache is voor onze "hallo" -sleutel - en dus wordt de waarde berekend en in de cache opgeslagen.

Merk ook op dat we de getUnchecked () operatie - dit berekent en laadt de waarde in de cache als deze nog niet bestaat.

3. Uitzettingsbeleid

Elke cache moet op een bepaald moment waarden verwijderen. Laten we het mechanisme bespreken om waarden uit de cache te verwijderen - met behulp van verschillende criteria.

3.1. Uitzetting op grootte

Wij kunnen beperk de grootte van onze cache gebruik makend van maximumgrootte(). Als de cache de limiet bereikt, worden de oudste items verwijderd.

In de volgende code beperken we de cachegrootte tot 3 records:

@Test openbare leegte whenCacheReachMaxSize_thenEviction () {CacheLoader-lader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder (). maximumSize (3) .build (loader); cache.getUnchecked ("eerste"); cache.getUnchecked ("tweede"); cache.getUnchecked ("derde"); cache.getUnchecked ("weer"); assertEquals (3, cache.size ()); assertNull (cache.getIfPresent ("eerste")); assertEquals ("FORTH", cache.getIfPresent ("weer")); }

3.2. Uitzetting naar gewicht

We kunnen ook beperk de cachegrootte met behulp van een aangepaste gewichtsfunctie. In de volgende code gebruiken we de lengte als onze aangepaste gewichtsfunctie:

@Test openbare leegte whenCacheReachMaxWeight_thenEviction () {CacheLoader-lader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; Weger weegByLength; weegByLength = nieuwe Weigher () {@Override public int weegt (String-sleutel, String-waarde) {return value.length (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder () .maximumWeight (16) .weger (weegByLength) .build (loader); cache.getUnchecked ("eerste"); cache.getUnchecked ("tweede"); cache.getUnchecked ("derde"); cache.getUnchecked ("laatste"); assertEquals (3, cache.size ()); assertNull (cache.getIfPresent ("eerste")); assertEquals ("LAST", cache.getIfPresent ("last")); }

Opmerking: de cache kan meer dan één record verwijderen om ruimte te laten voor een nieuwe grote.

3.3. Uitzetting door de tijd

Naast het gebruik van grootte om oude records te verdrijven, kunnen we ook tijd gebruiken. In het volgende voorbeeld passen we onze cache aan op verwijder records die 2 ms inactief zijn geweest:

@Test openbare leegte whenEntryIdle_thenEviction () gooit InterruptedException {CacheLoader loader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder () .expireAfterAccess (2, TimeUnit.MILLISECONDS) .build (loader); cache.getUnchecked ("hallo"); assertEquals (1, cache.size ()); cache.getUnchecked ("hallo"); Thread.sleep (300); cache.getUnchecked ("test"); assertEquals (1, cache.size ()); assertNull (cache.getIfPresent ("hallo")); }

We kunnen ook verwijder records op basis van hun totale live-tijd. In het volgende voorbeeld verwijdert de cache de records nadat ze 2 ms zijn opgeslagen:

@Test openbare leegte whenEntryLiveTimeExpire_thenEviction () gooit InterruptedException {CacheLoader loader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder () .expireAfterWrite (2, TimeUnit.MILLISECONDS) .build (loader); cache.getUnchecked ("hallo"); assertEquals (1, cache.size ()); Thread.sleep (300); cache.getUnchecked ("test"); assertEquals (1, cache.size ()); assertNull (cache.getIfPresent ("hallo")); }

4. Zwakke sleutels

Laten we vervolgens kijken hoe we ervoor kunnen zorgen dat onze cachesleutels zwakke referenties hebben - waardoor de garbage collector cachesleutels kan verzamelen waarnaar nergens anders wordt verwezen.

Standaard hebben zowel cachesleutels als waarden sterke referenties, maar we kunnen onze cache de sleutels laten opslaan met behulp van zwakke referenties met zwakke toetsen () zoals in het volgende voorbeeld:

@Test openbare leegte whenWeakKeyHasNoRef_thenRemoveFromCache () {CacheLoader-lader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder (). zwakkeKeys (). build (lader); }

5. Zachte waarden

We kunnen de garbage collector toestaan ​​om onze gecachte waarden te verzamelen met behulp van softValues ​​() zoals in het volgende voorbeeld:

@Test openbare leegte whenSoftValue_thenRemoveFromCache () {CacheLoader-lader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder (). softValues ​​(). build (loader); }

Opmerking: veel zachte verwijzingen kunnen de systeemprestaties beïnvloeden - het verdient de voorkeur maximumgrootte().

6. Handvat nul Waarden

Laten we nu eens kijken hoe we met de cache moeten omgaan nul waarden. Standaard, Guava-cache zal uitzonderingen genereren als u probeert een nul waarde - aangezien het geen zin heeft om een nul.

Maar als nul waarde betekent iets in uw code, dan kunt u goed gebruik maken van de Optioneel class zoals in het volgende voorbeeld:

@Test openbare leegte whenNullValue_thenOptional () {CacheLoader lader; loader = nieuwe CacheLoader() {@Override public Optioneel laden (String key) {return Optioneel.fromNullable (getSuffix (key)); }}; LoadingCache cache; cache = CacheBuilder.newBuilder (). build (loader); assertEquals ("txt", cache.getUnchecked ("text.txt"). get ()); assertFalse (cache.getUnchecked ("hallo"). isPresent ()); } private String getSuffix (final String str) {int lastIndex = str.lastIndexOf ('.'); if (lastIndex == -1) {retourneer null; } return str.substring (lastIndex + 1); }

7. Vernieuw de cache

Laten we vervolgens kijken hoe we onze cachewaarden kunnen vernieuwen.

7.1. Handmatig vernieuwen

We kunnen een enkele sleutel handmatig vernieuwen met behulp van LoadingCache.refresh (sleutel).

Stringwaarde = loadingCache.get ("key"); loadingCache.refresh ("sleutel");

Dit zal het CacheLoader om de nieuwe waarde voor het sleutel.

Totdat de nieuwe waarde met succes is geladen, de vorige waarde van de sleutel zal worden geretourneerd door de krijgen (sleutel).

7.2. Automatisch vernieuwen

We kunnen gebruiken CacheBuilder.refreshAfterWrite (duur) om waarden in de cache automatisch te vernieuwen.

@Test openbare leegte whenLiveTimeEnd_thenRefresh () {CacheLoader-lader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder () .refreshAfterWrite (1, TimeUnit.MINUTES) .build (loader); }

Het is belangrijk dat te begrijpen refreshAfterWrite (duur) maakt alleen een sleutel verkiesbaar voor het vernieuwen na de opgegeven duur. De waarde wordt alleen vernieuwd als een overeenkomstig item wordt opgevraagd door get (sleutel).

8. Laad de cache vooraf

We kunnen meerdere records in onze cache invoegen met zet alle() methode. In het volgende voorbeeld voegen we meerdere records toe aan onze cache met behulp van een Kaart:

@Test openbare leegte whenPreloadCache_thenUsePutAll () {CacheLoader-lader; loader = nieuwe CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache-cache; cache = CacheBuilder.newBuilder (). build (lader); Map map = nieuwe HashMap (); map.put ("eerste", "FIRST"); map.put ("second", "SECOND"); cache.putAll (kaart); assertEquals (2, cache.size ()); }

9. Kennisgeving van verwijdering

Soms moet u een aantal acties ondernemen wanneer een record uit de cache wordt verwijderd; dus, laten we het bespreken Verwijdering Kennisgeving.

We kunnen een RemovalListener om meldingen te krijgen van een record dat wordt verwijderd. We hebben ook toegang tot de oorzaak van de verwijdering - via de getCause () methode.

In het volgende voorbeeld, a Verwijdering Kennisgeving wordt ontvangen wanneer het vierde element in de cache vanwege zijn grootte:

@Test openbare leegte whenEntryRemovedFromCache_thenNotify () {CacheLoader-lader; loader = nieuwe CacheLoader () {@Override public String load (final String key) {return key.toUpperCase (); }}; RemovalListener luisteraar; listener = nieuwe RemovalListener () {@Override public void onRemoval (RemovalNotification n) {if (n.wasEvicted ()) {String oorzaak = n.getCause (). name (); assertEquals (RemovalCause.SIZE.toString (), oorzaak); }}}; LoadingCache-cache; cache = CacheBuilder.newBuilder () .maximumSize (3) .removalListener (luisteraar) .build (loader); cache.getUnchecked ("eerste"); cache.getUnchecked ("tweede"); cache.getUnchecked ("derde"); cache.getUnchecked ("laatste"); assertEquals (3, cache.size ()); }

10. Opmerkingen

Ten slotte zijn hier een paar aanvullende korte opmerkingen over de implementatie van de Guava-cache:

  • het is draadveilig
  • u kunt waarden handmatig in de cache invoegen met put (sleutel, waarde)
  • u kunt uw cacheprestaties meten met CacheStats ( hitRate (), missRate (), ..)

11. Conclusie

We hebben in deze tutorial veel gebruiksscenario's van de Guava Cache doorgenomen - van eenvoudig gebruik tot het verwijderen van elementen, vernieuwen en vooraf laden van de cache en verwijderingsmeldingen.

Zoals gewoonlijk zijn alle voorbeelden te vinden op GitHub.