Hvernig og hvers vegna við skrifuðum háhlaða skalanlega þjónustu fyrir 1C: Enterprise: Java, PostgreSQL, Hazelcast

Í þessari grein munum við tala um hvernig og hvers vegna við þróuðum Samskiptakerfi - vélbúnaður sem flytur upplýsingar á milli viðskiptavinaforrita og 1C:Enterprise netþjóna - frá því að setja verkefni til að hugsa í gegnum arkitektúr og útfærsluupplýsingar.

Samskiptakerfið (hér eftir nefnt SV) er dreift, villuþolið skilaboðakerfi með trygga afhendingu. SV er hönnuð sem háhlaða þjónusta með mikla sveigjanleika, fáanleg bæði sem netþjónusta (veitt af 1C) og sem fjöldaframleidd vara sem hægt er að nota á eigin netþjónaaðstöðu.

SV notar dreifða geymslu hazelcast og leitarvél Elasticsearch. Við munum líka tala um Java og hvernig við mælikvarða PostgreSQL lárétt.
Hvernig og hvers vegna við skrifuðum háhlaða skalanlega þjónustu fyrir 1C: Enterprise: Java, PostgreSQL, Hazelcast

Samsetning vandans

Til að gera það ljóst hvers vegna við bjuggum til víxlverkunarkerfið, skal ég segja þér aðeins frá því hvernig þróun viðskiptaforrita í 1C virkar.

Til að byrja með, smá um okkur fyrir þá sem ekki vita enn hvað við gerum :) Við erum að búa til 1C:Enterprise tæknivettvanginn. Vettvangurinn inniheldur þróunarverkfæri fyrir viðskiptaforrit, sem og keyrslutíma sem gerir viðskiptaforritum kleift að keyra í þvert á palla umhverfi.

Þróunarhugmynd viðskiptavinar-miðlara

Viðskiptaforrit búin til á 1C:Enterprise starfa á þremur stigum biðlara-þjónn arkitektúr "DBMS - forritaþjónn - viðskiptavinur". Umsóknarkóði skrifaður inn innbyggt 1C tungumál, er hægt að keyra á forritaþjóninum eða á biðlaranum. Öll vinna með forritahluti (möppur, skjöl o.s.frv.), sem og lestur og ritun gagnagrunnsins, fer eingöngu fram á þjóninum. Virkni eyðublaða og skipanaviðmóts er einnig útfært á þjóninum. Viðskiptavinurinn framkvæmir móttöku, opnun og birtingu eyðublaða, „samskipti“ við notandann (viðvaranir, spurningar...), smáútreikninga á eyðublöðum sem krefjast skjótra viðbragða (til dæmis margfalda verðið með magni), vinna með staðbundnar skrár, vinna með tæki.

Í forritakóða verða hausar verklagsreglna og aðgerða að gefa skýrt til kynna hvar kóðinn verður keyrður - með &AtClient / &AtServer tilskipunum (&AtClient / &AtServer á enskri útgáfu tungumálsins). 1C forritarar munu nú leiðrétta mig með því að segja að tilskipanir séu það í raun больше, en fyrir okkur er þetta ekki mikilvægt núna.

Þú getur hringt í miðlarakóða úr kóða viðskiptavinar, en þú getur ekki hringt í kóða viðskiptavinar frá kóða miðlara. Þetta er grundvallartakmörkun sem við gerðum af ýmsum ástæðum. Sérstaklega vegna þess að miðlarakóði verður að vera skrifaður á þann hátt að hann keyri á sama hátt, sama hvar hann er kallaður - frá biðlara eða frá netþjóni. Og ef hringt er í netþjónskóða frá öðrum netþjónskóða, þá er enginn viðskiptavinur sem slíkur. Og vegna þess að meðan á keyrslu miðlarakóðans stendur gæti viðskiptavinurinn sem hringdi í hann lokað, farið úr forritinu og þjónninn hefði ekki lengur neinn til að hringja í.

Hvernig og hvers vegna við skrifuðum háhlaða skalanlega þjónustu fyrir 1C: Enterprise: Java, PostgreSQL, Hazelcast
Kóði sem sér um smelli á hnapp: að hringja í netþjónsferli frá biðlara mun virka, að hringja í biðlaraferli frá miðlara mun ekki

Þetta þýðir að ef við viljum senda einhver skilaboð frá þjóninum til biðlaraforritsins, til dæmis um að gerð „langtíma“ skýrslu sé lokið og hægt sé að skoða skýrsluna, höfum við ekki slíka aðferð. Þú verður að nota brellur, til dæmis, skoða reglulega netþjóninn frá kóðanum. En þessi aðferð hleður kerfinu með óþarfa símtölum og lítur almennt ekki mjög glæsilegur út.

Og það er líka þörf, til dæmis þegar símtal berst SIP- þegar hringt er skal tilkynna um þetta til viðskiptavinaforrits svo það geti notað númer þess sem hringir til að finna það í gagnaðila gagnaðila og sýnt notanda upplýsingar um mótaðila sem hringir. Eða, til dæmis, þegar pöntun berst á vöruhúsið, tilkynntu umsókn viðskiptavinar um þetta. Almennt séð eru mörg tilvik þar sem slíkt fyrirkomulag væri gagnlegt.

Framleiðslan sjálf

Búðu til skilaboðakerfi. Hratt, áreiðanlegt, með trygga afhendingu, með getu til að leita að skilaboðum á sveigjanlegan hátt. Byggt á vélbúnaðinum, útfærðu boðbera (skilaboð, myndsímtöl) sem keyrir í 1C forritum.

Hannaðu kerfið þannig að það sé lárétt skalanlegt. Aukið álag verður að ná með því að fjölga hnútum.

Framkvæmd

Við ákváðum að samþætta ekki miðlarahluta SV beint inn í 1C:Enterprise pallinn, heldur innleiða hann sem sérstaka vöru, sem hægt er að kalla API frá kóða 1C forritalausna. Þetta var gert af ýmsum ástæðum, sú helsta var að ég vildi gera það mögulegt að skiptast á skilaboðum á milli mismunandi 1C forrita (til dæmis milli viðskiptastjórnunar og bókhalds). Mismunandi 1C forrit geta keyrt á mismunandi útgáfum af 1C:Enterprise pallinum, verið staðsett á mismunandi netþjónum osfrv. Við slíkar aðstæður er útfærsla á SV sem sérstakri vöru staðsett „við hlið“ 1C uppsetningar ákjósanlega lausnin.

Þannig að við ákváðum að búa til SV sem sérstaka vöru. Við mælum með því að lítil fyrirtæki noti CB netþjóninn sem við settum upp í skýinu okkar (wss://1cdialog.com) til að forðast kostnaðarkostnað sem tengist staðbundinni uppsetningu og uppsetningu netþjónsins. Stórum viðskiptavinum gæti fundist ráðlegt að setja upp sinn eigin CB netþjón á aðstöðu sinni. Við notuðum svipaða nálgun í SaaS skýinu okkar 1cFerskt - það er framleitt sem fjöldaframleidd vara til uppsetningar á stöðum viðskiptavina, og er einnig notað í skýinu okkar https://1cfresh.com/.

umsókn

Til að dreifa álaginu og bilanaþolinu munum við setja ekki eitt Java forrit, heldur nokkur, með álagsjafnara fyrir framan sig. Ef þú þarft að flytja skilaboð frá hnút til hnút, notaðu birta/ gerast áskrifandi í Hazelcast.

Samskipti milli viðskiptavinarins og netþjónsins eru í gegnum nettengi. Það hentar vel fyrir rauntímakerfi.

Dreift skyndiminni

Við völdum á milli Redis, Hazelcast og Ehcache. Það er 2015. Redis gaf bara út nýjan klasa (of nýr, skelfilegur), það er Sentinel með fullt af takmörkunum. Ehcache veit ekki hvernig á að setja saman í klasa (þessi virkni birtist síðar). Við ákváðum að prófa það með Hazelcast 3.4.
Hazelcast er sett saman í klasa úr kassanum. Í stakan hnút er það ekki mjög gagnlegt og er aðeins hægt að nota sem skyndiminni - það veit ekki hvernig á að henda gögnum á diskinn, ef þú tapar eina hnútnum taparðu gögnunum. Við setjum upp nokkra Hazelcasts, á milli þess sem við afritum mikilvæg gögn. Við tökum ekki öryggisafrit af skyndiminni - okkur er sama um það.

Fyrir okkur er Hazelcast:

  • Geymsla notendalota. Það tekur langan tíma að fara í gagnagrunninn fyrir lotu í hvert skipti, þannig að við setjum allar loturnar í Hazelcast.
  • Skyndiminni. Ef þú ert að leita að notendasniði skaltu athuga skyndiminni. Skrifaði ný skilaboð - settu þau í skyndiminni.
  • Efni fyrir samskipti milli forritatilvika. Hnúturinn býr til atburð og setur hann í Hazelcast efnið. Aðrir forritahnútar sem gerast áskrifendur að þessu efni taka á móti og vinna úr viðburðinum.
  • Klasalásar. Til dæmis búum við til umræðu með því að nota einstakan lykil (einstök umræða innan 1C gagnagrunnsins):

conversationKeyChecker.check("БЕНЗОКОЛОНКА");

      doInClusterLock("БЕНЗОКОЛОНКА", () -> {

          conversationKeyChecker.check("БЕНЗОКОЛОНКА");

          createChannel("БЕНЗОКОЛОНКА");
      });

Við athuguðum að það er engin rás. Við tókum lásinn, skoðuðum hann aftur og bjuggum til. Ef þú athugar ekki lásinn eftir að hafa tekið lásinn, þá er möguleiki á að annar þráður hafi einnig athugað á því augnabliki og reyni nú að búa til sömu umræðu - en hún er þegar til. Þú getur ekki læst með samstilltum eða venjulegum Java Lock. Í gegnum gagnagrunninn - það er hægt og það er samúð fyrir gagnagrunninn; í gegnum Hazelcast - það er það sem þú þarft.

Að velja DBMS

Við höfum mikla og farsæla reynslu af því að vinna með PostgreSQL og í samstarfi við hönnuði þessa DBMS.

Það er ekki auðvelt með PostgreSQL klasa - það er til XL, XC, Citus, en almennt séð eru þetta ekki NoSQL sem skalast út úr kassanum. Við litum ekki á NoSQL sem aðalgeymsluna; það var nóg að við tókum Hazelcast, sem við höfðum ekki unnið með áður.

Ef þú þarft að skala venslagagnagrunn þýðir það riftun. Eins og þú veist þá skiptum við gagnagrunninum niður í aðskilda hluta með klippingu þannig að hægt sé að setja hvern þeirra á sérstakan netþjón.

Fyrsta útgáfan af klippingu okkar gerði ráð fyrir getu til að dreifa hverri töflu umsóknar okkar á mismunandi netþjóna í mismunandi hlutföllum. Það eru fullt af skilaboðum á þjóni A - vinsamlegast, við skulum færa hluta af þessari töflu yfir á þjón B. Þessi ákvörðun öskraði einfaldlega um ótímabæra hagræðingu, svo við ákváðum að takmarka okkur við fjölleigjenda nálgun.

Þú getur lesið um fjölleigjanda, til dæmis, á vefsíðunni Citus Gögn.

SV hefur hugtökin umsókn og áskrifandi. Forrit er ákveðin uppsetning á viðskiptaforriti, svo sem ERP eða bókhaldi, með notendum þess og viðskiptagögnum. Áskrifandi er stofnun eða einstaklingur fyrir hönd þess sem forritið er skráð á SV netþjóni. Áskrifandi getur haft nokkur forrit skráð og þessi forrit geta skipt á skilaboðum sín á milli. Áskrifandinn varð leigjandi í kerfinu okkar. Skilaboð frá nokkrum áskrifendum geta verið staðsett í einum líkamlegum gagnagrunni; ef við sjáum að áskrifandi er farinn að búa til mikla umferð færum við hann yfir í sérstakan líkamlegan gagnagrunn (eða jafnvel sérstakan gagnagrunnsþjón).

Við erum með aðalgagnagrunn þar sem leiðartafla er geymd með upplýsingum um staðsetningu allra áskrifendagagnagrunna.

Hvernig og hvers vegna við skrifuðum háhlaða skalanlega þjónustu fyrir 1C: Enterprise: Java, PostgreSQL, Hazelcast

Til að koma í veg fyrir að aðalgagnagrunnurinn sé flöskuháls geymum við leiðartöfluna (og önnur oft nauðsynleg gögn) í skyndiminni.

Ef gagnagrunnur áskrifandans fer að hægjast munum við klippa hann niður í skipting inni. Í öðrum verkefnum sem við notum pg_pathman.

Þar sem það er slæmt að tapa notendaskilaboðum, höldum við gagnagrunnum okkar með eftirlíkingum. Sambland af samstilltum og ósamstilltum eftirmyndum gerir þér kleift að tryggja þig ef þú tapar aðalgagnagrunninum. Skilaboðatap mun aðeins eiga sér stað ef aðalgagnagrunnurinn og samstilltur eftirmynd hans mistakast samtímis.

Ef samstillt eftirmynd glatast verður ósamstillta eftirmyndin samstillt.
Ef aðalgagnagrunnurinn tapast verður samstillta eftirmyndin aðalgagnagrunnurinn og ósamstillta eftirmyndin verður samstillt eftirmynd.

Elasticsearch fyrir leit

Þar sem SV er meðal annars einnig boðberi krefst það hraðvirkrar, þægilegrar og sveigjanlegrar leitar, að teknu tilliti til formfræði, með ónákvæmum samsvörun. Við ákváðum að finna ekki upp hjólið aftur og nota ókeypis leitarvélina Elasticsearch, búin til út frá bókasafninu Lucene. Við sendum einnig Elasticsearch í þyrping (master – gögn – gögn) til að koma í veg fyrir vandamál ef bilun verður í forritahnútum.

Á github fundum við Rússneska formfræði viðbót fyrir Elasticsearch og notaðu það. Í Elasticsearch vísitölunni geymum við orðrætur (sem viðbótin ákvarðar) og N-grömm. Þegar notandinn slær inn texta til að leita leitum við að innrituðum texta meðal N-grömm. Þegar það er vistað í skránni verður orðið „textar“ skipt í eftirfarandi N-grömm:

[þeir, tek, tex, textar, textar, ek, ex, ext, textar, ks, kst, ksty, st, sty, þú],

Og rót orðsins „texti“ verður einnig varðveitt. Þessi aðferð gerir þér kleift að leita í upphafi, í miðju og í lok orðsins.

Heildar mynd

Hvernig og hvers vegna við skrifuðum háhlaða skalanlega þjónustu fyrir 1C: Enterprise: Java, PostgreSQL, Hazelcast
Endurtekið myndina frá upphafi greinarinnar, en með skýringum:

  • Balancer afhjúpaður á netinu; við erum með nginx, það getur verið hvaða sem er.
  • Java forritatilvik hafa samskipti sín á milli í gegnum Hazelcast.
  • Til að vinna með vefinnstungu sem við notum Netty.
  • Java forritið er skrifað í Java 8 og samanstendur af búntum OSGi. Áætlanirnar innihalda flutning yfir í Java 10 og umskipti yfir í einingar.

Þróun og prófun

Í því ferli að þróa og prófa SV, fundum við ýmsa áhugaverða eiginleika vörunnar sem við notum.

Álagsprófun og minnisleki

Útgáfa hverrar SV útgáfu felur í sér álagsprófun. Það er árangursríkt þegar:

  • Prófið virkaði í nokkra daga og engar þjónustubilanir komu upp
  • Viðbragðstími fyrir lykilaðgerðir fór ekki yfir þægilegan þröskuld
  • Afköst versnandi miðað við fyrri útgáfu er ekki meira en 10%

Við fyllum prófunargagnagrunninn af gögnum - til að gera þetta fáum við upplýsingar um virkasta áskrifandann frá framleiðsluþjóninum, margföldum tölur hans með 5 (fjölda skilaboða, umræður, notendur) og prófum það þannig.

Við framkvæmum álagsprófanir á samskiptakerfinu í þremur stillingum:

  1. álagspróf
  2. Aðeins tengingar
  3. Skráning áskrifenda

Á meðan á álagsprófinu stendur setjum við af stað nokkur hundruð þræði og þeir hlaða upp kerfinu án þess að stoppa: skrifa skilaboð, búa til umræður, fá lista yfir skilaboð. Við líkjum eftir aðgerðum venjulegra notenda (fáðu lista yfir ólesin skilaboð, skrifa einhverjum) og hugbúnaðarlausnum (senda pakka með annarri uppsetningu, vinna úr viðvörun).

Til dæmis, þetta er hvernig hluti af streituprófinu lítur út:

  • Notandi skráir sig inn
    • Óskar eftir ólesnum umræðum þínum
    • 50% líkur á að lesa skilaboð
    • 50% líkur á að senda skilaboð
    • Næsti notandi:
      • Hefur 20% líkur á að skapa nýja umræðu
      • Velur af handahófi einhverjar umræður sínar
      • Fer inn
      • Beiðnir um skilaboð, notendasnið
      • Býr til fimm skilaboð beint til handahófsnota úr þessari umræðu
      • Skilur eftir umræðu
      • Endurtekið 20 sinnum
      • Skráir sig út, fer aftur í upphaf handritsins

    • Spjallbotni fer inn í kerfið (líkir eftir skilaboðum frá forritakóða)
      • Hefur 50% líkur á að búa til nýja rás fyrir gagnaskipti (sérstök umræða)
      • 50% líkur á að skrifa skilaboð á einhverja af núverandi rásum

Atburðarásin „Aðeins tengingar“ birtist af ástæðu. Það er staða: notendur hafa tengt kerfið, en hafa ekki enn tekið þátt. Hver notandi kveikir á tölvunni klukkan 09:00 á morgnana, kemur á tengingu við netþjóninn og þegir. Þessir krakkar eru hættulegir, þeir eru margir - einu pakkarnir sem þeir eru með eru PING/PONG, en þeir halda tengingunni við netþjóninn (þeir geta ekki haldið því áfram - hvað ef það eru ný skilaboð). Prófið endurskapar aðstæður þar sem mikill fjöldi slíkra notenda reynir að skrá sig inn í kerfið á hálftíma. Það er svipað og álagspróf, en áhersla þess er einmitt á þetta fyrsta inntak - þannig að það eru engar bilanir (maður notar ekki kerfið, og það fellur þegar af - það er erfitt að hugsa um eitthvað verra).

Skráningarhandritið fyrir áskrifendur hefst frá fyrstu kynningu. Við gerðum álagspróf og vorum viss um að kerfið hægði ekki á sér í bréfaskiptum. En notendur komu og skráningin fór að mistakast vegna tímaleysis. Við skráningu notuðum við / dev / random, sem tengist óreiðu kerfisins. Þjónninn hafði ekki tíma til að safna nægilega óreiðu og þegar beðið var um nýtt SecureRandom, fraus hann í tugi sekúndna. Það eru margar leiðir út úr þessu ástandi, til dæmis: skiptu yfir í óörugga /dev/urandom, settu upp sérstakt borð sem myndar óreiðu, búðu til handahófskenndar tölur fyrirfram og geymdu þær í laug. Við lokuðum tímabundið vandamálinu með sundlaugina en síðan þá höfum við verið að keyra sérstakt próf til að skrá nýja áskrifendur.

Við notum sem álagsgjafa JMeter. Það veit ekki hvernig á að vinna með websocket; það þarf viðbót. Fyrstu leitarniðurstöðurnar fyrir fyrirspurnina „jmeter websocket“ eru: greinar frá BlazeMeter, sem mæla með viðbót frá Maciej Zaleski.

Þar ákváðum við að byrja.

Næstum strax eftir að alvarlegar prófanir hófust, komumst við að því að JMeter byrjaði að leka minni.

Viðbótin er sérstök stór saga; með 176 stjörnum hefur það 132 gaffla á github. Höfundurinn sjálfur hefur ekki skuldbundið sig til þess síðan 2015 (við tókum það árið 2015, þá vakti það ekki grunsemdir), nokkur github vandamál varðandi minnisleka, 7 ólokaðar dráttarbeiðnir.
Ef þú ákveður að framkvæma álagsprófun með því að nota þetta viðbætur, vinsamlegast gaum að eftirfarandi umræðum:

  1. Í fjölþráðu umhverfi var notaður venjulegur LinkedList og niðurstaðan varð NPE í keyrslutíma. Þetta er hægt að leysa annað hvort með því að skipta yfir í ConcurrentLinkedDeque eða með samstilltum blokkum. Við völdum fyrsta kostinn fyrir okkur (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/43).
  2. Minnisleki; þegar aftengt er er ekki eytt tengingarupplýsingum (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/44).
  3. Í straumspilun (þegar veftengi er ekki lokað í lok sýnis, en er notað síðar í áætluninni), virka svarmynstur ekki (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/19).

Þetta er ein af þeim á github. Það sem við gerðum:

  1. Hefur tekið gaffli Elyran Kogan (@elyrank) – það lagar vandamál 1 og 3
  2. Leyst vandamál 2
  3. Uppfærð bryggja frá 9.2.14 til 9.3.12
  4. Vafið SimpleDateFormat í ThreadLocal; SimpleDateFormat er ekki þráðaröruggt, sem leiddi til NPE á keyrslutíma
  5. Lagaði annan minnisleka (tengingunni var lokað á rangan hátt þegar það var aftengt)

Og samt rennur það!

Minnið byrjaði að klárast ekki á einum degi, heldur á tveimur. Það var nákvæmlega enginn tími eftir, svo við ákváðum að setja af stað færri þræði, en á fjórum umboðsmönnum. Þetta hefði átt að duga í að minnsta kosti viku.

Tveir dagar liðnir...

Nú er minnið að klárast í Hazelcast. Logarnir sýndu að eftir nokkra daga prófun byrjaði Hazelcast að kvarta yfir minnisleysi og eftir nokkurn tíma féll þyrpingin í sundur og hnúðarnir héldu áfram að deyja einn af öðrum. Við tengdum JVisualVM við hazelcast og sáum „rísandi sög“ - hún kallaði reglulega GC, en gat ekki hreinsað minnið.

Hvernig og hvers vegna við skrifuðum háhlaða skalanlega þjónustu fyrir 1C: Enterprise: Java, PostgreSQL, Hazelcast

Það kom í ljós að í hazelcast 3.4, þegar eytt er korti / multiMap (map.destroy()), losnar minnið ekki alveg:

github.com/hazelcast/hazelcast/issues/6317
github.com/hazelcast/hazelcast/issues/4888

Villan er nú lagfærð í 3.5, en það var vandamál þá. Við bjuggum til ný fjölkort með kraftmiklum nöfnum og eyddum þeim í samræmi við rökfræði okkar. Kóðinn leit einhvern veginn svona út:

public void join(Authentication auth, String sub) {
    MultiMap<UUID, Authentication> sessions = instance.getMultiMap(sub);
    sessions.put(auth.getUserId(), auth);
}

public void leave(Authentication auth, String sub) {
    MultiMap<UUID, Authentication> sessions = instance.getMultiMap(sub);
    sessions.remove(auth.getUserId(), auth);

    if (sessions.size() == 0) {
        sessions.destroy();
    }
}

Hringdu:

service.join(auth1, "НОВЫЕ_СООБЩЕНИЯ_В_ОБСУЖДЕНИИ_UUID1");
service.join(auth2, "НОВЫЕ_СООБЩЕНИЯ_В_ОБСУЖДЕНИИ_UUID1");

multiMap var búið til fyrir hverja áskrift og eytt þegar þess var ekki þörf. Við ákváðum að byrja á Map , lykillinn verður nafn áskriftarinnar og gildin verða lotuauðkenni (sem þú getur síðan fengið notendaauðkenni, ef þörf krefur).

public void join(Authentication auth, String sub) {
    addValueToMap(sub, auth.getSessionId());
}

public void leave(Authentication auth, String sub) { 
    removeValueFromMap(sub, auth.getSessionId());
}

Kortin hafa batnað.

Hvernig og hvers vegna við skrifuðum háhlaða skalanlega þjónustu fyrir 1C: Enterprise: Java, PostgreSQL, Hazelcast

Hvað annað höfum við lært um álagspróf?

  1. JSR223 þarf að vera skrifað í grófum stíl og innihalda safnskyndiminni - það er miklu hraðvirkara. Link.
  2. Jmeter-Plugins graf eru auðveldari að skilja en venjuleg. Link.

Um reynslu okkar af Hazelcast

Hazelcast var ný vara fyrir okkur, við byrjuðum að vinna með það frá útgáfu 3.4.1, nú keyrir framleiðsluþjónninn okkar útgáfu 3.9.2 (þegar þetta er skrifað er nýjasta útgáfan af Hazelcast 3.10).

ID kynslóð

Við byrjuðum á heiltöluauðkennum. Ímyndum okkur að við þurfum annan Long fyrir nýja aðila. Röð í gagnagrunninum hentar ekki, töflurnar taka þátt í sundrun - það kemur í ljós að það er skilaboð ID=1 í DB1 og skilaboð ID=1 í DB2, þú getur ekki sett þetta auðkenni í Elasticsearch, né í Hazelcast , en það versta er ef þú vilt sameina gögnin úr tveimur gagnagrunnum í einn (til dæmis að ákveða að einn gagnagrunnur sé nóg fyrir þessa áskrifendur). Þú getur bætt nokkrum AtomicLongs við Hazelcast og geymt teljarann ​​þar, þá er árangurinn við að fá nýtt auðkenni incrementAndGet plús tíminn fyrir beiðni til Hazelcast. En Hazelcast hefur eitthvað ákjósanlegra - FlakeIdGenerator. Þegar þeir hafa samband við hvern viðskiptavin fá þeir auðkennisbil, til dæmis það fyrsta - frá 1 til 10, hið síðara - frá 000 til 10, og svo framvegis. Nú getur viðskiptavinurinn gefið út ný auðkenni á eigin spýtur þar til sviðinu sem gefið er út til hans lýkur. Það virkar fljótt, en þegar þú endurræsir forritið (og Hazelcast biðlarann) byrjar ný röð - þess vegna sleppingar o.s.frv. Að auki skilja verktaki ekki í raun hvers vegna auðkennin eru heiltölur, en eru svo ósamræmi. Við vógum allt og skiptum yfir í UUID.

Við the vegur, fyrir þá sem vilja vera eins og Twitter, það er svona Snowcast bókasafn - þetta er útfærsla af Snowflake ofan á Hazelcast. Þú getur skoðað það hér:

github.com/noctarius/snowcast
github.com/twitter/snjókorn

En við höfum ekki komist að því lengur.

TransactionalMap.replace

Annað á óvart: TransactionalMap.replace virkar ekki. Hér er próf:

@Test
public void replaceInMap_putsAndGetsInsideTransaction() {

    hazelcastInstance.executeTransaction(context -> {
        HazelcastTransactionContextHolder.setContext(context);
        try {
            context.getMap("map").put("key", "oldValue");
            context.getMap("map").replace("key", "oldValue", "newValue");
            
            String value = (String) context.getMap("map").get("key");
            assertEquals("newValue", value);

            return null;
        } finally {
            HazelcastTransactionContextHolder.clearContext();
        }        
    });
}

Expected : newValue
Actual : oldValue

Ég þurfti að skrifa mitt eigið skipti með getForUpdate:

protected <K,V> boolean replaceInMap(String mapName, K key, V oldValue, V newValue) {
    TransactionalTaskContext context = HazelcastTransactionContextHolder.getContext();
    if (context != null) {
        log.trace("[CACHE] Replacing value in a transactional map");
        TransactionalMap<K, V> map = context.getMap(mapName);
        V value = map.getForUpdate(key);
        if (oldValue.equals(value)) {
            map.put(key, newValue);
            return true;
        }

        return false;
    }
    log.trace("[CACHE] Replacing value in a not transactional map");
    IMap<K, V> map = hazelcastInstance.getMap(mapName);
    return map.replace(key, oldValue, newValue);
}

Prófaðu ekki aðeins venjulega gagnagerð heldur einnig viðskiptaútgáfur þeirra. Það kemur fyrir að IMap virkar, en TransactionalMap er ekki lengur til.

Settu inn nýja JAR án niður í miðbæ

Í fyrsta lagi ákváðum við að taka upp hluti úr bekkjum okkar í Hazelcast. Til dæmis höfum við forritaflokk, við viljum vista og lesa hann. Vista:

IMap<UUID, Application> map = hazelcastInstance.getMap("application");
map.set(id, application);

Við lesum:

IMap<UUID, Application> map = hazelcastInstance.getMap("application");
return map.get(id);

Allt er að virka. Síðan ákváðum við að búa til vísitölu í Hazelcast til að leita eftir:

map.addIndex("subscriberId", false);

Og þegar þeir skrifuðu nýjan aðila byrjuðu þeir að fá ClassNotFoundException. Hazelcast reyndi að bæta við vísitöluna, en vissi ekkert um bekkinn okkar og vildi fá JAR með þessum flokki til hans. Við gerðum það, allt virkaði, en nýtt vandamál birtist: hvernig á að uppfæra JAR án þess að stöðva þyrpinguna algjörlega? Hazelcast tekur ekki upp nýja JAR meðan á hnút-fyrir-hnút uppfærslu stendur. Á þessum tímapunkti ákváðum við að við gætum lifað án vísitöluleitar. Þegar öllu er á botninn hvolft, ef þú notar Hazelcast sem verslun með lykilgildi, mun allt virka? Eiginlega ekki. Hér er aftur hegðun IMap og TransactionalMap öðruvísi. Þar sem IMap er sama, sendir TransactionalMap villu.

IMap. Við skrifum 5000 hluti, lesum þá. Allt er gert ráð fyrir.

@Test
void get5000() {
    IMap<UUID, Application> map = hazelcastInstance.getMap("application");
    UUID subscriberId = UUID.randomUUID();

    for (int i = 0; i < 5000; i++) {
        UUID id = UUID.randomUUID();
        String title = RandomStringUtils.random(5);
        Application application = new Application(id, title, subscriberId);
        
        map.set(id, application);
        Application retrieved = map.get(id);
        assertEquals(id, retrieved.getId());
    }
}

En það virkar ekki í viðskiptum, við fáum ClassNotFoundException:

@Test
void get_transaction() {
    IMap<UUID, Application> map = hazelcastInstance.getMap("application_t");
    UUID subscriberId = UUID.randomUUID();
    UUID id = UUID.randomUUID();

    Application application = new Application(id, "qwer", subscriberId);
    map.set(id, application);
    
    Application retrievedOutside = map.get(id);
    assertEquals(id, retrievedOutside.getId());

    hazelcastInstance.executeTransaction(context -> {
        HazelcastTransactionContextHolder.setContext(context);
        try {
            TransactionalMap<UUID, Application> transactionalMap = context.getMap("application_t");
            Application retrievedInside = transactionalMap.get(id);

            assertEquals(id, retrievedInside.getId());
            return null;
        } finally {
            HazelcastTransactionContextHolder.clearContext();
        }
    });
}

Í 3.8 birtist User Class Dreifing vélbúnaðurinn. Þú getur tilnefnt einn aðalhnút og uppfært JAR skrána á honum.

Nú höfum við gjörbreytt nálgun okkar: við raðum því sjálf í JSON og vistum það í Hazelcast. Hazelcast þarf ekki að þekkja uppbyggingu flokkanna okkar og við getum uppfært án niður í miðbæ. Útgáfa lénshluta er stjórnað af forritinu. Mismunandi útgáfur af forritinu geta verið í gangi á sama tíma og sú staða getur komið upp þegar nýja forritið skrifar hluti með nýjum reitum, en það gamla veit ekki um þessa reiti ennþá. Og á sama tíma les nýja forritið hluti sem eru skrifaðir af gamla forritinu sem eru ekki með nýja reiti. Við tökum á slíkum aðstæðum innan forritsins, en til einföldunar breytum við ekki eða eyðum reitum, við stækkum aðeins flokkana með því að bæta við nýjum sviðum.

Hvernig við tryggjum mikla afköst

Fjórar ferðir til Hazelcast - góðar, tvær í gagnagrunninn - slæmar

Að fara í skyndiminni fyrir gögn er alltaf betra en að fara í gagnagrunninn, en þú vilt ekki heldur geyma ónotaðar færslur. Við látum ákvörðun um hvað á að vista í skyndiminni þar til á síðasta stigi þróunar. Þegar nýja virknin er kóðuð kveikjum við á skráningu allra fyrirspurna í PostgreSQL (log_min_duration_statement til 0) og keyrum álagsprófun í 20 mínútur. Með því að nota safnaða annála geta tól eins og pgFouine og pgBadger byggt upp greiningarskýrslur. Í skýrslum leitum við fyrst og fremst að hægum og tíðum fyrirspurnum. Fyrir hægar fyrirspurnir smíðum við framkvæmdaáætlun (EXPLAIN) og metum hvort hægt sé að flýta fyrir slíkri fyrirspurn. Tíðar beiðnir um sömu inntaksgögn passa vel inn í skyndiminni. Við reynum að halda fyrirspurnum „flötum“, einni töflu fyrir hverja fyrirspurn.

Nýting

SV sem netþjónusta var tekin í notkun vorið 2017 og sem sérstök vara kom SV út í nóvember 2017 (á þeim tíma í beta útgáfu).

Í meira en starfsári hafa ekki verið alvarleg vandamál í rekstri CB netþjónustunnar. Við fylgjumst með netþjónustunni í gegnum Zabbix, safna og dreifa frá Bambus.

SV netþjónadreifingin er afhent í formi innfæddra pakka: RPM, DEB, MSI. Auk þess fyrir Windows bjóðum við upp á eitt uppsetningarforrit í formi eins EXE sem setur upp netþjóninn, Hazelcast og Elasticsearch á einni vél. Við vísuðum upphaflega til þessarar útgáfu af uppsetningunni sem „demo“ útgáfuna, en nú hefur komið í ljós að þetta er vinsælasti uppsetningarvalkosturinn.

Heimild: www.habr.com

Bæta við athugasemd