Sprievodca po Infinispane v Jave

1. Prehľad

V tejto príručke sa dozvieme o Infinispane, úložisku údajov kľúča / hodnoty v pamäti, ktoré sa dodáva s robustnejšou sadou funkcií ako iné nástroje toho istého výklenku.

Aby sme pochopili, ako to funguje, zostavíme jednoduchý projekt predstavujúci najbežnejšie funkcie a skontrolujeme, ako sa dajú použiť.

2. Nastavenie projektu

Aby sme to mohli využiť týmto spôsobom, budeme musieť pridať závislosť do našej pom.xml.

Najnovšiu verziu nájdete v úložisku Maven Central:

 org.infinispan infinispan-core 9.1.5.Final 

Všetky potrebné základné infraštruktúry budú odteraz riešené programovo.

3. CacheManager Nastaviť

The CacheManager je základom väčšiny funkcií, ktoré budeme používať. Funguje ako kontajner pre všetky deklarované pamäte cache, riadi ich životný cyklus a je zodpovedný za globálnu konfiguráciu.

Infinispan dodáva skutočne jednoduchý spôsob stavby CacheManager:

public DefaultCacheManager cacheManager () {vrátiť nový DefaultCacheManager (); }

Teraz sme pomocou nej schopní zostaviť svoje kešky.

4. Nastavenie vyrovnávacej pamäte

Cache je definovaná menom a konfiguráciou. Potrebnú konfiguráciu je možné vytvoriť pomocou triedy ConfigurationBuilder, ktoré sú už k dispozícii v našej triede.

Ak chcete otestovať naše pamäte cache, zostavíme jednoduchú metódu, ktorá simuluje niektoré náročné dotazy:

verejná trieda HelloWorldRepository {public String getHelloWorld () {try {System.out.println ("Vykonávam nejaký ťažký dopyt"); Závit. Spánok (1 000); } catch (InterruptedException e) {// ... e.printStackTrace (); } návrat "Hello World!"; }}

Aby bolo možné skontrolovať zmeny v našich keškách, poskytuje Infinispan jednoduchú anotáciu @ Poslucháč.

Pri definovaní našej vyrovnávacej pamäte môžeme odovzdať nejaký objekt so záujmom o každú udalosť, ktorá sa v ňom stane, a Infinispan ho na to upozorní pri spracovaní vyrovnávacej pamäte:

@Listener verejná trieda CacheListener {@CacheEntryCreated public void entryCreated (udalosť CacheEntryCreatedEvent) {this.printLog ("Pridanie kľúča" "+ event.getKey () +" 'do medzipamäte ", udalosť); } @CacheEntryExpired public void entryExpired (udalosť CacheEntryExpiredEvent) {this.printLog ("Platnosť kľúča" "+ event.getKey () +" 'z medzipamäte ", udalosť); } @CacheEntryVisited public void entryVisited (udalosť CacheEntryVisitedEvent) {this.printLog ("Key '" + event.getKey () + "' bol navštívený", udalosť); } @CacheEntryActivated public void entryActivated (udalosť CacheEntryActivatedEvent) {this.printLog ("Aktivačný kľúč '" + event.getKey () + "' v ​​pamäti cache", udalosť); } @CacheEntryPassivated public void entryPassivated (udalosť CacheEntryPassivatedEvent) {this.printLog ("Pasívny kľúč '" + event.getKey () + "' z cache", udalosť); } @CacheEntryLoaded public void entryLoaded (udalosť CacheEntryLoadedEvent) {this.printLog ("Načítava sa kľúč '" + event.getKey () + "' do cache", udalosť); } @CacheEntriesEvicated public void entriesEvicated (udalosť CacheEntriesEvicatedEvent) {StringBuilder builder = nový StringBuilder (); event.getEntries (). forEach ((kľúč, hodnota) -> builder.append (kľúč) .append (",")); System.out.println ("Vylúčenie nasledujúcich položiek z medzipamäte:" + builder.toString ()); } private void printLog (String log, CacheEntryEvent event) {if (! event.isPre ()) {System.out.println (log); }}}

Pred vytlačením našej správy skontrolujeme, či už došlo k udalosti, ktorá sa má informovať, pretože pre niektoré typy udalostí Infinispan odošle dve oznámenia: jedno pred a druhé hneď po spracovaní.

Poďme teraz vytvoriť metódu na spracovanie vytvárania vyrovnávacej pamäte za nás:

private Cache buildCache (reťazec cacheName, DefaultCacheManager cacheManager, poslucháč CacheListener, konfiguračná konfigurácia) {cacheManager.defineConfiguration (cacheName, konfigurácia); Vyrovnávacia pamäť cache = cacheManager.getCache (cacheName); cache.addListener (poslucháč); návratová keška; }

Všimnite si, ako odovzdávame konfiguráciu CacheManager, a potom použiť to isté cacheName získať objekt zodpovedajúci požadovanej pamäti cache. Všimnite si tiež, ako informujeme poslucháča o samotnom objekte cache.

Teraz skontrolujeme päť rôznych konfigurácií vyrovnávacej pamäte a uvidíme, ako ich môžeme nastaviť a čo najlepšie využiť.

4.1. Jednoduchá vyrovnávacia pamäť

Najjednoduchší typ vyrovnávacej pamäte je možné definovať v jednom riadku pomocou našej metódy buildCache:

public Cache simpleHelloWorldCache (DefaultCacheManager cacheManager, CacheListener listener) {return this.buildCache (SIMPLE_HELLO_WORLD_CACHE, cacheManager, listener, new ConfigurationBuilder (). build ()); }

Teraz môžeme postaviť Služby:

public String findSimpleHelloWorld () {String cacheKey = "simple-hello"; návrat simpleHelloWorldCache .computeIfAbsent (cacheKey, k -> repository.getHelloWorld ()); }

Všimnite si, ako používame vyrovnávaciu pamäť, najskôr skontrolujte, či je hľadaný záznam už uložený v pamäti. Ak nie je, budeme musieť zavolať nášmu Úložisko a potom ho uložte do medzipamäte.

Pridajme do našich testov jednoduchú metódu, aby sme načasovali naše metódy:

protected long timeThis (dodávateľ dodávateľa) {long millis = System.currentTimeMillis (); supply.get (); návrat System.currentTimeMillis () - millis; }

Pri jeho testovaní môžeme skontrolovať čas medzi vykonaním dvoch volaní metód:

@Test public void whenGetIsCalledTwoTimes_thenTheSecondShouldHitTheCache () {assertThat (timeThis (() -> helloWorldService.findSimpleHelloWorld ())) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findSimpleHelloWorld ())) .isLessThan (100); }

4.2. Vypršanie platnosti cache

Môžeme definovať vyrovnávaciu pamäť, v ktorej majú všetky záznamy životnosť, inými slovami, prvky budú po uplynutí daného obdobia z medzipamäte odstránené. Konfigurácia je celkom jednoduchá:

private Configuration expiringConfiguration () {return new ConfigurationBuilder (). expiration () .lifespan (1, TimeUnit.SECONDS) .build (); }

Teraz zostavujeme našu vyrovnávaciu pamäť pomocou vyššie uvedenej konfigurácie:

public Cache expiringHelloWorldCache (DefaultCacheManager cacheManager, CacheListener poslucháč) {návrat this.buildCache (EXPIRING_HELLO_WORLD_CACHE, cacheManager, listener, expiringConfiguration ()); }

A nakoniec to použite podobnou metódou z našej jednoduchej vyrovnávacej pamäte vyššie:

public String findSimpleHelloWorldInExpiringCache () {String cacheKey = "simple-hello"; Reťazec helloWorld = expiringHelloWorldCache.get (cacheKey); if (helloWorld == null) {helloWorld = repository.getHelloWorld (); expiringHelloWorldCache.put (cacheKey, helloWorld); } návrat helloWorld; }

Otestujme si svoje časy znova:

@Test public void whenGetIsCalledTwoTimesQuickly_thenTheSecondShouldHitTheCache () {assertThat (timeThis (() -> helloWorldService.findExpiringHelloWorld ())) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findExpiringHelloWorld ())) .isLessThan (100); }

Pri jeho spustení vidíme, že v rýchlom slede zasiahne cache. Na ukážku, že doba platnosti je relatívna k jej záznamu dať čas, vložme to do nášho záznamu:

@Test public void whenGetIsCalledTwiceSparsely_thenNeitherHitsTheCache () throws InterruptedException {assertThat (timeThis (() -> helloWorldService.findExpiringHelloWorld ())) .isGreaterThanOrEqualTo (1000); Závit. Spánok (1100); assertThat (timeThis (() -> helloWorldService.findExpiringHelloWorld ())) .isGreaterThanOrEqualTo (1000); }

Po vykonaní testu si všimnite, ako po danom čase náš záznam vypršal z cache. Môžeme to potvrdiť pohľadom na vytlačené riadky denníka od nášho poslucháča:

Vykonanie náročného dotazu Pridanie kľúča „simple-ahoj“ do medzipamäte Vypršanie platnosti kľúča „simple-ahoj“ z medzipamäte Vykonanie náročného dotazu Pridanie kľúča „simple-ahoj“ do medzipamäte

Upozorňujeme, že platnosť záznamu vypršala, keď sa k nemu pokúšame získať prístup. Infinispan kontroluje položku, ktorej platnosť vypršala, v dvoch momentoch: keď sa k nej pokúsime dostať, alebo keď vlákno kosačky skenuje vyrovnávaciu pamäť.

Vypršanie platnosti môžeme použiť aj v keškách bez neho v ich hlavnej konfigurácii. Metóda dať akceptuje viac argumentov:

simpleHelloWorldCache.put (cacheKey, helloWorld, 10, TimeUnit.SECONDS);

Alebo namiesto stanovenej životnosti môžeme dať nášmu vstupu maximum doba nečinnosti:

simpleHelloWorldCache.put (cacheKey, helloWorld, -1, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);

Použitím -1 k atribútu životnosti cache nevyprší jej platnosť, ale keď ju skombinujeme s 10 sekundami doba nečinnosti, povieme Infinispanu, aby vypršal platnosť tohto záznamu, pokiaľ nebude navštívený v tomto časovom rámci.

4.3. Vyradenie z vyrovnávacej pamäte

V Infinispane môžeme obmedziť počet záznamov v danej pamäti pomocou konfigurácia vysťahovania:

private Configuration evictingConfiguration () {return new ConfigurationBuilder () .memory (). evictionType (EvictionType.COUNT) .size (1) .build (); }

V tomto príklade obmedzujeme maximálny počet záznamov v tejto vyrovnávacej pamäti na jednu, čo znamená, že ak sa pokúsime zadať inú, bude z našej medzipamäte vylúčený.

Metóda je opäť podobná už tu uvedenej:

public String findEvictingHelloWorld (kľúč reťazca) {hodnota reťazca = evictingHelloWorldCache.get (kľúč); if (value == null) {value = repository.getHelloWorld (); evictingHelloWorldCache.put (kľúč, hodnota); } návratová hodnota; }

Vytvorme si náš test:

@ Test public void whenTwoAreAdded_thenFirstShouldntBeAvailable () {assertThat (timeThis (() -> helloWorldService.findEvictingHelloWorld ("kľúč 1")))) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findEvictingHelloWorld ("kľúč 2")))) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findEvictingHelloWorld ("kľúč 1")))) .isGreaterThanOrEqualTo (1000); }

Po vykonaní testu sa môžeme pozrieť na náš denník aktivít:

Vykonanie náročného dotazu Pridanie kľúča „kľúč 1“ do medzipamäte Vykonanie náročného dotazu Vylúčenie nasledujúcich položiek z medzipamäte: kľúč 1, Pridanie kľúča „kľúč 2“ do medzipamäte Vykonanie náročného dotazu Vylúčenie nasledujúcich záznamov z medzipamäte: kľúč 2, Pridanie kľúča 1 'do medzipamäte

Skontrolujte, ako sa prvý kľúč automaticky odstránil z medzipamäte, keď sme vložili druhý, a potom sa druhý kľúč tiež odstránil, aby sa opäť získal priestor pre náš prvý kľúč.

4.4. Pasivačná vyrovnávacia pamäť

The pasivácia cache je jednou z výkonných funkcií programu Infinispan. Kombináciou pasivácie a vysťahovania môžeme vytvoriť medzipamäť, ktorá nezaberie veľa pamäte bez straty informácií.

Pozrime sa na konfiguráciu pasivácie:

private Configuration passivatingConfiguration () {return new ConfigurationBuilder () .memory (). evictionType (EvictionType.COUNT) .size (1) .persistence () .passivation (true) // aktivácia pasivácie .addSingleFileStore () // v jednom súbore .purgeOnStartup (true) // vyčistí súbor pri štarte .location (System.getProperty ("java.io.tmpdir")) .build (); }

Opäť vynútime iba jeden záznam v našej pamäti cache, ale povieme Infinispanu, aby pasivoval zvyšné záznamy, nielen aby ich odstránil.

Pozrime sa, čo sa stane, keď sa pokúsime vyplniť viac ako jeden záznam:

public String findPassivatingHelloWorld (String key) {return passivatingHelloWorldCache.computeIfAbsent (key, k -> repository.getHelloWorld ()); }

Vytvorme náš test a spustime ho:

@Test public void whenTwoAreAdded_thenTheFirstShouldBeAvailable () {assertThat (timeThis (() -> helloWorldService.findPassivatingHelloWorld ("kľúč 1")))) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findPassivatingHelloWorld ("kľúč 2")))) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findPassivatingHelloWorld ("kľúč 1")))) .isLessThan (100); }

Teraz sa pozrime na naše aktivity poslucháčov:

Vykonanie náročného dotazu Pridanie kľúča „kľúč 1“ do medzipamäte Vykonanie náročného dotazu Pasivácia kľúča „kľúč 1“ z medzipamäte Vylúčenie nasledujúcich položiek z medzipamäte: kľúč 1, Pridanie kľúča „kľúč 2“ do medzipamäte Pasivácia kľúča „kľúč 2“ z medzipamäte Evicting nasledujúce položky z medzipamäte: kľúč 2, načítanie kľúča „kľúč 1“ do medzipamäte Aktivačný kľúč „kľúč 1“ v pamäti cache Bol navštívený kľúč „kľúč 1“

Všimnite si, koľko krokov bolo potrebných na to, aby sme udržali našu pamäť cache iba s jedným záznamom. Všimnite si tiež poradie krokov - pasivácia, vysťahovanie a následné načítanie s následnou aktiváciou. Pozrime sa, čo tieto kroky znamenajú:

  • Pasivácia - náš záznam je uložený na inom mieste, mimo sieťového úložiska Infinispanu (v tomto prípade pamäte)
  • Vysťahovanie - položka je odstránená, aby sa uvoľnila pamäť a zachoval sa nakonfigurovaný maximálny počet položiek v pamäti cache
  • Načítava - pri pokuse o prístup k nášmu pasivovanému záznamu Infinispan skontroluje, či je v ňom uložený obsah, a znova načíta záznam do pamäte
  • Aktivácia - záznam je teraz opäť prístupný v Infinispane

4.5. Transakčná medzipamäť

Infinispan sa dodáva s výkonnou kontrolou transakcií. Rovnako ako databázový náprotivok je užitočné pri udržiavaní integrity, keď sa viac ako jedno vlákno pokúša zapísať rovnaký záznam.

Pozrime sa, ako môžeme definovať medzipamäť s transakčnými schopnosťami:

private Configuration transactionalConfiguration () {return new ConfigurationBuilder () .transaction (). transactionMode (TransactionMode.TRANSACTIONAL) .lockingMode (LockingMode.PESSIMISTIC) .build (); }

Aby sme to mohli otestovať, vytvorme dve metódy - jednu, ktorá rýchlo dokončí svoju transakciu, a jednu, ktorá chvíľu trvá:

verejné celé číslo getQuickHowManyVisits () {TransactionManager tm = transactionalCache .getAdvancedCache (). getTransactionManager (); tm.begin (); Celé číslo howManyVisits = transactionalCache.get (KEY); howManyVisits ++; System.out.println ("Pokúsim sa nastaviť HowManyVisits na" + howManyVisits); Stopky hodinky = nové Stopky (); watch.start (); transactionalCache.put (KEY, howManyVisits); watch.stop (); System.out.println ("Podarilo sa mi nastaviť HowManyVisits na" + howManyVisits + "po čakaní" + watch.getTotalTimeSeconds () + "sekundy"); tm.commit (); vrátiť howManyVisits; }
public void startBackgroundBatch () {TransactionManager tm = transactionalCache .getAdvancedCache (). getTransactionManager (); tm.begin (); transactionalCache.put (KEY, 1000); System.out.println ("HowManyVisits by teraz mal byť 1 000," + "ale transakciu zadržiavame"); Závit. Spánok (1 000 L); tm.rollback (); System.out.println ("Pomalá dávka sa vrátila späť"); }

Teraz vytvorme test, ktorý vykoná obe metódy a skontrolujeme, ako sa bude chovať Infinispan:

@ Test public void whenLockingAnEntry_thenItShouldBeInaccessible () vyvolá InterruptedException {Runnable backGroundJob = () -> transactionalService.startBackgroundBatch (); Thread backgroundThread = nové vlákno (backGroundJob); transactionalService.getQuickHowManyVisits (); backgroundThread.start (); Závit. Spánok (100); // počkáme, kým sa naše vlákno zahreje assertThat (timeThis (() -> transactionalService.getQuickHowManyVisits ())) .isGreaterThan (500) .isLessThan (1000); }

Po jeho vykonaní sa v našej konzole znova zobrazia nasledujúce aktivity:

Pridanie kľúča „kľúča“ do medzipamäte Kľúč „kľúč“ bol navštívený Pokúsil som sa nastaviť HowManyVisits na 1 Bol som schopný nastaviť HowManyVisits na 1 po čakaní 0,001 sekundy HowManyVisits by teraz mal byť 1000, ale držíme transakciu Kľúč „kľúč“ bol navštívený Pokúsim sa nastaviť HowManyVisits na 2 Bol som schopný nastaviť HowManyVisits na 2 po čakaní 0,902 sekundy Pomalá dávka sa pretočila

Skontrolujte čas v hlavnom vlákne a čakajte na koniec transakcie vytvorenej pomalou metódou.

5. Záver

V tomto článku sme videli, čo je Infinispan a jeho poprednými vlastnosťami a schopnosťami sú medzipamäť v aplikácii.

Ako vždy, kód nájdete na Githube.


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