Uvod u kofein

1. Uvod

U ovom ćemo članku pogledati kofein - a knjižnica za predmemoriranje visokih performansi za Javu.

Jedna temeljna razlika između predmemorije i a Karta jest da predmemorija izbacuje pohranjene stavke.

An politika deložacije odlučuje koje bi objekte trebalo brisati u bilo koje vrijeme. Ova politika izravno utječe na brzinu učitavanja predmemorije - presudna karakteristika predmemoriranja knjižnica.

Kofein koristi Prozor TinyLfu politika deložacije koja osigurava a gotovo optimalna stopa pogodaka.

2. Ovisnost

Moramo dodati kofein ovisnost o našoj pom.xml:

 com.github.ben-manes.caffeine kofein 2.5.5 

Možete pronaći najnoviju verziju kofein na Maven Central.

3. Populacijska predmemorija

Usredotočimo se na kofein tri strategije za populaciju predmemorije: ručno, sinkrono i asinkrono učitavanje.

Prvo napišite klasu za vrste vrijednosti koje ćemo pohraniti u našu predmemoriju:

klasa DataObject {privatni konačni String podaci; privatni statički int objectCounter = 0; // standardni konstruktori / getteri javni statički DataObject get (String podaci) {objectCounter ++; vratiti novi DataObject (podaci); }}

3.1. Ručno naseljavanje

U ovoj strategiji ručno stavljamo vrijednosti u predmemoriju i dohvaćamo ih kasnije.

Inicirajmo našu predmemoriju:

Predmemorija predmemorije = Caffeine.newBuilder () .expireAfterWrite (1, TimeUnit.MINUTES) .maximumSize (100) .build ();

Sada, možemo dobiti neku vrijednost iz predmemorije pomoću getIfPresent metoda. Ova metoda će se vratiti null ako vrijednost nije prisutna u predmemoriji:

Tipka niza = "A"; DataObject dataObject = cache.getIfPresent (ključ); assertNull (dataObject);

Možemo popuniti predmemoriju ručno pomoću staviti metoda:

cache.put (ključ, podatakObjekt); dataObject = cache.getIfPresent (ključ); assertNotNull (dataObject);

Vrijednost također možemo dobiti pomoću dobiti metoda, koji traje a Funkcija zajedno s ključem kao argumentom. Ova će se funkcija koristiti za pružanje zamjenske vrijednosti ako ključ nije prisutan u predmemoriji, koja će se umetnuti u predmemoriju nakon izračuna:

dataObject = cache .get (ključ, k -> DataObject.get ("Podaci za A")); assertNotNull (dataObject); assertEquals ("Podaci za A", dataObject.getData ());

The dobiti metoda izvodi proračun atomski. To znači da će se izračun izvršiti samo jednom - čak i ako nekoliko niti istovremeno traži vrijednost. Zato koristeći dobiti poželjno je getIfPresent.

Ponekad trebamo poništi neke predmemorirane vrijednosti ručno:

cache.invalidate (ključ); dataObject = cache.getIfPresent (ključ); assertNull (dataObject);

3.2. Sinkrono učitavanje

Ova metoda učitavanja predmemorije traje Funkcija, koji se koristi za inicijalizaciju vrijednosti, slično kao dobiti metoda ručne strategije. Pogledajmo kako to možemo iskoristiti.

Prije svega, moramo inicijalizirati našu predmemoriju:

Predmemorija LoadingCache = Caffeine.newBuilder () .maximumSize (100) .expireAfterWrite (1, TimeUnit.MINUTES) .build (k -> DataObject.get ("Podaci za" + k));

Sada vrijednosti možemo dohvatiti pomoću dobiti metoda:

DataObject dataObject = cache.get (ključ); assertNotNull (dataObject); assertEquals ("Podaci za" + ključ, dataObject.getData ());

Skup vrijednosti možemo dobiti i pomoću getAll metoda:

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

Vrijednosti se preuzimaju iz temeljne pozadinske inicijalizacije Funkcija koja je proslijeđena izgraditi metoda. To omogućuje upotrebu predmemorije kao glavne fasade za pristup vrijednostima.

3.3. Asinkrono učitavanje

Ova strategija radi isto kao i prethodni, ali izvodi operacije sinkrono i vraća a CompletableFuture drži stvarnu vrijednost:

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

Možemo koristiti dobiti i getAll metode, na isti način, uzimajući u obzir činjenicu da se vraćaju CompletableFuture:

Tipka niza = "A"; cache.get (ključ) .thenAccept (dataObject -> {assertNotNull (dataObject); assertEquals ("Podaci za" + ključ, dataObject.getData ());}); cache.getAll (Arrays.asList ("A", "B", "C")). thenAccept (dataObjectMap -> assertEquals (3, dataObjectMap.size ()));

CompletableFuture ima bogat i koristan API, o čemu možete pročitati više u ovom članku.

4. Iseljavanje vrijednosti

Kofein ima tri strategije za izbacivanje vrijednosti: zasnovan na veličini, vremenu i referenci.

4.1. Iseljavanje zasnovano na veličini

Ova vrsta deložacije pretpostavlja da deložacija se događa kada se premaši ograničenje konfigurirane veličine predmemorije. Tamo su dva načina dobivanja veličine - brojanje predmeta u predmemoriji ili dobivanje njihove težine.

Da vidimo kako bismo mogli broji predmete u predmemoriji. Kada se predmemorija inicijalizira, njezina veličina jednaka je nuli:

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

Kad dodamo vrijednost, veličina se očito povećava:

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

U predmemoriju možemo dodati drugu vrijednost, što dovodi do uklanjanja prve vrijednosti:

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

Vrijedno je spomenuti da mi nazovite počistiti prije dobivanja veličine predmemorije. To je zato što se izbacivanje predmemorije izvršava asinkrono i ova metoda pomaže u iščekivanju završetka deložacije.

Možemo i mi proći a vagaFunkcijada biste dobili veličinu predmemorije:

Predmemorija LoadingCache = Caffeine.newBuilder () .maximumWeight (10) .weigher ((k, v) -> 5) .build (k -> DataObject.get ("Podaci za" + k)); assertEquals (0, cache.estimatedSize ()); cache.get ("A"); assertEquals (1, cache.estimatedSize ()); cache.get ("B"); assertEquals (2, cache.estimatedSize ());

Vrijednosti se uklanjaju iz predmemorije kada je težina veća od 10:

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

4.2. Vremensko iseljenje

Ova strategija deložacije je na osnovu vremena isteka unosa i ima tri vrste:

  • Ističe nakon pristupa - unos ističe nakon prolaska razdoblja od zadnjeg čitanja ili pisanja
  • Ističe nakon pisanja - unos ističe nakon prolaska razdoblja od zadnjeg upisa
  • Prilagođena politika - vrijeme isteka izračunava se za svaki unos pojedinačno Istek provedba

Konfigurirajmo strategiju expire-after-access pomoću expireAfterAccess metoda:

Predmemorija LoadingCache = Caffeine.newBuilder () .expireAfterAccess (5, TimeUnit.MINUTES) .build (k -> DataObject.get ("Podaci za" + k));

Da bismo konfigurirali strategiju isteka nakon pisanja, koristimo expireAfterWrite metoda:

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

Da bismo inicijalizirali prilagođenu politiku, moramo implementirati Istek sučelje:

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 (String key, DataObject value, long currentTime, long currentDuration) {return currentDuration;} @Override public long expireAfterRead (String key, DataObject value, long currentTime, long currentDuration) {return currentDuration;}}). Build (k -> DataObject .get ("Podaci za" + k));

4.3. Deložacija temeljena na referencama

Svoju predmemoriju možemo konfigurirati tako da dopušta prikupljanje smeća ključeva i / ili vrijednosti predmemorije. Da bismo to učinili, konfigurirali bismo upotrebu Slaba Refrencija za ključeve i vrijednosti, a možemo konfigurirati i SoftReference samo za skupljanje vrijednosti smeća.

The Slaba Refrencija uporaba omogućuje prikupljanje smeća objekata kada na njega nema jakih referenci. SoftReference omogućuje prikupljanje smeća na temelju globalne strategije Najmanje nedavno korištenog JVM-a. Više detalja o referencama u Javi možete pronaći ovdje.

Trebali bismo koristiti Caffeine.weakKeys (), Caffeine.weakValues ​​(), i Kofein.softValues ​​() da biste omogućili svaku opciju:

Predmemorija LoadingCache = Caffeine.newBuilder () .expireAfterWrite (10, TimeUnit.SECONDS) .weakKeys () .weakValues ​​() .build (k -> DataObject.get ("Podaci za" + k)); predmemorija = Caffeine.newBuilder () .expireAfterWrite (10, TimeUnit.SECONDS) .softValues ​​() .build (k -> DataObject.get ("Podaci za" + k));

5. Osvježavajuće

Moguće je konfigurirati predmemoriju za automatsko osvježavanje unosa nakon određenog razdoblja. Pogledajmo kako to učiniti pomoću refreshAfterWrite metoda:

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

Ovdje bismo trebali razumjeti a razlika između expireAfter i refreshAfter. Kada se zatraži unos koji je istekao, izvršenje se blokira dok nova gradnja ne bi bila izračunata u gradnji Funkcija.

Ali ako unos ispunjava uvjete za osvježavanje, tada će predmemorija vratiti staru vrijednost i asinkrono ponovno učitati vrijednost.

6. Statistika

Kofein ima sredstvo za bilježenje statistike o korištenju predmemorije:

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

Mi također možemo prijeći u recordStats dobavljača, koji kreira implementaciju StatsCounter. Ovaj će se objekt gurati sa svakom promjenom vezanom uz statistiku.

7. Zaključak

U ovom smo se članku upoznali s Cacheine caching knjižnicom za Javu. Vidjeli smo kako konfigurirati i popuniti predmemoriju, kao i kako odabrati odgovarajuće pravilo isteka ili osvježavanja u skladu s našim potrebama.

Izvorni kod ovdje prikazan dostupan je na Githubu.