Kepiye lan kenapa kita nulis layanan skalabel kanthi beban dhuwur kanggo 1C: Enterprise: Java, PostgreSQL, Hazelcast

Ing artikel iki kita bakal pirembagan bab carane lan apa kita dikembangaké Sistem Interaksi – mekanisme sing nransfer informasi antarane aplikasi klien lan 1C: Enterprise server - saka nyetel tugas kanggo mikir liwat arsitektur lan rincian implementasine.

Sistem Interaksi (sabanjuré diarani SV) minangka sistem olahpesen sing didistribusikake, fault-tolerant kanthi pangiriman dijamin. SV dirancang minangka layanan dhuwur-muat karo kaukur dhuwur, kasedhiya loro minangka layanan online (diwenehake dening 1C) lan minangka produk massa-diprodhuksi sing bisa tugasaken ing fasilitas server dhewe.

SV nggunakake panyimpenan mbagekke hazelcast lan mesin telusur Elasticsearch. Kita uga bakal pirembagan bab Jawa lan carane kita horisontal ukuran PostgreSQL.
Kepiye lan kenapa kita nulis layanan skalabel kanthi beban dhuwur kanggo 1C: Enterprise: Java, PostgreSQL, Hazelcast

Formulasi masalah

Kanggo nggawe jelas kenapa kita nggawe Sistem Interaksi, aku bakal ngandhani sampeyan babagan cara pangembangan aplikasi bisnis ing 1C.

Kanggo miwiti, sethithik babagan kita kanggo sing durung ngerti apa sing kita lakoni :) Kita nggawe platform teknologi 1C: Enterprise. Platform kasebut kalebu alat pangembangan aplikasi bisnis, uga runtime sing ngidini aplikasi bisnis bisa mlaku ing lingkungan lintas platform.

Paradigma pangembangan klien-server

Aplikasi bisnis sing digawe ing 1C: Enterprise beroperasi ing telung tingkat klien-server arsitektur "DBMS - server aplikasi - klien". Kode aplikasi ditulis ing dibangun ing basa 1C, bisa dieksekusi ing server aplikasi utawa ing klien. Kabeh karya karo obyek aplikasi (direktori, dokumen, etc.), uga maca lan nulis database, dileksanakake mung ing server. Fungsi formulir lan antarmuka printah uga dileksanakake ing server. Klien nindakake nampa, mbukak lan nampilake formulir, "komunikasi" karo pangguna (peringatan, pitakonan ...), petungan cilik ing formulir sing mbutuhake respon cepet (contone, nikelake rega kanthi jumlah), nggarap file lokal, nggarap peralatan.

Ing kode aplikasi, header prosedur lan fungsi kudu kanthi jelas nuduhake ing ngendi kode kasebut bakal dieksekusi - nggunakake arahan &AtClient / &AtServer (&AtClient / &AtServer ing versi basa Inggris). Pangembang 1C saiki bakal mbenerake aku kanthi ujar manawa arahan kasebut sejatine luwih saka, nanging kanggo kita iki ora penting saiki.

Sampeyan bisa nelpon kode server saka kode klien, nanging sampeyan ora bisa nelpon kode klien saka kode server. Iki minangka watesan dhasar sing digawe amarga sawetara alasan. Utamane, amarga kode server kudu ditulis kanthi cara sing padha, ora ketompo ngendi wae diarani - saka klien utawa saka server. Lan ing kasus nelpon kode server saka kode server liyane, ora ana klien kaya kasebut. Lan amarga sajrone eksekusi kode server, klien sing diarani bisa nutup, metu saka aplikasi kasebut, lan server ora ana sing bisa nelpon maneh.

Kepiye lan kenapa kita nulis layanan skalabel kanthi beban dhuwur kanggo 1C: Enterprise: Java, PostgreSQL, Hazelcast
Kode sing nangani klik tombol: nelpon prosedur server saka klien bakal bisa, nelpon prosedur klien saka server ora bakal

Iki tegese yen kita pengin ngirim sawetara pesen saka server kanggo aplikasi klien, contone, sing generasi laporan "long-mlaku" wis rampung lan laporan bisa viewed, kita ora duwe cara kuwi. Sampeyan kudu nggunakake trik, contone, polling server periodik saka kode klien. Nanging pendekatan iki ngemot sistem kanthi telpon sing ora perlu, lan umume ora katon elegan.

Lan ana uga sing perlu, contone, nalika telpon teka SIP- nalika nelpon, ngabari aplikasi klien babagan iki supaya bisa nggunakake nomer panelpon kanggo nemokake ing database counterparty lan nuduhake informasi pangguna bab counterparty nelpon. Utawa, contone, nalika pesenan teka ing gudang, ngabari aplikasi klien pelanggan babagan iki. Umumé, ana akeh kasus ing ngendi mekanisme kasebut bakal migunani.

Produksi dhewe

Nggawe mekanisme olahpesen. Cepet, dipercaya, kanthi pangiriman sing dijamin, kanthi kemampuan kanggo nggoleki pesen kanthi fleksibel. Adhedhasar mekanisme kasebut, tindakake utusan (pesen, panggilan video) sing mlaku ing aplikasi 1C.

Rancang sistem supaya bisa skala horisontal. Tambah beban kudu ditutupi kanthi nambah jumlah simpul.

Реализация

Kita mutusake ora nggabungake bagean server SV langsung menyang platform 1C: Enterprise, nanging kanggo ngleksanakake minangka produk sing kapisah, API sing bisa diarani saka kode solusi aplikasi 1C. Iki ditindakake kanthi sawetara alasan, sing utama yaiku aku pengin nggawe pesen ijol-ijolan ing antarane aplikasi 1C sing beda (contone, antarane Manajemen Perdagangan lan Akuntansi). Aplikasi 1C sing beda-beda bisa mlaku ing macem-macem versi 1C: Platform Enterprise, dumunung ing server sing beda, lsp. Ing kahanan kaya mengkono, implementasine SV minangka produk kapisah sing dumunung "ing sisih" instalasi 1C minangka solusi optimal.

Dadi, kita mutusake kanggo nggawe SV minangka produk sing kapisah. Disaranake perusahaan cilik nggunakake server CB sing diinstal ing maya (wss://1cdialog.com) kanggo ngindhari biaya overhead sing ana gandhengane karo instalasi lan konfigurasi server lokal. Klien gedhe bisa uga pengin nginstal server CB dhewe ing fasilitase. Kita nggunakake pendekatan sing padha ing produk SaaS awan 1c seger - diprodhuksi minangka produk massal kanggo instalasi ing situs klien, lan uga disebarake ing awan kita https://1cfresh.com/.

Aplikasi

Kanggo nyebarake toleransi beban lan kesalahan, kita ora bakal nyebarake siji aplikasi Java, nanging sawetara, kanthi keseimbangan beban ing ngarepe. Yen sampeyan kudu ngirim pesen saka simpul menyang simpul, gunakake nerbitake / langganan ing Hazelcast.

Komunikasi antarane klien lan server liwat websocket. Iku uga cocog kanggo sistem nyata-wektu.

Cache sing disebarake

Kita milih antarane Redis, Hazelcast lan Ehcache. Wis 2015. Redis mung ngeculake kluster anyar (anyar banget, medeni), ana Sentinel kanthi akeh larangan. Ehcache ora ngerti carane ngumpul dadi kluster (fungsi iki muncul mengko). Kita mutusake kanggo nyoba karo Hazelcast 3.4.
Hazelcast dirakit dadi kluster metu saka kothak. Ing mode simpul tunggal, ora banget migunani lan mung bisa digunakake minangka cache - ora ngerti carane mbucal data menyang disk, yen sampeyan kelangan simpul mung, sampeyan bakal kelangan data. Kita nyebarake sawetara Hazelcasts, ing antarane kita nggawe serep data kritis. Kita ora gawe serep cache - kita ora pikiran iku.

Kanggo kita, Hazelcast yaiku:

  • Panyimpenan sesi pangguna. Butuh wektu dawa kanggo pindhah menyang database kanggo sesi saben wektu, supaya kita sijine kabeh mau ing Hazelcast.
  • Cache. Yen sampeyan nggoleki profil pangguna, priksa cache. Nulis pesen anyar - sijine ing cache.
  • Topik kanggo komunikasi antarane kedadean aplikasi. Node ngasilake acara lan nyelehake ing topik Hazelcast. Node aplikasi liyane sing langganan topik iki nampa lan ngolah acara kasebut.
  • Kunci kluster. Contone, kita nggawe diskusi nggunakake kunci unik (diskusi tunggal ing basis data 1C):

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

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

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

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

Kita mriksa manawa ora ana saluran. Kita njupuk kunci, mriksa maneh, lan nggawe. Yen sampeyan ora mriksa kunci sawise njupuk kunci, banjur ana kemungkinan thread liyane uga mriksa ing wayahe lan saiki bakal nyoba kanggo nggawe diskusi padha - nanging wis ana. Sampeyan ora bisa ngunci nggunakake java Lock sing disinkronake utawa biasa. Liwat database - iku alon, lan iku tega kanggo database; liwat Hazelcast - iku sing perlu.

Milih DBMS

Kita duwe pengalaman ekstensif lan sukses nggarap PostgreSQL lan kolaborasi karo pangembang DBMS iki.

Ora gampang karo kluster PostgreSQL - ana XL, XC, Citus, nanging umume iki dudu NoSQL sing ukurane metu saka kothak. Kita ora nganggep NoSQL minangka panyimpenan utama; cukup yen kita njupuk Hazelcast, sing durung tau digarap sadurunge.

Yen sampeyan kudu skala database relasional, tegese sharding. Kaya sing sampeyan ngerteni, kanthi sharding, kita dibagi database dadi bagean sing kapisah supaya saben bisa diselehake ing server sing kapisah.

Versi pisanan saka sharding kita nganggep kemampuan kanggo nyebarake saben tabel aplikasi kita ing macem-macem server kanthi proporsi sing beda. Ana akeh pesen ing server A - mangga, ayo pindhah bagean tabel iki menyang server B. Keputusan iki mung njerit babagan optimasi durung wayahe, mula kita mutusake kanggo mbatesi pendekatan multi-tenant.

Sampeyan bisa maca babagan multi-tenant, contone, ing situs web Data Citus.

SV duwe konsep aplikasi lan pelanggan. Aplikasi minangka instalasi khusus saka aplikasi bisnis, kayata ERP utawa Akuntansi, karo pangguna lan data bisnis. Pelanggan minangka organisasi utawa individu sing atas jenenge aplikasi kasebut kadhaptar ing server SV. Pelanggan bisa duwe sawetara aplikasi sing kadhaptar, lan aplikasi kasebut bisa ijol-ijolan pesen. Pelanggan dadi tenant ing sistem kita. Pesen saka sawetara pelanggan bisa ditemokake ing siji basis data fisik; yen kita weruh yen pelanggan wis wiwit ngasilake akeh lalu lintas, kita pindhah menyang database fisik sing kapisah (utawa malah server database sing kapisah).

Kita duwe database utama ing ngendi tabel rute disimpen kanthi informasi babagan lokasi kabeh database pelanggan.

Kepiye lan kenapa kita nulis layanan skalabel kanthi beban dhuwur kanggo 1C: Enterprise: Java, PostgreSQL, Hazelcast

Kanggo nyegah database utama dadi bottleneck, kita nyimpen tabel routing (lan data liyane sing kerep dibutuhake) ing cache.

Yen database pelanggan wiwit alon mudhun, kita bakal Cut menyang partisi nang. Ing proyek liyane sing digunakake pg_pathman.

Wiwit kelangan pesen pangguna iku ala, kita njaga basis data kanthi replika. Kombinasi replika sinkron lan asinkron ngidini sampeyan ngasuransiake dhewe yen ilang database utama. Mundhut pesen mung bakal kedadeyan yen database utami lan replika sinkrone gagal bebarengan.

Yen replika sinkron ilang, replika asinkron dadi sinkron.
Yen database utama ilang, replika sinkron dadi basis data utama, lan replika asinkron dadi replika sinkron.

Elasticsearch kanggo panelusuran

Wiwit, ing antarane liyane, SV uga utusan, mbutuhake cepet, trep lan fleksibel search, njupuk menyang akun morfologi, nggunakake cocog imprecise. Kita mutusaké ora reinvent setir lan nggunakake free search engine Elasticsearch, digawe adhedhasar perpustakaan Lucene. Kita uga nyebarake Elasticsearch ing kluster (master - data - data) kanggo ngilangi masalah yen gagal node aplikasi.

Ing github kita nemokake Plugin morfologi Rusia kanggo Elasticsearch lan gunakake. Ing indeks Elasticsearch kita nyimpen werna tembung (sing ditemtokake plugin) lan N-gram. Nalika pangguna ngetik teks kanggo nggoleki, kita nggoleki teks sing diketik ing antarane N-gram. Nalika disimpen ing indeks, tembung "teks" bakal dipérang dadi N-gram ing ngisor iki:

[sing, tek, tex, teks, teks, ek, ex, ext, teks, ks, kst, ksty, st, sty, sampeyan],

Lan oyod saka tembung "teks" uga bakal dilestarekake. Pendekatan iki ngidini sampeyan nggoleki ing wiwitan, ing tengah, lan ing pungkasan tembung.

Sakabèhé gambar

Kepiye lan kenapa kita nulis layanan skalabel kanthi beban dhuwur kanggo 1C: Enterprise: Java, PostgreSQL, Hazelcast
Baleni gambar saka wiwitan artikel, nanging kanthi panjelasan:

  • Balancer kapapar ing Internet; kita duwe nginx, bisa wae.
  • Instance aplikasi Java saling komunikasi liwat Hazelcast.
  • Kanggo nggarap soket web sing digunakake Nettie.
  • Aplikasi Java ditulis ing Java 8 lan kasusun saka bundel OSGi. Rencana kasebut kalebu migrasi menyang Java 10 lan transisi menyang modul.

Pangembangan lan testing

Ing proses ngembangaken lan testing SV, kita ketemu sawetara fitur menarik saka produk sing digunakake.

Nguji beban lan bocor memori

Rilis saben release SV melu testing mbukak. Iku sukses nalika:

  • Tes kasebut makarya nganti pirang-pirang dina lan ora ana kegagalan layanan
  • Wektu respon kanggo operasi kunci ora ngluwihi ambang sing nyaman
  • Penurunan kinerja dibandhingake karo versi sadurunge ora luwih saka 10%

Kita ngisi database tes kanthi data - kanggo nindakake iki, kita nampa informasi babagan pelanggan sing paling aktif saka server produksi, tikelake nomer kanthi 5 (jumlah pesen, diskusi, pangguna) lan nyoba kanthi cara kasebut.

Kita nindakake testing beban sistem interaksi ing telung konfigurasi:

  1. tes stres
  2. Sambungan mung
  3. Pendaftaran langganan

Sajrone tes stres, kita miwiti sawetara atus utas, lan mbukak sistem tanpa mandheg: nulis pesen, nggawe diskusi, nampa dhaptar pesen. Kita simulasi tumindak pangguna biasa (njaluk dhaptar pesen sing durung diwaca, nulis menyang wong) lan solusi piranti lunak (ngirim paket konfigurasi sing beda, proses tandha).

Contone, iki minangka bagian saka tes stres:

  • Pangguna mlebu
    • Njaluk diskusi sing durung diwaca
    • 50% kamungkinan kanggo maca pesen
    • 50% kamungkinan kanggo teks
    • Panganggo sabanjure:
      • Nduwe 20% kasempatan kanggo nggawe diskusi anyar
      • Acak milih sembarang diskusi sawijining
      • Mlebet
      • Panjaluk pesen, profil pangguna
      • Nggawe limang pesen sing ditujokake menyang pangguna acak saka diskusi iki
      • Ninggalake diskusi
      • Baleni 20 kaping
      • Log metu, bali menyang wiwitan skrip

    • A chatbot mlebu sistem (niru olahpesen saka kode aplikasi)
      • Nduwe kemungkinan 50% nggawe saluran anyar kanggo ijol-ijolan data (diskusi khusus)
      • 50% bisa nulis pesen menyang saluran sing ana

Skenario "Sambungan Mung" muncul kanthi alesan. Ana kahanan: pangguna wis nyambungake sistem, nanging durung melu. Saben pangguna nguripake komputer ing jam 09:00 esuk, nggawe sambungan menyang server lan tetep meneng. Wong-wong iki mbebayani, ana akeh - mung paket PING / PONG, nanging tetep sambungan menyang server (ora bisa tetep munggah - apa yen ana pesen anyar). Tes kasebut ngasilake kahanan ing ngendi akeh pangguna kasebut nyoba mlebu menyang sistem sajrone setengah jam. Iku padha karo test kaku, nanging fokus iku sabenere ing input pisanan iki - supaya ora ana gagal (wong ora nggunakake sistem, lan wis tiba mati - iku angel kanggo mikir bab Samsaya Awon).

Skrip registrasi pelanggan diwiwiti saka peluncuran pisanan. Kita nganakake tes stres lan yakin manawa sistem kasebut ora alon-alon sajrone korespondensi. Nanging pangguna teka lan registrasi wiwit gagal amarga wektu entek. Nalika ndhaptar kita digunakake / dev / acak, sing ana hubungane karo entropi sistem. Server ora duwe wektu kanggo nglumpukake entropi sing cukup lan nalika SecureRandom anyar dijaluk, beku nganti puluhan detik. Ana akeh cara metu saka kahanan iki, Contone: ngalih menyang kurang aman / dev / urandom, nginstal Papan khusus sing ngasilake entropi, generate nomer acak ing advance lan nyimpen ing blumbang. Kita nutup sementara masalah karo blumbang, nanging wiwit saiki kita wis mbukak test kapisah kanggo ndhaptar pelanggan anyar.

Kita digunakake minangka generator beban JMeter. Ora ngerti cara nggarap websocket; butuh plugin. Kapisan ing asil panelusuran kanggo pitakon "jmeter websocket" yaiku: artikel saka BlazeMeter, kang nyaranake plugin dening Maciej Zaleski.

Ing kono kita mutusake kanggo miwiti.

Meh sanalika sawise miwiti tes serius, kita nemokake yen JMeter wiwit bocor memori.

Plugin kasebut minangka crita gedhe sing kapisah; kanthi 176 lintang, ana 132 garpu ing github. Penulis dhewe wis ora setya wiwit 2015 (kita njupuk ing 2015, banjur ora nambah curiga), sawetara masalah github babagan bocor memori, 7 panjalukan tarik sing ora ditutup.
Yen sampeyan mutusake kanggo nindakake tes beban nggunakake plugin iki, mangga digatekake diskusi ing ngisor iki:

  1. Ing lingkungan multi-threaded, LinkedList biasa digunakake, lan asile NPE ing runtime. Iki bisa ditanggulangi kanthi ngalih menyang ConcurrentLinkedDeque utawa kanthi pamblokiran sing disinkronake. Kita milih pilihan pisanan kanggo awake dhewe (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/43).
  2. Bocor memori; nalika medhot, informasi sambungan ora dibusak (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/44).
  3. Ing mode streaming (nalika websocket ora ditutup ing mburi sampel, nanging digunakake mengko ing rencana), pola Response ora bisa (https://github.com/maciejzaleski/JMeter-WebSocketSampler/issues/19).

Iki minangka salah sawijining sing ana ing github. Apa sing ditindakake:

  1. Wis dijupuk garpu Elyran Kogan (@elyrank) - ndandani masalah 1 lan 3
  2. Masalah ditanggulangi 2
  3. Dianyari jetty saka 9.2.14 kanggo 9.3.12
  4. Dibungkus SimpleDateFormat ing ThreadLocal; SimpleDateFormat ora aman thread, sing nyebabake NPE nalika runtime
  5. Ndandani bocor memori liyane (sambungan salah ditutup nalika pedhot)

Lan isih mili!

Memori wiwit entek ora sedina, nanging rong dina. Ora ana wektu sing isih ana, mula kita mutusake kanggo ngluncurake benang sing luwih sithik, nanging ing papat agen. Iki kudu cukup kanggo paling sethithik sak minggu.

Wis rong dina...

Saiki Hazelcast wis entek memori. Log kasebut nuduhake yen sawise sawetara dina tes, Hazelcast wiwit sambat babagan kekurangan memori, lan sawise sawetara wektu kluster kasebut ambruk, lan kelenjar terus mati siji-siji. Kita nyambungake JVisualVM menyang hazelcast lan ndeleng "rising saw" - kanthi rutin disebut GC, nanging ora bisa mbusak memori.

Kepiye lan kenapa kita nulis layanan skalabel kanthi beban dhuwur kanggo 1C: Enterprise: Java, PostgreSQL, Hazelcast

Ternyata ing hazelcast 3.4, nalika mbusak peta / multiMap (map.destroy ()), memori ora dibebasake:

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

Bug saiki didandani ing 3.5, nanging ana masalah nalika iku. Kita nggawe multiMaps anyar kanthi jeneng dinamis lan mbusak miturut logika kita. Kode katon kaya iki:

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

Telpon:

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

multiMap digawe kanggo saben langganan lan dibusak nalika ora perlu. Kita mutusake yen kita bakal miwiti Peta , tombol kasebut bakal dadi jeneng langganan, lan nilai kasebut bakal dadi pengenal sesi (saka sampeyan bisa entuk pengenal pangguna, yen perlu).

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

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

Grafik wis apik.

Kepiye lan kenapa kita nulis layanan skalabel kanthi beban dhuwur kanggo 1C: Enterprise: Java, PostgreSQL, Hazelcast

Apa maneh sing kita sinau babagan tes beban?

  1. JSR223 kudu ditulis kanthi groovy lan kalebu cache kompilasi - luwih cepet. link.
  2. Grafik Jmeter-Plugins luwih gampang dingerteni tinimbang sing standar. link.

Babagan pengalaman kita karo Hazelcast

Hazelcast minangka produk anyar kanggo kita, kita miwiti nggarap saka versi 3.4.1, saiki server produksi kita mlaku versi 3.9.2 (ing wektu nulis, versi paling anyar saka Hazelcast yaiku 3.10).

ID generasi

Kita miwiti karo pengenal integer. Ayo mbayangno yen kita butuh Long liyane kanggo entitas anyar. Urutan ing database ora cocok, tabel melu sharding - ternyata ana pesen ID = 1 ing DB1 lan pesen ID = 1 ing DB2, sampeyan ora bisa sijine ID iki ing Elasticsearch, utawa ing Hazelcast , nanging sing paling awon yaiku yen sampeyan pengin nggabungake data saka rong database dadi siji (contone, mutusake yen siji database cukup kanggo pelanggan kasebut). Sampeyan bisa nambah sawetara AtomicLongs kanggo Hazelcast lan tetep counter ana, banjur kinerja njupuk ID anyar incrementAndGet plus wektu kanggo panjalukan kanggo Hazelcast. Nanging Hazelcast duwe sing luwih optimal - FlakeIdGenerator. Nalika ngubungi saben klien, dheweke diwenehi sawetara ID, contone, sing pisanan - saka 1 nganti 10, sing kapindho - saka 000 nganti 10, lan liya-liyane. Saiki klien bisa ngetokake pengenal anyar dhewe nganti kisaran sing ditanggepi rampung. Kerjane kanthi cepet, nanging nalika sampeyan miwiti maneh aplikasi (lan klien Hazelcast), urutan anyar diwiwiti - mula skip, lsp. Kajaba iku, pangembang ora ngerti sebabe ID kasebut integer, nanging ora konsisten. Kita nimbang kabeh lan ngalih menyang UUID.

Ngomong-ngomong, kanggo sing pengin kaya Twitter, ana perpustakaan Snowcast - iki minangka implementasi Snowflake ing ndhuwur Hazelcast. Sampeyan bisa ndeleng ing kene:

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

Nanging kita wis ora nandang gerah maneh.

TransactionalMap.replace

Kaget liyane: TransactionalMap.replace ora bisa. Punika tes:

@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

Aku kudu nulis ganti dhewe nggunakake 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 ora mung struktur data biasa, nanging uga versi transaksional. Iku kedadeyan yen IMap bisa digunakake, nanging TransactionalMap ora ana maneh.

Lebokake JAR anyar tanpa downtime

Kaping pisanan, kita mutusake kanggo ngrekam obyek saka kelas kita ing Hazelcast. Contone, kita duwe kelas Aplikasi, kita pengin nyimpen lan maca. Simpen:

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

Kita waca:

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

Kabeh mlaku. Banjur kita mutusake kanggo mbangun indeks ing Hazelcast kanggo nggoleki kanthi:

map.addIndex("subscriberId", false);

Lan nalika nulis entitas anyar, dheweke wiwit nampa ClassNotFoundException. Hazelcast nyoba kanggo nambah kanggo indeks, nanging ora ngerti apa-apa bab kelas kita lan pengin JAR karo kelas iki diwenehake kanggo. Kita nindakake mung, kabeh bisa, nanging ana masalah anyar: carane nganyari JAR tanpa mungkasi kluster? Hazelcast ora njupuk JAR anyar sajrone nganyari node-by-node. Ing titik iki kita mutusake yen kita bisa urip tanpa nggoleki indeks. Sawise kabeh, yen sampeyan nggunakake Hazelcast minangka toko kunci-nilai, banjur kabeh bakal bisa digunakake? Ora temenan. Ing kene maneh prilaku IMap lan TransactionalMap beda. Yen IMap ora peduli, TransactionalMap nggawe kesalahan.

IMap. Kita nulis 5000 obyek, maca. Kabeh wis samesthine.

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

Nanging ora bisa digunakake ing transaksi, kita entuk 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();
        }
    });
}

Ing 3.8, mekanisme Penyebaran Kelas Panganggo muncul. Sampeyan bisa nemtokake siji master simpul lan nganyari file JAR ing.

Saiki kita wis rampung ngganti pendekatan kita: kita serialize dhewe menyang JSON lan simpen ing Hazelcast. Hazelcast ora perlu ngerti struktur kelas kita, lan kita bisa nganyari tanpa downtime. Versi obyek domain dikontrol dening aplikasi. Versi aplikasi sing beda-beda bisa mlaku bebarengan, lan kahanan bisa uga nalika aplikasi anyar nulis obyek kanthi kolom anyar, nanging sing lawas durung ngerti babagan lapangan kasebut. Lan ing wektu sing padha, aplikasi anyar maca obyek sing ditulis dening aplikasi lawas sing ora duwe lapangan anyar. Kita nangani kahanan kaya mengkono ing aplikasi, nanging kanggo gamblang kita ora ngganti utawa mbusak kothak, kita mung nggedhekake kelas kanthi nambah lapangan anyar.

Carane kita njamin kinerja dhuwur

Four trip kanggo Hazelcast - apik, loro kanggo database - ala

Menyang cache kanggo data mesthi luwih apik tinimbang menyang database, nanging sampeyan uga ora pengin nyimpen cathetan sing ora digunakake. Kita ninggalake keputusan babagan apa sing kudu disimpen nganti tahap pangembangan pungkasan. Nalika fungsi anyar dikode, kita nguripake log kabeh pitakon ing PostgreSQL (log_min_duration_statement kanggo 0) lan mbukak testing mbukak kanggo menit 20. Nggunakake log diklumpukake, keperluan kaya pgFouine lan pgBadger bisa mbangun laporan analitis. Ing laporan, kita utamane nggoleki pitakon sing alon lan kerep. Kanggo pitakon alon, kita mbangun rencana eksekusi (EXPLAIN) lan ngevaluasi manawa pitakon kasebut bisa dicepetake. Panjaluk sing kerep kanggo data input sing padha pas karo cache. Kita nyoba supaya pitakon "datar", siji tabel saben pitakon.

Operasi

SV minangka layanan online iki sijine menyang operasi ing spring 2017, lan minangka produk kapisah, SV dirilis ing November 2017 (ing wektu ing status versi beta).

Ing luwih saka setahun operasi, ora ana masalah serius ing operasi layanan online CB. We ngawasi layanan online liwat Zabbix, ngumpulake lan nyebarke saka Bambu.

Distribusi server SV diwenehake ing wangun paket asli: RPM, DEB, MSI. Plus kanggo Windows kita nyedhiyani installer siji ing wangun EXE siji sing nginstal server, Hazelcast lan Elasticsearch ing siji mesin. Kita wiwitane nyebutake versi instalasi iki minangka versi "demo", nanging saiki wis jelas yen iki minangka pilihan penyebaran paling populer.

Source: www.habr.com

Add a comment