Nola eta zergatik idatzi genuen karga handiko zerbitzu eskalagarria 1Crako: Enterprise: Java, PostgreSQL, Hazelcast

Artikulu honetan nola eta zergatik garatu garen hitz egingo dugu Interakzio Sistema – Bezero-aplikazioen eta 1C:Enterprise zerbitzarien artean informazioa transferitzen duen mekanismoa, zeregin bat ezartzetik arkitektura eta ezarpenaren xehetasunak pentsatzeraino.

Interaction System (aurrerantzean SV deitzen dena) banatzen den mezularitza-sistema bat da, akatsekiko tolerantziarekin, entrega bermatua duena. SV eskalagarritasun handiko karga handiko zerbitzu gisa diseinatu da, bai lineako zerbitzu gisa (1C-k emandakoa) bai zure zerbitzari-instalazioetan hedatu daitekeen masa-ekoiztutako produktu gisa.

SV-k biltegiratze banatua erabiltzen du Hazelcast eta bilatzailea Elasticsearch. Javari buruz eta PostgreSQL horizontalki nola eskalatzen dugun ere hitz egingo dugu.
Nola eta zergatik idatzi genuen karga handiko zerbitzu eskalagarria 1Crako: Enterprise: Java, PostgreSQL, Hazelcast

Arazoaren formulazioa

Interaction System zergatik sortu dugun argi uzteko, 1C-n negozio-aplikazioen garapenak nola funtzionatzen duen azalduko dizut.

Hasteko, guri buruz pixka bat zer egiten dugun oraindik ez dakienentzat :) 1C:Enterprise plataforma teknologikoa egiten ari gara. Plataformak negozio-aplikazioak garatzeko tresna bat dakar, baita negozio-aplikazioak plataforma anitzeko ingurune batean exekutatzeko aukera ematen duen exekuzio-denbora ere.

Bezero-zerbitzariaren garapen paradigma

1C:Enterprise-n sortutako negozio-aplikazioek hiru mailatan funtzionatzen dute bezero-zerbitzaria arkitektura “DBMS – aplikazio zerbitzaria – bezeroa”. Aplikazioaren kodea idatzita 1C hizkuntza integratua, aplikazioen zerbitzarian edo bezeroan exekutatu daiteke. Aplikazio-objektuekin lan guztiak (direktorioak, dokumentuak, etab.), baita datu-basea irakurtzea eta idaztea ere, zerbitzarian bakarrik egiten dira. Inprimakien eta komandoen interfazearen funtzionaltasuna ere zerbitzarian inplementatzen da. Bezeroak inprimakiak jaso, ireki eta bistaratzea, erabiltzailearekin “komunikatzea” (abisuak, galderak...), erantzun azkarra eskatzen duten inprimakietan kalkulu txikiak egiten ditu (adibidez, prezioa kantitatez biderkatuz), tokiko fitxategiekin lan egiten du, ekipoekin lan egitea.

Aplikazioaren kodean, prozeduren eta funtzioen goiburuek berariaz adierazi behar dute non exekutatuko den kodea - &AtClient / &AtServer zuzentarauak erabiliz (&AtClient / &AtServer hizkuntzaren ingelesezko bertsioan). 1C garatzaileek zuzenduko naute zuzentarauak benetan direla esanez baino gehiago, baina guretzat hori ez da garrantzitsua orain.

Zerbitzari-kodeari dei diezaiokezu bezero-kodetik, baina ezin diozu bezero-kodeari deitu zerbitzari-kodetik. Arrazoi batzuengatik egin dugun oinarrizko muga da. Bereziki, zerbitzariaren kodea idatzi behar baita modu berean exekutatzen den edozein lekutan deitzen den - bezerotik edo zerbitzaritik. Eta zerbitzari-kodea beste zerbitzari-kode batetik deitzen denean, ez dago bezerorik. Eta zerbitzariaren kodea exekutatzen ari den bitartean, hura deitu zuen bezeroak itxi egin zezakeen, aplikaziotik irten eta zerbitzariak ez luke inor deitzeko izango.

Nola eta zergatik idatzi genuen karga handiko zerbitzu eskalagarria 1Crako: Enterprise: Java, PostgreSQL, Hazelcast
Botoi-klik bat prozesatzen duen kodea: bezerotik zerbitzari-prozedura bati deitzeak funtzionatuko du, zerbitzaritik bezero-prozedura deitzeak ez du.

Horrek esan nahi du zerbitzaritik bezeroaren aplikaziora mezuren bat bidali nahi badugu, adibidez, “luzeko” txostenaren sorrera amaitu dela eta txostena ikusi daitekeela, ez dugula horrelako metodorik. Trikimailuak erabili behar dituzu, adibidez, aldian-aldian zerbitzariari galdeketa bezeroaren kodeatik. Baina ikuspegi honek sistema alferrikako deiekin kargatzen du eta, oro har, ez du itxura oso dotorea.

Eta beharra ere badago, adibidez, telefono dei bat iristen denean SIP- Dei bat egiterakoan, horren berri eman bezeroaren aplikazioari, deitzailearen zenbakia erabil dezan kontrapartearen datu-basean aurkitzeko eta erabiltzaileari dei egiten dion kontraparteari buruzko informazioa erakusteko. Edo, adibidez, eskaera bat biltegira iristen denean, horren berri eman bezeroaren bezeroaren aplikazioari. Orokorrean, mekanismo hori erabilgarria izango litzatekeen kasu asko daude.

Ekoizpena bera

Sortu mezularitza-mekanismo bat. Azkarra, fidagarria, entrega bermatuta, mezuak malgutasunez bilatzeko gaitasunarekin. Mekanismoan oinarrituta, inplementatu mezulari bat (mezuak, bideo-deiak) 1C aplikazioetan exekutatzen dena.

Diseina ezazu sistema horizontalki eskalagarria izan dadin. Gero eta handiagoa den karga estali behar da nodo kopurua handituz.

Inplementazioa

SVren zerbitzariaren zatia 1C:Enterprise plataforman zuzenean ez integratzea erabaki genuen, produktu bereizi gisa ezartzea baizik, zeinaren APIa 1C aplikazio-soluzioen kodetik dei daiteke. Hainbat arrazoirengatik egin zen hori, eta horietako nagusia izan zen 1C aplikazio ezberdinen artean mezuak trukatzea posible egin nahi nuela (adibidez, Merkataritza Kudeaketa eta Kontabilitatearen artean). 1C aplikazio desberdinak 1C:Enterprise plataformaren bertsio ezberdinetan exekutatu daitezke, zerbitzari ezberdinetan egon, etab. Baldintza horietan, SV 1C instalazioen "alboan" kokatutako produktu bereizi gisa ezartzea irtenbide egokiena da.

Beraz, SV produktu bereizi gisa egitea erabaki genuen. Enpresa txikiek gure hodeian instalatu dugun CB zerbitzaria erabiltzea gomendatzen dugu (wss://1cdialog.com) zerbitzariaren tokiko instalazioarekin eta konfigurazioarekin lotutako gastu orokorrak ekiditeko. Bezero handiek komenigarritzat jo dezakete beren instalazioetan CB zerbitzaria instalatzea. Antzeko ikuspegia erabili dugu gure hodeiko SaaS produktuan 1cFreskoa – masa-ekoiztutako produktu gisa ekoizten da bezeroen guneetan instalatzeko, eta gure hodeian ere zabaltzen da https://1cfresh.com/.

Eskaera

Karga eta akatsen tolerantzia banatzeko, Java aplikazio bat ez, hainbat zabalduko ditugu, karga-orekatzailea aurrean dutela. Mezu bat nodo batetik bestera transferitu behar baduzu, erabili argitaratu/harpidetu Hazelcast-en.

Bezeroaren eta zerbitzariaren arteko komunikazioa websocket bidez egiten da. Denbora errealeko sistemetarako egokia da.

Banatutako cachea

Redis, Hazelcast eta Ehcache artean aukeratu dugu. 2015a da. Redis-ek kluster berri bat kaleratu berri du (berriegia, beldurgarria), Sentinel dago murrizketa askorekin. Ehcache-k ez daki nola muntatu kluster batean (funtzio hau geroago agertu zen). Hazelcast 3.4rekin probatzea erabaki genuen.
Hazelcast kutxatik kanpo multzo batean muntatzen da. Nodo bakarreko moduan, ez da oso erabilgarria eta cache gisa bakarrik erabil daiteke - ez daki datuak diskora nola iraultzen, nodo bakarra galtzen baduzu, datuak galduko dituzu. Hainbat Hazelcast zabaltzen ditugu, eta horien artean datu kritikoen babeskopiak egiten ditugu. Ez dugu cachearen babeskopia egiten, ez zaigu axola.

Guretzat, Hazelcast hau da:

  • Erabiltzaileen saioak biltegiratzea. Denbora asko behar da datu basera saio baterako joateak, beraz, Hazelcast-en jartzen ditugu saio guztiak.
  • Cachea. Erabiltzaile profil baten bila bazabiltza, egiaztatu cachea. Mezu berri bat idatzi - jarri cachean.
  • Aplikazio-instantzien arteko komunikaziorako gaiak. Nodoak gertaera bat sortzen du eta Hazelcast gaian jartzen du. Gai honetara harpidetutako beste aplikazio-nodoek ekitaldia jasotzen eta prozesatzen dute.
  • Kluster blokeoak. Adibidez, eztabaida bat sortzen dugu gako esklusibo bat erabiliz (eztabaida bakarra 1C datu-basearen barruan):

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

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

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

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

Kanalik ez dagoela egiaztatu dugu. Sarraila hartu, berriro egiaztatu eta sortu dugu. Blokeoa hartu ondoren blokeoa egiaztatzen ez baduzu, aukera dago beste hari bat ere egiaztatzea une horretan eta orain eztabaida bera sortzen saiatuko da, baina dagoeneko badago. Ezin duzu blokeatu java Lock sinkronizatua edo arrunta erabiliz. Datu-basearen bidez - motela da, eta pena da datu-basearentzat; Hazelcast-en bidez - hori da behar duzuna.

DBMS bat aukeratzea

Esperientzia zabala eta arrakastatsua dugu PostgreSQL-ekin lanean eta DBMS honen garatzaileekin elkarlanean.

Ez da erraza PostgreSQL kluster batekin - badago XL, XC, Citus, baina, oro har, hauek ez dira kutxatik kanpo eskalatzen diren NoSQLak. Ez genuen NoSQL biltegiratze nagusitzat hartu; nahikoa izan zen Hazelcast hartzea, aurretik landu ez genuena.

Datu-base erlazional bat eskalatu behar baduzu, horrek esan nahi du zatiketa. Dakizuenez, sharding-arekin datu-basea zati ezberdinetan banatzen dugu, horietako bakoitza zerbitzari batean kokatu ahal izateko.

Gure zatiketaren lehen bertsioak gure aplikazioaren taula bakoitza zerbitzari desberdinetan proportzio desberdinetan banatzeko gaitasuna suposatu zuen. A zerbitzarian mezu asko daude - mesedez, eraman dezagun taula honen zati bat B zerbitzarira. Erabaki honek optimizazio goiztiarrari buruz garrasi egin zuen, beraz, maizter anitzeko ikuspegi batera mugatzea erabaki genuen.

Maizter askori buruz irakur dezakezu, adibidez, webgunean Citus Data.

SVk aplikazio eta harpidedun kontzeptuak ditu. Aplikazio bat negozio-aplikazio baten instalazio espezifikoa da, hala nola ERP edo Kontabilitatea, bere erabiltzaileekin eta negozio-datuekin. Harpideduna aplikazioa SV zerbitzarian erregistratuta dagoen erakunde edo pertsona bat da. Harpidedun batek hainbat aplikazio izan ditzake erregistratuta, eta aplikazio hauek elkarren artean mezuak trukatu ditzakete. Harpideduna maizter bihurtu zen gure sisteman. Hainbat harpidedunen mezuak datu-base fisiko batean aurki daitezke; harpidedun bat trafiko asko sortzen hasi dela ikusten badugu, datu-base fisiko bereizi batera eramango dugu (edo datu-base zerbitzari berezi batera ere).

Datu-base nagusi bat dugu non bideratze-taula bat gordetzen den harpidedun datu-base guztien kokapenari buruzko informazioarekin.

Nola eta zergatik idatzi genuen karga handiko zerbitzu eskalagarria 1Crako: Enterprise: Java, PostgreSQL, Hazelcast

Datu-base nagusia botila-lepo bat izan ez dadin, bideratze-taula (eta maiz behar diren beste datu batzuk) cache batean gordetzen dugu.

Harpidedunaren datu-basea moteltzen hasten bada, barruan partizioetan moztuko dugu. Erabiltzen ditugun beste proiektu batzuetan pg_pathman.

Erabiltzaileen mezuak galtzea txarra denez, gure datu-baseak erreplikekin mantentzen ditugu. Erreplika sinkronoen eta asinkronoen konbinazioak datu-base nagusia galduz gero zure burua aseguratzeko aukera ematen du. Mezu-galera datu-base nagusiak eta bere erreplika sinkronoak aldi berean huts egiten badu soilik gertatuko da.

Erreplika sinkronoa galtzen bada, erreplika asinkronoa sinkrono bihurtzen da.
Datu-base nagusia galtzen bada, erreplika sinkronoa datu-base nagusi bihurtzen da, eta erreplika asinkronoa erreplika sinkronoa.

Bilaketa elastikoa

Besteak beste, SV mezularia ere bada, bilaketa azkarra, erosoa eta malgua eskatzen du, morfologia kontuan hartuta, bat-etortze zehatzak erabiliz. Gurpila ez berrasmatzea eta liburutegian oinarrituta sortutako Elasticsearch doako bilatzailea erabiltzea erabaki genuen. Lucene. Era berean, Elasticsearch kluster batean zabaltzen dugu (maisua – datuak – datuak) aplikazio-nodoen hutsegiteen kasuan arazoak ezabatzeko.

Github-en aurkitu dugu Errusiako morfologia plugina Elasticsearch-erako eta erabili. Elasticsearch indizean hitzen erroak (pluginak zehazten dituena) eta N-gramak gordetzen ditugu. Erabiltzaileak bilaketarako testua sartzen duen heinean, idatzitako testua N-gramen artean bilatzen dugu. Aurkibidean gordetzen denean, "testuak" hitza N-gramo hauetan banatuko da:

[horiek, tek, tex, testua, testuak, ek, ex, ext, testuak, ks, kst, ksty, st, sty, zuk],

Eta “testu” hitzaren erroa ere gordeko da. Planteamendu honek hitzaren hasieran, erdian eta amaieran bilatzeko aukera ematen du.

Irudi nagusia

Nola eta zergatik idatzi genuen karga handiko zerbitzu eskalagarria 1Crako: Enterprise: Java, PostgreSQL, Hazelcast
Errepikatu irudia artikuluaren hasieratik, baina azalpenekin:

  • Balancer Interneten agerian; nginx dugu, edozein izan daiteke.
  • Java aplikazioen instantziak elkarren artean komunikatzen dira Hazelcast bidez.
  • Erabiltzen dugun web socket batekin lan egiteko Netty.
  • Java aplikazioa Java 8-n idatzita dago eta sortaz osatuta dago OSGi azkenaldi. Planak Java 10-ra migratzea eta moduluetarako trantsizioa barne hartzen ditu.

Garapena eta probak

SV garatzeko eta probatzeko prozesuan, erabiltzen ditugun produktuen ezaugarri interesgarri ugari topatu ditugu.

Karga-probak eta memoria-ihesak

SV bertsio bakoitzaren kaleratzeak karga probak dakartza. Arrakasta izaten da:

  • Probak hainbat egunez funtzionatu zuen eta ez zen zerbitzu akatsik izan
  • Funtsezko eragiketetarako erantzun denborak ez zuen atalase erosoa gainditu
  • Errendimenduaren hondatzea aurreko bertsioarekin alderatuta ez da % 10 baino gehiago

Proba datu-basea datuz betetzen dugu; horretarako, harpidedun aktiboenari buruzko informazioa jasotzen dugu produkzio-zerbitzaritik, bere zenbakiak 5ez biderkatu (mezu, eztabaida, erabiltzaile kopurua) eta horrela probatzen dugu.

Elkarreragin-sistemaren karga-probak hiru konfiguraziotan egiten ditugu:

  1. estres proba
  2. Konexioak soilik
  3. Harpidedunen erregistroa

Estres proban, ehunka hari abiarazten ditugu, eta sistema gelditu gabe kargatzen dute: mezuak idatzi, eztabaidak sortu, mezuen zerrenda jaso. Erabiltzaile arrunten ekintzak (irakurri gabeko nire mezuen zerrenda lortu, norbaiti idatzi) eta software irtenbideak (konfigurazio ezberdineko pakete bat bidali, alerta bat prozesatu) simulatzen ditugu.

Adibidez, hauxe da estres-probaren zati bat:

  • Erabiltzaileak saioa hasten du
    • Irakurri gabeko eztabaidak eskatzen ditu
    • Mezuak irakurtzeko aukera % 50
    • %50eko probabilitatea testuak bidaltzeko
    • Hurrengo erabiltzailea:
      • Eztabaida berri bat sortzeko %20ko aukera du
      • Ausaz aukeratzen du bere eztabaidetako bat
      • Barrura doa
      • Eskaerak mezuak, erabiltzailearen profilak
      • Eztabaida honetatik ausazko erabiltzaileei zuzendutako bost mezu sortzen ditu
      • Eztabaida uzten du
      • 20 aldiz errepikatzen da
      • Saioa amaitzen da, gidoiaren hasierara itzultzen da

    • Chatbot bat sartzen da sisteman (mezuak emulatzen ditu aplikazio-kodetik)
      • Datuak trukatzeko kanal berri bat sortzeko % 50eko aukera du (eztabaida berezia)
      • % 50 litekeena da mezu bat idazteko dagoen edozein kanaletan

"Konexioak soilik" eszenatokia arrazoi batengatik agertu zen. Egoera bat dago: erabiltzaileek sistema konektatu dute, baina oraindik ez dute parte hartu. Erabiltzaile bakoitzak goizeko 09:00etan pizten du ordenagailua, zerbitzariarekin konexioa ezartzen du eta isilik geratzen da. Mutil hauek arriskutsuak dira, horietako asko daude - dauzkaten pakete bakarrak PING/PONG dira, baina zerbitzariarekiko konexioa mantentzen dute (ezin dute mantendu - zer mezu berri bat badago). Testak egoera bat erreproduzitzen du, non erabiltzaile kopuru handi bat sisteman saioa hasten ordu erdian saiatzen den. Estres-proba baten antzekoa da, baina lehen sarrera honetan dago arreta, beraz, hutsegiterik egon ez dadin (pertsona batek ez du sistema erabiltzen eta dagoeneko erortzen da - zaila da zerbait okerragoa pentsatzea).

Harpidedunen erregistratzeko script-a lehen abiaraztetik hasten da. Estres proba bat egin genuen eta ziur egon ginen korrespondentzian sistema ez zela moteltzen. Baina erabiltzaileak etorri ziren eta erregistroa huts egiten hasi zen denbora-muga baten ondorioz. Izena ematerakoan erabili genuen / dev / random, sistemaren entropiarekin lotuta dagoena. Zerbitzariak ez zuen astirik izan entropia nahikoa pilatzeko eta SecureRandom berri bat eskatu zenean, hamarnaka segundoz izoztu zen. Egoera horretatik ateratzeko modu asko daude, adibidez: /dev/urandom segurua ez den batera aldatu, entropia sortzen duen plaka berezi bat instalatu, ausazko zenbakiak aldez aurretik sortu eta igerileku batean gorde. Igerilekuaren arazoa behin-behinean itxi genuen, baina orduz geroztik beste proba bat egiten ari gara harpidedun berriak erregistratzeko.

Karga-sorgailu gisa erabiltzen dugu JMeter. Ez daki websocket-ekin nola lan egin; plugin bat behar du. "jmeter websocket" kontsultaren bilaketa-emaitzetan lehenengoak hauek dira: BlazeMeterreko artikuluak, gomendatzen duena Maciej Zaleskiren plugina.

Hor erabaki genuen hastea.

Proba serioak hasi eta ia berehala, JMeter memoria galtzen hasi zela aurkitu genuen.

Plugin-a istorio handi bat da; 176 izarrekin, 132 sardexka ditu github-en. Egileak berak ez du konpromisorik hartu 2015etik (2015ean hartu genuen, gero ez zuen susmorik sortu), memoria-filtrazioei buruzko hainbat github arazo, itxi gabeko 7 pull request.
Plugin hau erabiliz karga-probak egitea erabakitzen baduzu, mesedez jarri arreta honako eztabaida hauei:

  1. Hari anitzeko ingurune batean, ohiko LinkedList bat erabili zen, eta emaitza izan zen NPE exekuzioan. Hau ConcurrentLinkedDeque-ra aldatuz edo bloke sinkronizatuen bidez konpondu daiteke. Lehenengo aukera geuk aukeratu genuen (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/43).
  2. Memoria ihesa; deskonektatzean, konexioaren informazioa ez da ezabatzen (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/44).
  3. Streaming moduan (websocket-a laginaren amaieran ixten ez denean, baina geroago planean erabiltzen denean), Erantzun ereduek ez dute funtzionatzen (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/19).

Github-eko horietako bat da. Zer egin genuen:

  1. Hartu dute sardexka Elyran Kogan (@elyrank) - 1 eta 3 arazoak konpontzen ditu
  2. Ebatzitako problema 2
  3. Kaila eguneratua 9.2.14tik 9.3.12ra
  4. SimpleDateFormat bilduta ThreadLocal-en; SimpleDateFormat ez da hari segurua, eta horrek NPE ekarri zuen exekuzioan
  5. Beste memoria-isuri bat konpondu da (konexioa gaizki itxi zen deskonektatzean)

Eta hala ere isurtzen da!

Memoria agortzen hasi zen ez egun batean, bitan baizik. Ez zegoen erabat denborarik, beraz, hari gutxiago abiaraztea erabaki genuen, baina lau eragiletan. Horrek nahikoa izan behar zuen gutxienez astebetez.

Bi egun pasa dira...

Orain Hazelcast memoria agortzen ari da. Erregistroek erakusten zuten pare bat egun probak egin ondoren, Hazelcast memoria faltagatik kexatzen hasi zela, eta denboraren buruan klusterra erori zen eta nodoak banan-banan hiltzen jarraitu zuten. JVisualVM hazelcast-era konektatu genuen eta "gorantz doan zerra" bat ikusi genuen; aldizka GC deitzen zuen, baina ezin izan zuen memoria garbitu.

Nola eta zergatik idatzi genuen karga handiko zerbitzu eskalagarria 1Crako: Enterprise: Java, PostgreSQL, Hazelcast

Hazelcast 3.4-n, mapa / multiMap (map.destroy()) ezabatzean, memoria ez da guztiz askatzen:

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

Akatsa 3.5ean konpondu da, baina orduan arazo bat zen. MultiMaps berriak sortu ditugu izen dinamikoekin eta gure logikaren arabera ezabatu ditugu. Kodeak honelako itxura zuen:

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();
    }
}

Nora:

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

multiMap harpidetza bakoitzerako sortu zen eta behar ez zenean ezabatu zen. Mapa martxan jarriko genuela erabaki genuen , gakoa harpidetzaren izena izango da, eta balioak saio-identifikatzaileak izango dira (horietatik lor ditzakezu gero erabiltzaile-identifikatzaileak, behar izanez gero).

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

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

Zerrendak hobetu egin dira.

Nola eta zergatik idatzi genuen karga handiko zerbitzu eskalagarria 1Crako: Enterprise: Java, PostgreSQL, Hazelcast

Zer gehiago ikasi dugu karga probei buruz?

  1. JSR223 groovy-n idatzi behar da eta konpilazio cachea sartu - askoz azkarragoa da. Link.
  2. Jmeter-Plugins grafikoak errazago ulertzen dira estandarrak baino. Link.

Hazelcast-ekin dugun esperientziari buruz

Hazelcast produktu berria zen guretzat, 3.4.1 bertsiotik hasi ginen lanean, orain gure ekoizpen zerbitzaria 3.9.2 bertsioa exekutatzen ari da (idazketa momentuan, Hazelcast-en azken bertsioa 3.10 da).

NAN sortzea

Identifikatzaile osoekin hasi ginen. Pentsa dezagun beste Long bat behar dugula entitate berri baterako. Datu-baseko sekuentzia ez da egokia, taulek zatiketa-lanetan parte hartzen dute - DB1-en mezu ID=1 eta DB1-n mezu ID=2 bat dagoela ematen du, ezin duzu ID hori jarri Elasticsearch-en, ezta Hazelcast-en ere. , baina okerrena da bi datu-baseetako datuak bakarrean konbinatu nahi badituzu (adibidez, harpidedun hauentzat datu-base bakarra nahikoa dela erabakitzea). Hazelcast-i hainbat AtomicLong gehi ditzakezu eta kontagailua bertan mantendu, gero ID berri bat lortzeko errendimendua incrementAndGet gehi Hazelcast-i eskaera egiteko denbora. Baina Hazelcast-ek zerbait optimoagoa du - FlakeIdGenerator. Bezero bakoitzarekin harremanetan jartzean, ID tarte bat ematen zaie, adibidez, lehenengoari –1etik 10ra, bigarrenari– 000etik 10ra, eta abar. Orain bezeroak identifikatzaile berriak igorri ditzake bere kabuz igorritako barrutia amaitu arte. Azkar funtzionatzen du, baina aplikazioa (eta Hazelcast bezeroa) berrabiarazten duzunean, sekuentzia berri bat hasten da; Horrez gain, garatzaileek ez dute ulertzen zergatik diren IDak osoak, baina hain inkoherenteak dira. Dena pisatu eta UUIDetara aldatu genuen.

Bide batez, Twitter bezalakoa izan nahi dutenentzat Snowcast liburutegia dago - Snowflake Hazelcast-en gainean dagoen inplementazioa da. Hemen ikus dezakezu:

github.com/noctarius/snowcast
github.com/twitter/snowflake

Baina ez gara gehiago lortu.

TransactionalMap.ordezkatu

Beste sorpresa bat: TransactionalMap.replace ez dabil. Hona hemen proba bat:

@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

Nire ordezkapena idatzi behar izan nuen getForUpdate erabiliz:

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);
}

Probatu datu-egitura arruntak ez ezik, haien transakzio-bertsioak ere. Gertatzen da IMap funtzionatzen duela, baina TransactionalMap jada ez da existitzen.

Sartu JAR berri bat geldialdirik gabe

Lehenik eta behin, gure klaseetako objektuak Hazelcast-en grabatzea erabaki genuen. Adibidez, Aplikazio klase bat dugu, gorde eta irakurri nahi dugu. Gorde:

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

Emanaldia:

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

Dena dabil. Orduan, Hazelcast-en aurkibide bat sortzea erabaki genuen hauen arabera bilatzeko:

map.addIndex("subscriberId", false);

Eta entitate berri bat idaztean, ClassNotFoundException jasotzen hasi ziren. Hazelcast aurkibidera gehitzen saiatu zen, baina ez zekien ezer gure klaseari buruz eta klase honekin JAR bat ematea nahi zuen. Hori egin genuen, denak funtzionatu zuen, baina arazo berri bat agertu zen: nola eguneratu JAR klusterra erabat gelditu gabe? Hazelcast-ek ez du JAR berria jasotzen nodoz nodo eguneratzean. Une honetan erabaki genuen aurkibiderik bilatu gabe bizi gintezkeela. Azken finean, Hazelcast gako-balioen denda gisa erabiltzen baduzu, dena funtzionatuko du? Benetan ez. Hemen berriro ere IMap eta TransactionalMap-en portaera ezberdina da. IMap-ek axola ez dion lekuetan, TransactionalMap-ek errore bat botatzen du.

IMap. 5000 objektu idazten ditugu, irakurri. Dena espero da.

@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());
    }
}

Baina transakzio batean ez du funtzionatzen, ClassNotFoundException bat lortzen dugu:

@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-n, Erabiltzaile-klasearen hedapen-mekanismoa agertu zen. Nodo nagusi bat izenda dezakezu eta bertan JAR fitxategia egunera dezakezu.

Orain guztiz aldatu dugu gure ikuspegia: geuk serializatzen dugu JSON-en eta Hazelcast-en gordetzen dugu. Hazelcast-ek ez du gure klaseen egitura ezagutu behar, eta etenaldirik gabe eguneratu gaitezke. Domeinuko objektuen bertsioa aplikazioak kontrolatzen du. Aplikazioaren bertsio desberdinak exekutatzen egon daitezke aldi berean, eta egoera bat gerta daiteke aplikazio berriak eremu berriekin objektuak idazten dituenean, baina zaharrak oraindik eremu horien berri ez dakienean. Eta, aldi berean, aplikazio berriak eremu berririk ez duten aplikazio zaharrak idatzitako objektuak irakurtzen ditu. Horrelako egoerak aplikazioaren barruan kudeatzen ditugu, baina sinpletasunerako ez ditugu eremuak aldatzen edo ezabatzen, eremu berriak gehituz soilik zabaltzen ditugu klaseak.

Errendimendu handia nola bermatzen dugun

Hazelcast-era lau bidaia - ona, bi datu-basera - txarra

Datuen cachera joatea datu-basera joatea baino hobea da beti, baina ez dituzu erabili nahi ez dituzun erregistroak ere gorde nahi. Cachean zer erabakitzea garapenaren azken fasera arte uzten dugu. Funtzio berria kodetzen denean, PostgreSQL-n kontsulta guztien erregistroa aktibatzen dugu (log_min_duration_statement 0-ra) eta karga-probak egiten ditugu 20 minutuz. Bildutako erregistroak erabiliz, pgFouine eta pgBadger bezalako utilitateek txosten analitikoak eraiki ditzakete. Txostenetan, batez ere, kontsulta motelak eta maiztasunak bilatzen ditugu. Kontsulta moteletarako, exekuzio-plan bat eraikitzen dugu (AZALDU) eta kontsulta hori bizkortu daitekeen ebaluatzen dugu. Sarrerako datu berdinen maiz egiten diren eskaerak ondo sartzen dira cachean. Kontsultak "lauak" mantentzen saiatzen gara, kontsulta bakoitzeko taula bat.

ustiapena

SV lineako zerbitzu gisa 2017ko udaberrian jarri zen martxan, eta produktu bereizi gisa, SV 2017ko azaroan kaleratu zen (garai hartan beta bertsioaren egoeran).

Urtebete baino gehiagotan, ez da arazo larririk egon CB lineako zerbitzuaren funtzionamenduan. Internet bidezko zerbitzua kontrolatzen dugu Zabbix, bildu eta zabaldu Banbu.

SV zerbitzariaren banaketa jatorrizko paketeetan ematen da: RPM, DEB, MSI. Gainera Windows-erako instalatzaile bakarra eskaintzen dugu EXE bakar baten moduan, zerbitzaria, Hazelcast eta Elasticsearch makina batean instalatzen dituena. Hasieran instalazioaren bertsio honi "demo" bertsioa deitzen genion, baina orain argi geratu da hori dela hedapen aukerarik ezagunena.

Iturria: www.habr.com

Gehitu iruzkin berria