Introduksjon til koffein

1. Introduksjon

I denne artikkelen skal vi ta en titt på koffein - a caching-bibliotek med høy ytelse for Java.

En grunnleggende forskjell mellom en cache og en Kart er at en cache kaster ut lagrede gjenstander.

An utkastelsespolitikk bestemmer hvilke objekter som skal slettes til enhver tid. Denne politikken påvirker direkte hurtigbuffers trefffrekvens - en avgjørende egenskap ved caching-biblioteker.

Koffein bruker Vindu TinyLfu utkastelsespolitikk, som gir en nesten optimal trefffrekvens.

2. Avhengighet

Vi må legge til koffein avhengighet til vår pom.xml:

 com.github.ben-manes.koffein koffein 2.5.5 

Du finner den siste versjonen av koffein på Maven Central.

3. Fyll ut hurtigbuffer

La oss fokusere på koffein tre strategier for hurtigbufferpopulasjon: manuell, synkron lasting og asynkron lasting.

La oss først skrive en klasse for hvilke verdityper vi lagrer i cachen vår:

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

3.1. Manuell innfylling

I denne strategien legger vi verdier manuelt i hurtigbufferen og henter dem senere.

La oss initialisere cachen vår:

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

Nå, vi kan få litt verdi fra hurtigbufferen ved hjelp av getIfPresent metode. Denne metoden vil komme tilbake null hvis verdien ikke er tilstede i hurtigbufferen:

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

Vi kan fyll ut hurtigbufferen manuelt ved hjelp av sette metode:

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

Vi kan også få verdien ved å bruke metode, som tar en Funksjon sammen med en nøkkel som argument. Denne funksjonen vil bli brukt til å gi tilbakevendingsverdien hvis nøkkelen ikke er tilstede i hurtigbufferen, som vil bli satt inn i hurtigbufferen etter beregning:

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

De metoden utfører beregningen atomisk. Dette betyr at beregningen bare gjøres en gang - selv om flere tråder ber om verdien samtidig. Derfor ved hjelp av er å foretrekke fremfor getIfPresent.

Noen ganger trenger vi det ugyldiggjøre noen bufrede verdier manuelt:

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

3.2. Synkron lasting

Denne metoden for å laste hurtigbufferen tar en Funksjon, som brukes til å initialisere verdier, i likhet med metode for manuell strategi. La oss se hvordan vi kan bruke det.

Først og fremst må vi initialisere cachen vår:

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

Nå kan vi hente verdiene ved hjelp av metode:

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

Vi kan også få et sett verdier ved hjelp av få alt metode:

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

Verdier blir hentet fra den underliggende back-end initialiseringen Funksjon som ble overført til bygge metode. Dette gjør det mulig å bruke hurtigbufferen som hovedfasade for å få tilgang til verdier.

3.3. Asynkron belastning

Denne strategien fungerer på samme måte som forrige, men utfører operasjoner asynkront og returnerer a Fullførbar fremtid holder den faktiske verdien:

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

Vi kan bruke og få alt metoder, på samme måte, med tanke på at de kommer tilbake Fullførbar fremtid:

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

Fullførbar fremtid har et rikt og nyttig API, som du kan lese mer om i denne artikkelen.

4. Utsetting av verdier

Koffein har tre strategier for verdiskasting: størrelsesbasert, tidsbasert og referansebasert.

4.1. Størrelsesbasert utkastelse

Denne typen utkastelse forutsetter det utkastelse skjer når den konfigurerte størrelsesgrensen for hurtigbufferen overskrides. Det er to måter å få størrelsen på - teller gjenstander i hurtigbufferen, eller får vekten.

La oss se hvordan vi kunne telle objekter i hurtigbufferen. Når hurtigbufferen initialiseres, er størrelsen lik null:

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

Når vi legger til en verdi, øker størrelsen åpenbart:

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

Vi kan legge til den andre verdien i hurtigbufferen, noe som fører til fjerning av den første verdien:

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

Det er verdt å nevne at vi Ring rydde opp metoden før du får bufferstørrelsen. Dette er fordi cacheutkastelsen utføres asynkront, og denne metoden hjelper til å avvente fullføringen av utkastelsen.

Vi kan også passere en veierFunksjonfor å få størrelsen på hurtigbufferen:

LoadingCache cache = Caffeine.newBuilder () .maximumWeight (10) .veier ((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 ());

Verdiene fjernes fra hurtigbufferen når vekten er over 10:

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

4.2. Tidsbasert utkastelse

Denne utkastelsesstrategien er basert på utløpstidspunktet for oppføringen og har tre typer:

  • Utløper etter tilgang - oppføringen er utløpt etter at perioden er gått siden siste lese- eller skriveprogram
  • Utløp etter skriving - oppføringen er utløpt etter at perioden er gått siden siste skriving skjedde
  • Tilpasset policy - en utløpstid beregnes for hver oppføring individuelt av Utløp gjennomføring

La oss konfigurere strategien for utløp etter tilgang ved hjelp av expireAfterAccess metode:

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

For å konfigurere utløp etter skrivestrategi bruker vi expireAfterWrite metode:

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

For å initialisere en tilpasset policy, må vi implementere Utløp grensesnitt:

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økkel, DataObject-verdi, lang strømTid, lang strømDurasjon) {returstrømDurasjon;} @ Overstyr offentlig lang utløpAfterRead (Strengnøkkel, DataObject-verdi, lang strømTid, lang strømDurasjon) {returstrømDurasjon;}}). Bygg (k -> DataObject .get ("Data for" + k));

4.3. Referansebasert utkastelse

Vi kan konfigurere cachen vår slik at den tillater det søppelinnsamling av hurtignøkler og / eller verdier. For å gjøre dette vil vi konfigurere bruken av WeakRefence for både nøkler og verdier, og vi kan konfigurere SoftReference kun for søppelinnsamling av verdier.

De WeakRefence bruk tillater søppeloppsamling av objekter når det ikke er noen sterke referanser til objektet. SoftReference tillater at gjenstander samles opp søppel basert på JVMs globale minst-nylig brukte strategi. Mer informasjon om referanser i Java finner du her.

Vi burde bruke Koffein. Svake nøkler (), Caffeine.weakValues ​​(), og Caffeine.softValues ​​() for å aktivere hvert alternativ:

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 mulig å konfigurere hurtigbufferen til å oppdatere oppføringer etter en definert periode automatisk. La oss se hvordan du gjør dette ved hjelp av refreshAfterWrite metode:

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

Her skal vi forstå a forskjell mellom utløper etter og refreshAfter. Når den utløpte oppføringen blir bedt om, blokkeres en kjøring til den nye verdien ville blitt beregnet av bygningen Funksjon.

Men hvis oppføringen er kvalifisert for forfriskning, vil hurtigbufferen returnere en gammel verdi og last verdien på nytt asynkront.

6. Statistikk

Koffein har et middel til registrerer statistikk om cache-bruk:

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å gå inn i recordStats leverandør, som skaper en implementering av StatsCounter. Dette objektet vil bli presset med hver statistikkrelaterte endring.

7. Konklusjon

I denne artikkelen ble vi kjent med koffeinbufferbiblioteket for Java. Vi så hvordan du konfigurerer og fyller ut en hurtigbuffer, samt hvordan du velger en passende utløps- eller oppdateringspolicy etter våre behov.

Kildekoden vist her er tilgjengelig på Github.


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