Kumaha sareng kunaon urang nyerat jasa scalable beban tinggi pikeun 1C: Enterprise: Java, PostgreSQL, Hazelcast

Dina artikel ieu kami baris ngobrol ngeunaan kumaha jeung naha urang dimekarkeun Sistim interaksi - mékanisme anu mindahkeun inpormasi antara aplikasi klien sareng server 1C: Enterprise - tina netepkeun tugas dugi ka mikirkeun detail arsitéktur sareng palaksanaan.

Sistem Interaksi (saterusna disebut SV) mangrupakeun disebarkeun, sistem olahtalatah lepat-toleran kalayan pangiriman dijamin. SV dirancang salaku layanan beban tinggi kalawan scalability tinggi, sadia duanana salaku layanan online (disadiakeun ku 1C) jeung salaku produk masal-dihasilkeun nu bisa deployed on fasilitas server sorangan.

SV ngagunakeun gudang disebarkeun Hazelcast jeung mesin pencari Elasticsearch. Urang ogé bakal ngobrol ngeunaan Java sareng kumaha urang sacara horisontal skala PostgreSQL.
Kumaha sareng kunaon urang nyerat jasa scalable beban tinggi pikeun 1C: Enterprise: Java, PostgreSQL, Hazelcast

Ngarumuskeun masalah

Pikeun netelakeun kunaon urang nyiptakeun Sistem Interaksi, kuring bakal nyarioskeun ka anjeun sakedik ngeunaan kumaha pamekaran aplikasi bisnis di 1C jalan.

Pikeun dimimitian ku, saeutik ngeunaan kami pikeun maranéhanana anu teu acan terang naon urang ngalakukeun :) Kami nyieun 1C: platform téhnologi Enterprise. Platformna kalebet alat pangembangan aplikasi bisnis, ogé waktos jalan anu ngamungkinkeun aplikasi bisnis dijalankeun dina lingkungan lintas platform.

paradigma ngembangkeun klien-server

Aplikasi bisnis anu diciptakeun dina 1C: Enterprise beroperasi dina tilu tingkat klien-server arsitéktur "DBMS - server aplikasi - klien". Kode aplikasi ditulis dina diwangun-di 1C basa, bisa dieksekusi dina server aplikasi atawa dina klien nu. Sadaya karya jeung objék aplikasi (diréktori, dokumén, jsb), kitu ogé maca jeung nulis database, dipigawé ngan dina server. Pungsi formulir na panganteur paréntah ogé dilaksanakeun dina server. Klién ngalaksanakeun nampi, muka sareng ningalikeun bentuk, "komunikasi" sareng pangguna (peringatan, patarosan ...), itungan leutik dina bentuk anu peryogi réspon gancang (contona, ngalikeun harga ku kuantitas), damel sareng file lokal, gawé bareng parabot.

Dina kode aplikasi, lulugu prosedur sareng fungsi kedah sacara eksplisit nunjukkeun dimana kodeu bakal dieksekusi - nganggo arahan &AtClient / &AtServer (&AtClient / &AtServer dina versi basa Inggris). Pamekar 1C ayeuna bakal ngabenerkeun kuring ku nyarios yén arahan saleresna badag, tapi keur urang ieu teu penting ayeuna.

Anjeun tiasa nelepon kode server tina kode klien, tapi anjeun teu bisa nelepon kode klien tina kode server. Ieu mangrupikeun watesan dasar anu kami lakukeun ku sababaraha alesan. Khususna, sabab kode server kudu ditulis dina cara nu executes cara sarua euweuh urusan dimana eta disebut - ti klien atawa ti server. Sareng dina kasus nelepon kode server tina kode server anu sanés, teu aya klien sapertos kitu. Sareng kusabab nalika palaksanaan kode server, klien anu nyauran éta tiasa nutup, kaluar tina aplikasi, sareng server moal aya deui anu nelepon.

Kumaha sareng kunaon urang nyerat jasa scalable beban tinggi pikeun 1C: Enterprise: Java, PostgreSQL, Hazelcast
Kodeu anu ngatur tombol klik: nelepon prosedur server ti klien bakal jalan, nelepon prosedur klien ti server moal.

Ieu ngandung harti yén lamun urang rék ngirim sababaraha pesen ti server ka aplikasi klien, contona, yén generasi laporan "lila-ngajalankeun" geus réngsé sarta laporan bisa ditempo, urang teu boga metoda sapertos. Anjeun kudu make trik, contona, périodik polling server ti kode klien. Tapi pendekatan ieu ngamuat sistem sareng telepon anu teu perlu, sareng umumna henteu katingalina elegan.

Sareng aya ogé anu peryogi, contona, nalika telepon sumping SIP- nalika nelepon, wartosan aplikasi klien ngeunaan ieu supados tiasa nganggo nomer panelepon pikeun milarian dina database counterparty sareng nunjukkeun inpormasi pangguna ngeunaan counterparty anu nelepon. Atanapi, contona, nalika pesenan dugi ka gudang, wartosan aplikasi klien palanggan ngeunaan ieu. Sacara umum, aya loba kasus dimana mékanisme sapertos bakal mangpaat.

Produksi sorangan

Jieun mékanisme olahtalatah. Gancang, dipercaya, kalayan pangiriman anu dijamin, kalayan kamampuan milarian pesen sacara fleksibel. Dumasar mékanisme, laksanakeun utusan (pesen, telepon pidéo) anu ngajalankeun di jero aplikasi 1C.

Rarancang sistem pikeun skala horisontal. Beban ningkatna kedah katutupan ku ningkatkeun jumlah titik.

Реализация

Urang mutuskeun teu ngahijikeun bagian server of SV langsung kana 1C: platform Perusahaan, tapi pikeun nerapkeun salaku produk misah, API nu bisa disebut ti kodeu 1C solusi aplikasi. Hal ieu dilakukeun pikeun sababaraha alesan, anu utama nyaéta yén kuring hoyong ngagentos pesen antara aplikasi 1C anu béda (contona, antara Manajemén Perdagangan sareng Akuntansi). Aplikasi 1C anu béda tiasa dijalankeun dina vérsi anu béda tina 1C: platform Perusahaan, ayana dina server anu béda, jsb. Dina kaayaan kitu, palaksanaan SV salaku produk misah lokasina "di sisi" tina pamasangan 1C mangrupakeun solusi optimal.

Ku kituna, urang mutuskeun nyieun SV salaku produk misah. Kami ngarékoméndasikeun yén pausahaan leutik ngagunakeun server CB nu urang dipasang dina awan urang (wss://1cdialog.com) pikeun nyingkahan biaya overhead pakait sareng instalasi lokal sarta konfigurasi tina server. Klién ageung panginten langkung saé pikeun masang server CB sorangan di fasilitasna. Kami nganggo pendekatan anu sami dina produk SaaS awan kami 1c seger - éta dihasilkeun salaku produk masal-dihasilkeun pikeun instalasi di situs klien ', sarta ogé deployed dina awan urang https://1cfresh.com/.

aplikasi

Pikeun ngadistribusikaeun beban sarta kasabaran sesar, urang bakal nyebarkeun teu hiji aplikasi Java, tapi sababaraha, kalawan beban balancer di hareup aranjeunna. Upami anjeun kedah nransfer pesen ti node ka node, paké publish/subscribe di Hazelcast.

Komunikasi antara klien sareng server nyaéta via websocket. Éta cocog pikeun sistem real-time.

cache disebarkeun

Kami milih antara Redis, Hazelcast sareng Ehcache. Ieu 2015. Redis nembé ngarilis klaster énggal (anyar teuing, pikasieuneun), aya Sentinel kalayan seueur larangan. Ehcache henteu terang kumaha carana ngumpul kana klaster (fungsi ieu muncul engké). Kami mutuskeun pikeun nyobian éta sareng Hazelcast 3.4.
Hazelcast dirakit kana klaster kaluar tina kotak. Dina modeu titik tunggal, teu pisan mangpaat tur ngan bisa dipaké salaku cache a - teu nyaho kumaha carana dump data ka disk, lamun leungit hijina titik, anjeun leungit data. Kami nyebarkeun sababaraha Hazelcasts, diantarana kami nyadangkeun data kritis. Kami henteu nyadangkeun cache - kami henteu kapikiran.

Pikeun kami, Hazelcast nyaéta:

  • Panyimpenan sesi pamaké. Butuh waktu lila pikeun buka database pikeun sési unggal waktu, jadi urang nempatkeun sagala sesi dina Hazelcast.
  • Cache. Upami anjeun milarian profil pangguna, pariksa cache. Nulis pesen anyar - nempatkeun eta dina cache nu.
  • Topik pikeun komunikasi antara instansi aplikasi. Node ngahasilkeun hiji acara sarta nempatkeun eta dina topik Hazelcast. Titik aplikasi anu sanés ngalanggan topik ieu nampi sareng ngolah acara.
  • Konci klaster. Contona, urang nyieun sawala ngagunakeun konci unik (diskusi singleton dina database 1C):

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

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

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

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

Kami pariksa yén teu aya saluran. Kami nyandak konci, pariksa deui, sareng nyiptakeunana. Upami anjeun henteu pariksa konci saatos nyandak konci, maka aya kamungkinan yén benang anu sanés ogé dipariksa dina waktos éta sareng ayeuna bakal nyobian ngadamel diskusi anu sami - tapi éta parantos aya. Anjeun teu tiasa ngonci nganggo java Lock anu disingkronkeun atanapi biasa. Ngaliwatan pangkalan data - éta laun, sareng karunya pikeun pangkalan data; ngalangkungan Hazelcast - éta anu anjeun peryogikeun.

Milih hiji DBMS

Kami gaduh pangalaman éksténsif sareng suksés damel sareng PostgreSQL sareng kolaborasi sareng pamekar DBMS ieu.

Henteu gampang sareng klaster PostgreSQL - aya XL, XC, Citus, tapi sacara umum ieu sanés NoSQL anu skala kaluar tina kotak. Kami henteu nganggap NoSQL salaku panyimpen utama; éta cekap kami nyandak Hazelcast, anu kami henteu acan damel sateuacanna.

Lamun perlu skala database relational, hartina sharding. Sakumaha anjeun terang, kalayan sharding kami ngabagi pangkalan data kana bagian anu misah supados masing-masing tiasa disimpen dina server anu misah.

Versi kahiji tina sharding urang nganggap kamampuhan pikeun ngadistribusikaeun unggal tabel aplikasi urang sakuliah server béda dina babandingan béda. Aya seueur pesen dina server A - punten, hayu urang mindahkeun bagian tina méja ieu ka server B. Kaputusan ieu ngan saukur ngajerit ngeunaan optimasi prématur, ku kituna urang mutuskeun pikeun ngawatesan diri kana pendekatan multi-tenant.

Anjeun tiasa maca ngeunaan multi tenant, contona, dina website Data Citus.

SV boga konsep aplikasi tur palanggan. Aplikasi mangrupikeun pamasangan khusus tina aplikasi bisnis, sapertos ERP atanapi Akunting, sareng pangguna sareng data bisnisna. Palanggan mangrupikeun organisasi atanapi individu anu atas nama aplikasina didaptarkeun dina server SV. Palanggan tiasa gaduh sababaraha aplikasi anu kadaptar, sareng aplikasi ieu tiasa silih tukeur pesen. Palanggan janten panyewa dina sistem kami. Pesen ti sababaraha palanggan tiasa aya dina hiji database fisik; lamun urang nempo yén palanggan geus dimimitian pikeun ngahasilkeun loba lalulintas, urang mindahkeun ka database fisik misah (atawa malah server database misah).

Kami gaduh database utama dimana tabel routing disimpen kalayan inpormasi ngeunaan lokasi sadaya database palanggan.

Kumaha sareng kunaon urang nyerat jasa scalable beban tinggi pikeun 1C: Enterprise: Java, PostgreSQL, Hazelcast

Pikeun nyegah database utama jadi bottleneck a, urang nyimpen tabel routing (jeung data remen diperlukeun séjén) dina cache a.

Upami database palanggan mimiti ngalambatkeun, kami bakal motong kana partisi di jero. Dina proyék séjén kami nganggo pg_pathman.

Kusabab kaleungitan pesen pangguna goréng, kami ngajaga pangkalan data nganggo réplika. Kombinasi réplika sinkron sareng asinkron ngamungkinkeun anjeun pikeun ngajamin diri upami kaleungitan pangkalan data utama. Leungitna pesen ngan bakal lumangsung lamun database primér sarta réplika sinkron na gagal sakaligus.

Upami réplika sinkron leungit, réplika asinkron janten sinkron.
Upami pangkalan data utama leungit, réplika sinkron janten pangkalan data utama, sareng réplika asinkron janten réplika sinkron.

Elasticsearch pikeun milarian

Kusabab, diantara hal séjén, SV ogé utusan, merlukeun gancang, pilarian merenah tur fléksibel, nyokot kana morfologi akun, ngagunakeun patandingan imprecise. Urang mutuskeun teu reinvent kabayang jeung ngagunakeun search engine bébas Elasticsearch, dijieun dumasar kana perpustakaan Lucene. Urang ogé nyebarkeun Elasticsearch dina klaster (master - data - data) pikeun ngaleungitkeun masalah dina acara gagalna titik aplikasi.

Dina github kami mendakan Plugin morfologi Rusia pikeun Elasticsearch tur ngagunakeun eta. Dina indéks Elasticsearch urang nyimpen akar kecap (anu plugin nangtukeun) jeung N-gram. Nalika pangguna ngalebetkeun téks pikeun milarian, urang milarian téks anu diketik diantara N-gram. Nalika disimpen kana indéks, kecap "téks" bakal dibagi kana N-gram ieu:

[éta, tek, tex, téks, téks, ek, ex, ext, téks, ks, kst, ksty, st, sty, anjeun],

Jeung akar kecap "téks" ogé bakal dilestarikan. Pendekatan ieu ngamungkinkeun anjeun milarian di awal, di tengah, sareng di tungtung kecap.

Gambar sakabéh

Kumaha sareng kunaon urang nyerat jasa scalable beban tinggi pikeun 1C: Enterprise: Java, PostgreSQL, Hazelcast
Malikan deui gambar ti mimiti tulisan, tapi kalayan katerangan:

  • Balancer kakeunaan dina Internét; urang gaduh nginx, tiasa wae.
  • Instansi aplikasi Java saling komunikasi via Hazelcast.
  • Pikeun dianggo sareng stop kontak wéb kami nganggo Netty.
  • Aplikasi Java ditulis dina Java 8 sareng diwangun ku bundles OSGi. Rencanana kalebet migrasi ka Java 10 sareng transisi ka modul.

Ngembangkeun jeung nguji

Dina prosés ngamekarkeun jeung nguji SV, kami datang di sakuliah sababaraha fitur metot tina produk kami nganggo.

Uji beban sareng bocor mémori

Pelepasan unggal pelepasan SV ngalibatkeun uji beban. Éta suksés nalika:

  • Tés digawé sababaraha dinten sareng teu aya gagal jasa
  • waktos respon pikeun operasi konci teu ngaleuwihan ambang nyaman
  • Deteriorasi kinerja dibandingkeun sareng versi sateuacana henteu langkung ti 10%

Urang ngeusian database test jeung data - pikeun ngalakukeun ieu, urang nampi informasi ngeunaan palanggan paling aktip ti server produksi, kalikeun angka na ku 5 (jumlah pesen, diskusi, pamaké) jeung nguji éta cara.

Kami ngalaksanakeun uji beban sistem interaksi dina tilu konfigurasi:

  1. tés stress
  2. Sambungan wungkul
  3. pendaptaran palanggan

Salila test stress, urang ngajalankeun sababaraha ratus threads, sarta aranjeunna muka sistem tanpa eureun: nulis pesen, nyieun diskusi, narima daptar pesen. Urang simulate lampah pamaké biasa (meunang daptar pesen kuring can dibaca, nulis ka batur) jeung solusi software (nepikeun paket tina konfigurasi béda, ngolah waspada).

Salaku conto, ieu mangrupikeun bagian tina tés setrés:

  • Pamaké asup
    • Nyuhunkeun diskusi anjeun anu teu acan dibaca
    • 50% kamungkinan maca pesen
    • 50% kamungkinan kana téks
    • Pamaké salajengna:
      • Boga 20% kasempetan pikeun nyieun diskusi anyar
      • Acak milih salah sahiji diskusi na
      • Asup ka jero
      • Requests pesen, propil pamaké
      • Nyiptakeun lima pesen anu ditujukeun ka pangguna acak tina diskusi ieu
      • Daun diskusi
      • Ngulang 20 kali
      • Log kaluar, balik deui ka awal naskah

    • A chatbot asup kana sistem (emulates olahtalatah tina kode aplikasi)
      • Mibanda kasempetan 50% nyieun saluran anyar pikeun bursa data (diskusi husus)
      • 50% kamungkinan nyerat pesen ka saluran anu aya

Skenario "Sambungan Ngan" muncul pikeun alesan. Aya kaayaan: pamaké geus disambungkeun sistem, tapi teu acan gotten aub. Unggal pamaké ngahurungkeun komputer dina 09:00 isuk-isuk, ngadegkeun sambungan ka server na tetep jempé. Ieu guys bahaya, aya loba di antarana - hijina bungkusan aranjeunna gaduh PING / PONG, tapi aranjeunna tetep sambungan ka server (aranjeunna teu bisa tetep nepi - kumaha lamun aya pesen anyar). Tés éta ngahasilkeun deui kaayaan dimana sajumlah ageung pangguna sapertos nyobian asup kana sistem dina satengah jam. Éta sami sareng tes setrés, tapi fokusna persis kana input anu munggaran ieu - supados henteu aya kagagalan (jalma henteu nganggo sistem, sareng éta parantos murag - hese mikiran anu langkung parah).

Skrip pendaptaran palanggan dimimitian ti peluncuran munggaran. Kami ngalaksanakeun tés setrés sareng yakin yén sistem henteu ngalambatkeun salami korespondensi. Tapi pangguna sumping sareng pendaptaran mimiti gagal kusabab waktosna. Nalika ngadaptar kami dipaké / dev / acak, nu patali jeung éntropi sistem. server teu boga waktu pikeun ngumpulkeun cukup éntropi sarta lamun SecureRandom anyar dipénta, froze pikeun puluhan detik. Aya loba cara kaluar tina kaayaan ieu, Contona: pindah ka kirang aman / dev / urandom, install dewan husus nu dibangkitkeun éntropi, ngahasilkeun angka acak sateuacanna tur nyimpen aranjeunna dina kolam renang a. Urang samentara ditutup masalah jeung kolam renang nu, tapi saprak lajeng urang geus ngajalankeun hiji test misah pikeun ngadaptar palanggan anyar.

Kami nganggo salaku generator beban JMeter. Éta henteu terang kumaha damel sareng websocket; éta peryogi plugin. Anu kahiji dina hasil pamilarian pikeun pamundut "jmeter websocket" nyaéta: artikel ti BlazeMeter, nu nyarankeun plugin ku Maciej Zaleski.

Éta tempat urang mutuskeun pikeun ngamimitian.

Ampir langsung saatos ngamimitian uji serius, kami mendakan yén JMeter mimiti bocor mémori.

Plugin mangrupikeun carita gedé anu misah; kalayan 176 béntang, éta ngagaduhan 132 garpu dina github. Panulis sorangan henteu komitmen ka dinya saprak 2015 (urang nyandak eta dina 2015, mangka teu ngangkat kacurigaan), sababaraha masalah github ngeunaan bocor memori, 7 requests tarikan unclosed.
Upami anjeun mutuskeun pikeun nguji beban nganggo plugin ieu, punten perhatoskeun diskusi ieu:

  1. Dina lingkungan multi-threaded, LinkedList biasa dipaké, sarta hasilna éta NPE dina runtime. Ieu tiasa direngsekeun ku cara ngalih ka ConcurrentLinkedDeque atanapi ku blok anu disingkronkeun. Urang milih pilihan kahiji pikeun diri urang sorangan (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/43).
  2. Bocor memori; nalika megatkeun sambungan, informasi sambungan teu dihapus (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/44).
  3. Dina modeu streaming (nalika websocket henteu ditutup dina tungtung sampel, tapi dipaké engké dina rencana), pola respon teu jalan (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/19).

Ieu mangrupikeun salah sahiji anu aya dina github. Naon anu urang lakukeun:

  1. Geus nyokot garpu Elyran Kogan (@elyrank) - ngalereskeun masalah 1 sareng 3
  2. Ngarengsekeun masalah 2
  3. Diropéa jetty ti 9.2.14 ka 9.3.12
  4. Dibungkus SimpleDateFormat di ThreadLocal; SimpleDateFormat henteu aman-thread, anu nyababkeun NPE nalika runtime
  5. Ngalereskeun bocor mémori anu sanés (sambunganna salah ditutup nalika diputus)

Jeung can ngalir!

Mémori mimiti béak teu sadinten, tapi dua. Henteu aya waktos anu tinggaleun, janten kami mutuskeun pikeun ngaluncurkeun langkung seueur benang, tapi dina opat agén. Ieu kudu geus cukup pikeun sahenteuna saminggu.

Dua poé geus kaliwat...

Ayeuna Hazelcast kehabisan mémori. Log nunjukkeun yén saatos sababaraha dinten uji, Hazelcast mimiti ngawadul ngeunaan kurangna mémori, sareng saatos sababaraha waktos kluster ambruk, sareng titik-titik terus maot hiji-hiji. Kami nyambungkeun JVisualVM kana hazelcast sareng ningali "ragaji naék" - éta sering disebut GC, tapi henteu tiasa mupus mémori.

Kumaha sareng kunaon urang nyerat jasa scalable beban tinggi pikeun 1C: Enterprise: Java, PostgreSQL, Hazelcast

Tétéla dina hazelcast 3.4, nalika ngahapus peta / multiMap (map.destroy()), mémori henteu dibébaskeun lengkep:

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

Kutuna ayeuna parantos dibenerkeun dina 3.5, tapi éta masalah harita. Kami nyiptakeun multiMaps énggal kalayan nami dinamis sareng ngahapus aranjeunna dumasar kana logika urang. Kodeu katingali sapertos kieu:

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

Telepon:

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

multiMap dijieun pikeun tiap langganan sarta dihapus lamun teu diperlukeun. Urang mutuskeun yén urang bakal ngamimitian Peta , koncina bakal nami langganan, sareng nilaina bakal janten identifier sési (ti mana anjeun teras tiasa kéngingkeun identifier pangguna, upami diperyogikeun).

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

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

Bagan parantos ningkat.

Kumaha sareng kunaon urang nyerat jasa scalable beban tinggi pikeun 1C: Enterprise: Java, PostgreSQL, Hazelcast

Naon deui anu urang diajar ngeunaan uji beban?

  1. JSR223 kedah ditulis dina groovy sareng kalebet cache kompilasi - langkung gancang. link.
  2. Grafik Jmeter-Plugins langkung gampang kahartos tibatan anu baku. link.

Ngeunaan pangalaman urang jeung Hazelcast

Hazelcast mangrupikeun produk énggal pikeun kami, kami ngamimitian damel sareng éta tina versi 3.4.1, ayeuna server produksi kami ngajalankeun versi 3.9.2 (dina waktos nyerat, versi Hazelcast panganyarna nyaéta 3.10).

Generasi ID

Urang mimitian ku identifier integer. Hayu urang ngabayangkeun yén urang peryogi Long sejen pikeun éntitas anyar. Sekuen dina pangkalan data henteu cocog, tabel aub dina sharding - tétéla aya pesen ID = 1 di DB1 sareng pesen ID = 1 di DB2, anjeun moal tiasa nempatkeun ID ieu dina Elasticsearch, atanapi di Hazelcast. , tapi anu paling parah nyaéta upami anjeun hoyong ngagabungkeun data tina dua pangkalan data kana hiji (contona, mutuskeun yén hiji pangkalan data cekap pikeun palanggan ieu). Anjeun tiasa nambihan sababaraha AtomicLongs ka Hazelcast sareng ngajaga loket di dinya, teras prestasi pikeun kéngingkeun ID énggal nyaéta incrementAndGet ditambah waktos kanggo pamundut ka Hazelcast. Tapi Hazelcast ngagaduhan anu langkung optimal - FlakeIdGenerator. Nalika ngahubungi unggal klien, aranjeunna dipasihan rentang ID, contona, anu kahiji - ti 1 dugi ka 10, anu kadua - tina 000 dugi ka 10, sareng saterasna. Ayeuna klien bisa ngaluarkeun identifiers anyar sorangan nepi ka rentang dikaluarkeun pikeun eta ends. Gawéna gancang, tapi nalika anjeun ngabalikan deui aplikasi (sareng klien Hazelcast), sekuen anyar dimimitian - ku kituna skips, jsb. Sajaba ti éta, pamekar teu bener ngarti naha ID nu integer, tapi jadi inconsistent. Urang ditimbang sagalana jeung switched ka UUIDs.

Ku jalan kitu, pikeun anu hoyong janten sapertos Twitter, aya perpustakaan Snowcast sapertos - ieu mangrupikeun palaksanaan Snowflake di luhur Hazelcast. Anjeun tiasa ningali di dieu:

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

Tapi urang geus teu meunang sabudeureun eta deui.

TransactionalMap.replace

kejutan sejen: TransactionalMap.replace teu jalan. Ieu tés:

@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

Kuring kungsi nulis ngaganti sorangan maké 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);
}

Nguji henteu ngan ukur struktur data biasa, tapi ogé versi transaksionalna. Éta kajadian yén IMap tiasa dianggo, tapi TransactionalMap henteu aya deui.

Selapkeun JAR anyar tanpa downtime

Kahiji, urang mutuskeun pikeun ngarekam objék tina kelas urang di Hazelcast. Contona, urang boga kelas Aplikasi, urang rék nyimpen jeung maca eta. Simpen:

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

Urang maca:

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

Sagalana jalan. Teras kami mutuskeun pikeun ngawangun indéks dina Hazelcast pikeun milarian ku:

map.addIndex("subscriberId", false);

Sareng nalika nyerat éntitas énggal, aranjeunna mimiti nampi ClassNotFoundException. Hazelcast nyoba pikeun nambahkeun kana indéks, tapi teu nyaho nanaon tentang kelas urang jeung hayang hiji jar kalawan kelas ieu disadiakeun pikeun eta. Kami ngalakukeun éta, sadayana damel, tapi masalah énggal muncul: kumaha carana ngapdet jar tanpa ngeureunkeun klaster? Hazelcast henteu nyandak JAR énggal salami apdet node-by-node. Dina titik ieu kami mutuskeun yén urang tiasa hirup tanpa milarian indéks. Barina ogé, upami anjeun nganggo Hazelcast salaku toko konci-nilai, maka sadayana bakal jalan? Henteu ogé. Di dieu deui paripolah IMap sareng TransactionalMap béda. Dimana IMap henteu paduli, TransactionalMap ngalungkeun kasalahan.

IMap. Urang nulis 5000 objék, baca aranjeunna. Sagalana diharepkeun.

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

Tapi éta henteu tiasa dianggo dina transaksi, kami nampi 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();
        }
    });
}

Dina 3.8, mékanisme Deployment Kelas Pamaké muncul. Anjeun tiasa nunjuk hiji master node sareng ngapdet file JAR di dinya.

Ayeuna kami parantos robih pendekatan kami: urang sérialisasikeun kana JSON sareng simpen dina Hazelcast. Hazelcast henteu kedah terang struktur kelas urang, sareng urang tiasa ngamutahirkeun tanpa downtime. Versioning objék domain dikawasa ku aplikasi. Versi anu béda tina aplikasi tiasa dijalankeun dina waktos anu sami, sareng kaayaan mungkin nalika aplikasi énggal nyerat objék kalayan widang énggal, tapi anu lami henteu acan terang ngeunaan widang ieu. Sareng dina waktos anu sami, aplikasi énggal maca obyék anu ditulis ku aplikasi lami anu henteu ngagaduhan widang énggal. Kami ngadamel kaayaan sapertos kitu dina aplikasi, tapi pikeun kesederhanaan kami henteu ngarobih atanapi ngahapus lapangan, kami ngan ukur ngalegaan kelas ku nambihan widang anyar.

Kumaha urang mastikeun kinerja luhur

Opat lalampahan ka Hazelcast - alus, dua ka database - goréng

Pindah ka cache pikeun data sok langkung saé tibatan angkat ka pangkalan data, tapi anjeun ogé henteu hoyong nyimpen rékaman anu henteu dianggo. Urang ninggalkeun kaputusan ngeunaan naon cache nepi ka tahap ahir pangwangunan. Nalika fungsionalitas anyar disandi, urang ngahurungkeun logging sadaya queries di PostgreSQL (log_min_duration_statement ka 0) tur ngajalankeun nguji beban pikeun menit 20. Ngagunakeun log dikumpulkeun, Utiliti kawas pgFouine na pgBadger bisa ngawangun laporan analitik. Dina laporan, urang utamina milarian patarosan anu lambat sareng sering. Pikeun queries slow, urang ngawangun rencana palaksanaan (EXPLAIN) jeung evaluate naha query misalna bisa gancangan. Paménta sering pikeun data input anu sami pas kana cache. Urang nyobian tetep queries "datar", hiji méja per query.

eksploitasi

SV salaku hiji layanan online ieu nempatkeun kana operasi di cinyusu 2017, jeung salaku produk misah, SV dirilis dina bulan Nopémber 2017 (dina waktos éta dina status versi béta).

Dina leuwih ti sataun operasi, teu aya masalah serius dina operasi sahiji layanan online CB. Urang ngawas jasa online via Zabbix, ngumpulkeun sarta nyebarkeun tina awi.

Distribusi server SV disayogikeun dina bentuk bungkusan asli: RPM, DEB, MSI. Ditambah pikeun Windows kami nyayogikeun pamasangan tunggal dina bentuk EXE tunggal anu masang server, Hazelcast sareng Elasticsearch dina hiji mesin. Kami mimitina ngarujuk kana versi pamasangan ieu salaku versi "demo", tapi ayeuna parantos jelas yén ieu mangrupikeun pilihan panyebaran anu paling populér.

sumber: www.habr.com

Tambahkeun komentar