Programovanie

Vytvorte všeobecnú službu ukladania do medzipamäte na zlepšenie výkonu

Predpokladajme, že vás spolupracovník požiada o zoznam všetkých krajín sveta. Pretože nie ste žiadny odborník na geografiu, prechádzate na webovú stránku Organizácie Spojených národov, stiahnite si zoznam a vytlačte jej ju. Chce však iba preskúmať zoznam; vlastne to neberie so sebou. Pretože posledná vec, ktorú potrebujete, je ďalší kúsok papiera na stole, odovzdáte zoznam do skartovača.

O deň neskôr žiada ďalší spolupracovník to isté: zoznam všetkých krajín na svete. Nadávate si, že ste tento zoznam neuchovali, a opäť surfujete späť na webovú stránku OSN. Pri tejto návšteve webovej stránky si všimnete, že OSN aktualizuje zoznam krajín každých šesť mesiacov. Môžete si stiahnuť a vytlačiť zoznam svojich spolupracovníkov. Pozerá sa na to, ďakuje vám a zoznam opäť necháva na vás. Tentokrát tento zoznam založíte do správy v priloženej post-it poznámke, ktorá vám pripomína, že po 6 mesiacoch ho máte zahodiť.

Iste, v priebehu nasledujúcich týždňov vaši spolupracovníci budú zoznam vyžadovať znova a znova. Gratulujete si k vyplneniu dokumentu, pretože dokument môžete z registračnej skrinky extrahovať rýchlejšie ako z webovej stránky. Koncept vašej registratúry sa uchytí; čoskoro všetci začnú vkladať predmety do vašej skrinky. Aby ste zabránili neusporiadaniu skrinky, stanovili ste pokyny pre jej používanie. Vo vašej úradnej funkcii ako vedúci kartotéky, dáte svojim spolupracovníkom pokyn, aby na všetky dokumenty umiestnili štítky a post-it poznámky, ktoré identifikujú dokumenty a ich vyradenie / dátum vypršania platnosti. Štítky pomáhajú vašim spolupracovníkom nájsť dokument, ktorý hľadajú, a post-it notes kvalifikujú, či sú informácie aktuálne.

Kartotéka sa stáva tak populárnou, že do nej čoskoro nebudete môcť ukladať žiadne nové dokumenty. Musíte sa rozhodnúť, čo vyhodiť a čo si nechať. Aj keď vyhodíte všetky dokumenty, ktorým vypršala platnosť, skrinka stále prekypuje papierom. Ako sa rozhodnete, ktorých nevypršaných dokumentov sa zbaviť? Zahodíte najstarší dokument? Môžete vyhodiť najmenej často používané alebo najmenej nedávno použité; v obidvoch prípadoch budete potrebovať denník, ktorý bude uvedený v zozname pri prístupe ku každému dokumentu. Alebo by ste sa mohli rozhodnúť, ktoré dokumenty zlikvidovať, na základe iného determinantu; rozhodnutie je čisto osobné.

Ak chcete spojiť vyššie uvedenú analógiu so skutočným svetom s počítačovým svetom, kartotéka funguje ako vyrovnávacia pamäť: vysokorýchlostná pamäť, ktorá občas vyžaduje údržbu. Dokumenty v pamäti cache sú objekty vo vyrovnávacej pamäti, všetky zodpovedajú vami stanoveným normám, správca vyrovnávacej pamäte. Proces čistenia pamäte cache sa nazýva očistenie. Pretože položky vo vyrovnávacej pamäti sú vyčistené po uplynutí určitého času, vyrovnávacia pamäť sa nazýva a časovaná vyrovnávacia pamäť.

V tomto článku sa dozviete, ako vytvoriť stopercentne čistú vyrovnávaciu pamäť Java, ktorá používa anonymné vlákno na pozadí na vyčistenie položiek, ktorým uplynula platnosť. Uvidíte, ako vytvoriť takúto vyrovnávaciu pamäť, pričom pochopíte kompromisy spojené s rôznymi návrhmi.

Zostavte vyrovnávaciu pamäť

Dosť analógií registračných skriniek: poďme ďalej na webové stránky. Servery webových stránok sa tiež musia vyrovnať s ukladaním do pamäte cache. Servery opakovane dostávajú žiadosti o informácie, ktoré sú totožné s ostatnými požiadavkami. Pre svoju ďalšiu úlohu musíte vytvoriť internetovú aplikáciu pre jednu z najväčších svetových spoločností. Po štyroch mesiacoch vývoja, vrátane mnohých bezsenných nocí a príliš veľkého množstva Jolt colas, prechádza aplikácia do vývojového testovania s 1 000 používateľmi. Po testovaní vývoja nasleduje certifikačný test s 5 000 používateľmi a následná implementácia výroby pre 20 000 používateľov. Keď sa však vyskytne chyba nedostatku pamäte, zatiaľ čo aplikáciu otestuje iba 200 používateľov, vývojové testovanie sa zastaví.

Na rozpoznanie zdroja zníženia výkonu použijete produkt na profilovanie a zistíte, že server načítava viac kópií databázy Sada výsledkovs, z ktorých každá má niekoľko tisíc záznamov. Záznamy tvoria zoznam produktov. Zoznam produktov je navyše pre všetkých používateľov identický. Zoznam nezávisí od používateľa, ako by to mohol byť prípad, keby bol zoznam produktov výsledkom parametrizovaného dotazu. Rýchlo sa rozhodnete, že jedna kópia zoznamu môže slúžiť všetkým súbežným používateľom, a tak ju uložte do medzipamäte.

Vyvstáva však niekoľko otázok, ktoré zahŕňajú také zložitosti ako:

  • Čo ak sa zoznam produktov zmení? Ako môže cache vypršať platnosť zoznamov? Ako zistím, ako dlho by mal zoznam produktov zostať v pamäti cache, kým nevyprší jeho platnosť?
  • Čo ak existujú dva odlišné zoznamy produktov a tieto dva zoznamy sa menia v rôznych intervaloch? Môžem vypršať platnosť každého zoznamu jednotlivo alebo musia mať všetky rovnakú trvanlivosť?
  • Čo ak je medzipamäť prázdna a dvaja žiadatelia ju vyskúšajú súčasne? Keď to obaja nájdu prázdne, vytvoria si vlastné zoznamy a potom sa obaja pokúsia vložiť svoje kópie do vyrovnávacej pamäte?
  • Čo ak položky sedia v pamäti cache celé mesiace bez toho, aby k nim niekto získal prístup? Nezožerú pamäť?

Na riešenie týchto problémov je potrebné vytvoriť službu ukladania softvéru do medzipamäte.

Podľa analógie kartotéky ľudia vždy najskôr skontrolovali skrinku, keď hľadali dokumenty. Váš softvér musí implementovať rovnaký postup: pred načítaním nového zoznamu z databázy musí požiadavka skontrolovať službu ukladania do pamäte cache. Ako vývojár softvéru je vašou zodpovednosťou prístup do pamäte cache pred prístupom do databázy. Ak sa zoznam produktov už načítal do medzipamäte, použijete zoznam uložený v pamäti, ak nevypršala jeho platnosť. Ak sa zoznam produktov nenachádza v pamäti cache, načítate ju z databázy a okamžite ju uložíte do pamäte cache.

Poznámka: Predtým, ako pristúpite k požiadavkám a kódu služby ukladania do vyrovnávacej pamäte, možno budete chcieť skontrolovať bočný panel dole „Ukladanie do pamäte cache versus združovanie“. Vysvetľuje to združovanie, súvisiaci koncept.

Požiadavky

V súlade s princípmi dobrého návrhu som definoval zoznam požiadaviek na službu ukladania do pamäte cache, ktorú vyvinieme v tomto článku:

  1. K službe ukladania do pamäte cache má prístup akákoľvek aplikácia Java.
  2. Objekty je možné umiestniť do medzipamäte.
  3. Objekty je možné extrahovať z medzipamäte.
  4. Objekty v medzipamäti môžu sami určiť, kedy uplynie ich platnosť, čo umožňuje maximálnu flexibilitu. Služby ukladania do medzipamäte, ktorým vyprší platnosť všetkých objektov pomocou rovnakého vzorca vypršania platnosti, neposkytujú optimálne využitie objektov vo vyrovnávacej pamäti. Tento prístup je v rozsiahlych systémoch nedostatočný, pretože napríklad zoznam produktov sa môže meniť každý deň, zatiaľ čo zoznam umiestnení obchodov sa môže meniť iba raz za mesiac.
  5. Vlákno na pozadí, ktoré beží pod nízkou prioritou, odstraňuje objekty v pamäti, ktorých platnosť vypršala.
  6. Službu ukladania do pamäte cache je možné neskôr vylepšiť použitím najmenej nedávno používaného (LRU) alebo najmenej často používaného (LFU) čistiaceho mechanizmu.

Implementácia

Na splnenie požiadavky 1 používame 100% čisté prostredie Java. Poskytovaním verejnosti dostať a nastaviť metódami v kešovacej službe spĺňame aj požiadavky 2 a 3.

Pred pokračovaním v diskusii k požiadavke 4 stručne spomeniem, že požiadavku 5 splníme vytvorením anonymného vlákna v správcovi cache; toto vlákno začína v statickom bloku. Taktiež uspokojujeme požiadavku 6 identifikáciou bodov, do ktorých by sa neskôr mal pridať kód na implementáciu algoritmov LRU a LFU. O týchto požiadavkách sa budem podrobnejšie rozprávať ďalej v článku.

Teraz späť k požiadavke 4, kde sa veci stávajú zaujímavými. Ak každý objekt v pamäti musí sám určiť, či jeho platnosť vypršala, musíte mať spôsob, ako sa opýtať na objekt, ak už jeho platnosť skončila. To znamená, že všetky objekty v pamäti cache musia vyhovovať určitým pravidlám; toho v Jave dosiahnete implementáciou rozhrania.

Začnime pravidlami, ktoré upravujú objekty umiestnené v pamäti cache.

  1. Všetky objekty musia mať verejnú metódu nazvanú isExpired (), ktorá vracia boolovskú hodnotu.
  2. Všetky objekty musia mať verejnú metódu nazvanú getIdentifier (), ktorý vráti objekt, ktorý odlišuje objekt od všetkých ostatných v pamäti cache.

Poznámka: Pred priamym vstupom do kódu musíte pochopiť, že vyrovnávaciu pamäť môžete implementovať mnohými spôsobmi. Našiel som viac ako tucet rôznych implementácií. Enhydra a Caucho poskytujú vynikajúce zdroje, ktoré obsahujú niekoľko implementácií vyrovnávacej pamäte.

Kód rozhrania pre službu ukladania do vyrovnávacej pamäte tohto článku nájdete v zozname 1.

Zoznam 1. Cacheable.java

/ ** * Názov: Ukladanie do medzipamäte Popis: Toto rozhranie definuje metódy, ktoré musia byť implementované všetkými objektmi, ktoré chcú byť umiestnené v cache. * * Autorské práva: Copyright (c) 2001 * Spoločnosť: JavaWorld * FileName: Cacheable.java @author Jonathan Lurie @version 1.0 * / verejné rozhranie Cacheable {/ * Vyžadovaním toho, aby všetky objekty určovali svoje vlastné expirácie, je algoritmus abstrahovaný od služba ukladania do pamäte cache, čím poskytuje maximálnu flexibilitu, pretože každý objekt môže prijať inú stratégiu vypršania platnosti. * / public boolean isExpired (); / * Táto metóda zabezpečí, že služba ukladania do pamäte cache nie je zodpovedná za jednoznačnú identifikáciu objektov umiestnených v pamäti cache. * / public Object getIdentifier (); } 

Akýkoľvek objekt umiestnený v pamäti cache - a String, napríklad - musí byť zabalený do objektu, ktorý implementuje Ukladateľný do medzipamäte rozhranie. Výpis 2 je príkladom všeobecnej triedy wrapperov s názvom CachedObject; môže obsahovať akýkoľvek objekt, ktorý je potrebné umiestniť do kešovacej služby. Upozorňujeme, že táto trieda obálky implementuje Ukladateľný do medzipamäte rozhranie definované v zozname 1.

Zoznam 2. CachedManagerTestProgram.java

/ ** * Názov: Ukladanie do medzipamäte * Popis: Obal objektu Generic Cache Object. Implementuje rozhranie Cacheable * používa stratégiu TimeToLive na expiráciu CacheObject. * Copyright: Copyright (c) 2001 * Company: JavaWorld * Filename: CacheManagerTestProgram.java * @author Jonathan Lurie * @version 1.0 * / public class CachedObject implements Cacheable {// +++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++ / * Táto premenná sa použije na zistenie, či platnosť objektu skončila. * / private java.util.Date dateofExpiration = null; identifikátor súkromného objektu = null; / * Toto obsahuje skutočnú "hodnotu". Toto je objekt, ktorý je potrebné zdieľať. * / public Object object = null; // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++ verejné CachedObject (Object obj, Object id, int minutesToLive) {this.object = obj; this.identifier = id; // minutesToLive 0 znamená, že žije donekonečna. if (minutesToLive! = 0) {dateofExpiration = new java.util.Date (); java.util.Calendar cal = java.util.Calendar.getInstance (); cal.setTime (dateofExpiration); cal.add (cal.MINUTE, minutesToLive); dateofExpiration = cal.getTime (); }} // +++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++ public boolean isExpired () {// Pamätajte, že ak počet minút života je nula, potom to žije naveky! if (dateofExpiration! = null) {// dátum vypršania platnosti je porovnaný. if (dateofExpiration.before (new java.util.Date ())) {System.out.println ("CachedResultSet.isExpired: Expired from Cache! EXPIRE TIME:" + dateofExpiration.toString () + "CURRENT TIME:" + ( new java.util.Date ()). toString ()); návrat pravdivý; } else {System.out.println ("CachedResultSet.isExpired: Expired not from Cache!"); návrat nepravdivý; }} else // To znamená, že žije večne! návrat nepravdivý; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++ verejný objekt getIdentifier () {návratový identifikátor; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++} 

The CachedObject triedy vystavuje metódu konštruktora, ktorá má tri parametre:

public CachedObject (Object obj, Object id, int minutesToLive) 

Nasledujúca tabuľka popisuje tieto parametre.

Popisy parametrov konštruktora CachedObject
názovTypPopis
ObjObjektObjekt, ktorý je zdieľaný. Je definovaný ako objekt, ktorý umožňuje maximálnu flexibilitu.
IdObjektId obsahuje jedinečný identifikátor, ktorý odlišuje obj parameter zo všetkých ostatných objektov nachádzajúcich sa v pamäti cache. Služba ukladania do pamäte cache nie je zodpovedná za zabezpečenie jedinečnosti objektov v pamäti cache.
minutesToLiveIntPočet minút, ktoré obj parameter je platný v pamäti cache. V tejto implementácii služba ukladania do medzipamäte interpretuje hodnotu nula, čo znamená, že objektu nikdy nevyprší platnosť. Tento parameter možno budete chcieť zmeniť v prípade, že budete musieť expirovať objekty za menej ako jednu minútu.

Metóda konštruktora určuje dátum vypršania platnosti objektu v pamäti cache pomocou a čas do života stratégia. Ako už z jeho názvu vyplýva, čas do života znamená, že určitý objekt má stanovený čas, na konci ktorého je považovaný za mŕtvy. Pridaním minutesToLive, konštruktéra int parametra, k aktuálnemu času sa počíta dátum vypršania platnosti. Toto vypršanie platnosti je priradené premennej triedy dátum spotreby.

Teraz isExpired () metóda musí jednoducho určiť, či dátum spotreby je pred alebo po aktuálnom dátume a čase. Ak je dátum pred aktuálnym časom a objekt v pamäti sa považuje za vypršaný, isExpired () metóda vráti true; ak je dátum po aktuálnom čase, objektu v medzipamäti nevypršala platnosť a - isExpired () vráti nepravdivé. Samozrejme, ak dátum spotreby je null, čo by sa stalo, keby minutesToLive bola nula, potom je expirovaná () metóda vždy vráti hodnotu false, čo naznačuje, že objekt uložený v pamäti je stále.

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