Introduktion til koffein

1. Introduktion

I denne artikel skal vi se på koffein - a højtydende cachebibliotek til Java.

En grundlæggende forskel mellem en cache og en Kort er, at en cache udsætter lagrede genstande.

En udsættelsespolitik beslutter, hvilke objekter der skal slettes til enhver tid. Denne politik påvirker direkte cacheens hitrate - en afgørende egenskab ved cachebiblioteker.

Koffein bruger Vindue TinyLfu udsættelsespolitik, som giver en næsten optimal hitrate.

2. Afhængighed

Vi er nødt til at tilføje koffein afhængighed af vores pom.xml:

 com.github.ben-manes. koffein koffein 2.5.5 

Du kan finde den nyeste version af koffein på Maven Central.

3. Udfyld cache

Lad os fokusere på koffein tre strategier for cache-population: manuel, synkron indlæsning og asynkron indlæsning.

Lad os først skrive en klasse for de typer værdier, som vi gemmer i vores cache:

klasse DataObject {private final String data; privat statisk int objectCounter = 0; // standardkonstruktører / getters offentlig statisk DataObject get (String data) {objectCounter ++; returner nyt DataObject (data); }}

3.1. Manuel udfyldning

I denne strategi sætter vi værdier manuelt i cachen og henter dem senere.

Lad os initialisere vores cache:

Cache cache = Caffeine.newBuilder () .expireAfterWrite (1, TimeUnit.MINUTES) .maximumSize (100) .build ();

Nu, vi kan få noget værdi fra cachen ved hjælp af getIfPresent metode. Denne metode vender tilbage nul hvis værdien ikke er til stede i cachen:

Strengnøgle = "A"; DataObject dataObject = cache.getIfPresent (nøgle); assertNull (dataObject);

Vi kan udfyld cachen manuelt ved hjælp af sætte metode:

cache.put (nøgle, dataObject); dataObject = cache.getIfPresent (nøgle); assertNotNull (dataObject);

Vi kan også få værdien ved hjælp af metode, som tager en Fungere sammen med en nøgle som argument. Denne funktion vil blive brugt til at give reserveværdien, hvis nøglen ikke er til stede i cachen, som ville blive indsat i cachen efter beregning:

dataObject = cache .get (nøgle, k -> DataObject.get ("Data for A")); assertNotNull (dataObject); assertEquals ("Data for A", dataObject.getData ());

Det metode udfører beregningen atomisk. Dette betyder, at beregningen kun foretages en gang - selvom flere tråde beder om værdien samtidigt. Derfor ved brug af er at foretrække frem for getIfPresent.

Nogle gange har vi brug for det ugyldiggøre nogle cachelagrede værdier manuelt:

cache.invalidate (nøgle); dataObject = cache.getIfPresent (nøgle); assertNull (dataObject);

3.2. Synkron indlæsning

Denne metode til at indlæse cachen tager en Fungere, som bruges til initialisering af værdier svarende til metode til manuel strategi. Lad os se, hvordan vi kan bruge det.

Først og fremmest skal vi initialisere vores cache:

LoadingCache cache = Caffeine.newBuilder () .maximumSize (100) .expireAfterWrite (1, TimeUnit.MINUTES) .build (k -> DataObject.get ("Data for" + k));

Nu kan vi hente værdierne ved hjælp af metode:

DataObject dataObject = cache.get (nøgle); assertNotNull (dataObject); assertEquals ("Data for" + nøgle, dataObject.getData ());

Vi kan også få et sæt værdier ved hjælp af getAll metode:

KortdataObjectMap = cache.getAll (Arrays.asList ("A", "B", "C")); assertEquals (3, dataObjectMap.size ());

Værdier hentes fra den underliggende back-end initialisering Fungere der blev videregivet til bygge metode. Dette gør det muligt at bruge cachen som hovedfacade til at få adgang til værdier.

3.3. Asynkron belastning

Denne strategi fungerer det samme som det foregående, men udfører operationer asynkront og returnerer a Fuldført holder den faktiske værdi:

AsyncLoadingCache cache = Caffeine.newBuilder () .maximumSize (100) .expireAfterWrite (1, TimeUnit.MINUTES) .buildAsync (k -> DataObject.get ("Data for" + k));

Vi kan brug og getAll metoderpå samme måde under hensyntagen til, at de vender tilbage Fuldført:

Strengnøgle = "A"; cache.get (nøgle) .thenAccept (dataObject -> {assertNotNull (dataObject); assertEquals ("Data for" + nøgle, dataObject.getData ());}); cache.getAll (Arrays.asList ("A", "B", "C")). derefterAccept (dataObjectMap -> assertEquals (3, dataObjectMap.size ()));

Fuldført har en rig og nyttig API, som du kan læse mere om i denne artikel.

4. Udvisning af værdier

Koffein har tre strategier for værdiudkastning: størrelsesbaseret, tidsbaseret og referencebaseret.

4.1. Størrelsesbaseret udsættelse

Denne form for udsættelse forudsætter det udsættelse opstår, når den konfigurerede størrelsesgrænse for cachen overskrides. Der er to måder at få størrelsen på - tæller objekter i cachen eller får deres vægt.

Lad os se, hvordan vi kunne tæl objekter i cachen. Når cachen initialiseres, er dens størrelse lig med nul:

LoadingCache cache = Caffeine.newBuilder () .maximumSize (1) .build (k -> DataObject.get ("Data for" + k)); assertEquals (0, cache.estimatedSize ());

Når vi tilføjer en værdi, øges størrelsen naturligvis:

cache.get ("A"); assertEquals (1, cache.estimatedSize ());

Vi kan tilføje den anden værdi til cachen, hvilket fører til fjernelse af den første værdi:

cache.get ("B"); cache.cleanUp (); assertEquals (1, cache.estimatedSize ());

Det er værd at nævne, at vi ring til Ryd op metode, før du får cache-størrelsen. Dette skyldes, at cacheudkastet udføres asynkront, og denne metode hjælper med at afvente færdiggørelsen.

Det kan vi også videregive en vejerFungerefor at få størrelsen på cachen:

LoadingCache cache = Caffeine.newBuilder () .maximumWeight (10) .vægt ((k, v) -> 5) .build (k -> DataObject.get ("Data for" + k)); assertEquals (0, cache.estimatedSize ()); cache.get ("A"); assertEquals (1, cache.estimatedSize ()); cache.get ("B"); assertEquals (2, cache.estimatedSize ());

Værdierne fjernes fra cachen, når vægten er over 10:

cache.get ("C"); cache.cleanUp (); assertEquals (2, cache.estimatedSize ());

4.2. Tidsbaseret udsættelse

Denne udsættelsesstrategi er baseret på udløbstidspunktet for posten og har tre typer:

  • Udløb efter adgang - indtastning udløber, når perioden er gået, siden sidste læsning eller skrivning fandt sted
  • Udløber efter skrivning - indtastning udløber, når perioden er gået, siden sidste skrivning fandt sted
  • Brugerdefineret politik - en udløbstid beregnes for hver post individuelt af Udløb implementering

Lad os konfigurere strategien for udløb efter adgang ved hjælp af expireAfterAccess metode:

LoadingCache cache = Caffeine.newBuilder () .expireAfterAccess (5, TimeUnit.MINUTES) .build (k -> DataObject.get ("Data for" + k));

For at konfigurere udløbs-efter-skriv-strategi bruger vi expireAfterWrite metode:

cache = Caffeine.newBuilder () .expireAfterWrite (10, TimeUnit.SECONDS) .weakKeys () .weakValues ​​() .build (k -> DataObject.get ("Data for" + k));

For at initialisere en brugerdefineret politik er vi nødt til at implementere Udløb grænseflade:

cache = Caffeine.newBuilder (). expireAfter (new Expiry () {@Override public long expireAfterCreate (String key, DataObject value, long currentTime) {return value.getData (). length () * 1000;} @Override public long expireAfterUpdate (Strengnøgle, DataObject-værdi, lang strømTid, lang strømDuration) {return currentDuration;} @Override public long expireAfterRead (String key, DataObject value, long currentTime, long currentDuration) {return currentDuration;}}). Build (k -> DataObject .get ("Data for" + k));

4.3. Referencebaseret udsættelse

Vi kan konfigurere vores cache til at tillade det affaldssamling af cache nøgler og / eller værdier. For at gøre dette konfigurerer vi brugen af WeakRefence for både nøgler og værdier, og vi kan konfigurere SoftReference kun til affaldsindsamling af værdier.

Det WeakRefence brug tillader affaldssamling af objekter, når der ikke er nogen stærke referencer til objektet. SoftReference tillader genstande at blive indsamlet affald baseret på den globale strategi, der er brugt mindst for nylig i JVM. Flere detaljer om referencer i Java kan findes her.

Vi burde bruge Koffein. Svage nøgler (), Caffeine.weakValues ​​(), og Caffeine.softValues ​​() for at aktivere hver indstilling:

LoadingCache cache = Caffeine.newBuilder () .expireAfterWrite (10, TimeUnit.SECONDS) .weakKeys () .weakValues ​​() .build (k -> DataObject.get ("Data for" + k)); cache = Caffeine.newBuilder () .expireAfterWrite (10, TimeUnit.SECONDS) .softValues ​​() .build (k -> DataObject.get ("Data for" + k));

5. Forfriskende

Det er muligt at konfigurere cachen til automatisk at opdatere poster efter en defineret periode. Lad os se, hvordan du gør dette ved hjælp af refreshAfterWrite metode:

Caffeine.newBuilder () .refreshAfterWrite (1, TimeUnit.MINUTES) .build (k -> DataObject.get ("Data for" + k));

Her skal vi forstå en forskel mellem udløber efter og opdaterEfter. Når den udløbne post anmodes, blokeres en udførelse, indtil den nye værdi ville være beregnet af bygningen Fungere.

Men hvis posten er kvalificeret til forfriskning, ville cachen returnere en gammel værdi og genindlæs værdien asynkront.

6. Statistik

Koffein har et middel til optagelse af statistik om cache-brug:

LoadingCache cache = Caffeine.newBuilder () .maximumSize (100) .recordStats () .build (k -> DataObject.get ("Data for" + k)); cache.get ("A"); cache.get ("A"); assertEquals (1, cache.stats (). hitCount ()); assertEquals (1, cache.stats (). missCount ());

Vi kan også passere ind recordStats leverandør, hvilket skaber en implementering af StatsCounter. Dette objekt skubbes med hver statistikrelateret ændring.

7. Konklusion

I denne artikel blev vi bekendt med Caffeine caching-biblioteket til Java. Vi så, hvordan man konfigurerer og udfylder en cache, samt hvordan man vælger en passende udløbs- eller opdateringspolitik efter vores behov.

Kildekoden vist her er tilgængelig på Github.


$config[zx-auto] not found$config[zx-overlay] not found