Halo, Habr! Nama saya Sergey Lezhnin, saya seorang arsitek senior di Sbertekh. Salah satu petunjuk pekerjaan saya adalah Unified Frontal System. Sistem ini memiliki layanan manajemen parameter konfigurasi. Ini digunakan oleh banyak pengguna, layanan dan aplikasi, yang membutuhkan kinerja tinggi. Dalam posting ini saya akan memberi tahu bagaimana layanan ini berkembang dari yang pertama, paling sederhana, ke versi saat ini dan mengapa kami akhirnya mengerahkan seluruh arsitektur 180 derajat.

Di sinilah kami mulai - ini adalah implementasi pertama dari layanan manajemen parameter:

Klien meminta parameter konfigurasi dari layanan. Layanan menerjemahkan permintaan ke dalam basis data, menerima respons, dan mengembalikannya ke klien. Pada saat yang sama, administrator dapat mengelola parameter menggunakan layanan terpisah mereka: menambah nilai baru, mengubah yang sekarang.
Pendekatan ini memiliki satu keunggulan - kesederhanaan. Ada lebih banyak kerugian, meskipun semuanya terkait:
- akses yang sering ke penyimpanan melalui jaringan,
- persaingan yang tinggi untuk akses ke database (kami memilikinya terletak di satu simpul),
- kinerja buruk.
Untuk lulus pengujian beban, arsitektur ini harus menyediakan beban tidak lebih dari yang datang melalui akses langsung ke database. Akibatnya, pengujian beban sirkuit ini tidak lulus.
Tahap kedua: kami memutuskan untuk menyimpan data di sisi layanan.

Di sini, data pada permintaan awalnya dimuat ke cache bersama dan dikembalikan dari cache pada permintaan berikutnya. Administrator layanan tidak hanya mengelola data, tetapi juga menandainya dalam cache sehingga ketika mereka berubah, mereka diperbarui.
Jadi kami mengurangi jumlah akses ke repositori. Pada saat yang sama, sinkronisasi data ternyata sederhana, karena layanan administrator memiliki akses ke cache di memori dan mengontrol pengaturan ulang. Di sisi lain, jika terjadi kegagalan jaringan, klien tidak akan dapat menerima data. Dan secara umum, logika untuk memperoleh data rumit: jika tidak ada data dalam cache, Anda perlu mendapatkannya dari database, masukkan ke dalam cache dan baru kemudian kembalikan. Perlu dikembangkan lebih lanjut.
Tahap ketiga pengembangan adalah caching data sisi klien:

Klien memiliki shell untuk mengakses layanan ("modul klien"), yang menyembunyikan cache data lokal. Jika data yang diminta tidak ada dalam cache saat meminta, layanan akan dipanggil. Layanan meminta parameter dari database dan mengembalikannya. Dibandingkan dengan skema sebelumnya, manajemen caching rumit di sini. Untuk mengatur ulang parameter, layanan harus memberi tahu pelanggan bahwa parameter ini telah berubah.
Dalam arsitektur ini, kami mengurangi jumlah panggilan ke layanan dan ke database. Sekarang, jika parameter sudah diminta, itu akan kembali ke klien tanpa mengakses jaringan, bahkan jika layanan atau database tidak tersedia. Di sisi lain, minus utama adalah bahwa logika bertukar data dengan klien itu rumit, Anda juga harus memberi tahu dia melalui beberapa layanan - misalnya, antrian pesan. Klien harus berlangganan ke topik, ia menerima pemberitahuan tentang perubahan parameter, dan dalam cache-nya klien harus mengatur ulang untuk mendapatkan nilai baru. Skema yang cukup rumit.
Akhirnya, kita sampai pada tahap terakhir saat ini. Dalam hal ini kami dibantu oleh prinsip-prinsip dasar yang dirumuskan dalam Manifesto Reaktif.
- Responsif: Sistem merespons secepat mungkin.
- Tangguh: sistem terus merespons bahkan jika terjadi kegagalan.
- Elastis: sistem menggunakan sumber daya sesuai dengan beban.
- Message Driven: menyediakan sinkronisasi dan pengiriman pesan gratis antar komponen sistem.
Skema yang sesuai dengan pendekatan ini ternyata cukup sederhana:

Prinsip umum adalah ini: klien berlangganan ke parameter konfigurasi, dan ketika nilainya berubah, server memberi tahu klien tentang hal ini. Skema di atas sedikit disederhanakan: itu tidak mencerminkan bahwa ketika klien mendaftar, ia perlu menginisialisasi dan mendapatkan nilai awal. Tapi kemudian ada hal utama di dalamnya: panah berubah arah. Sebelumnya, klien atau cache secara aktif meminta layanan untuk perubahan data, tetapi sekarang layanan itu sendiri mengirimkan peristiwa tentang perubahan data, dan mereka diperbarui oleh klien.
Arsitektur ini memiliki beberapa keunggulan penting. Jumlah panggilan ke layanan dan penyimpanan berkurang, karena klien tidak secara aktif memintanya. Bahkan, banding untuk setiap parameter yang diinginkan hanya terjadi satu kali, ketika berlangganan. Maka klien sudah cukup menerima aliran perubahan. Ketersediaan data meningkat karena klien selalu memiliki nilai - itu di-cache. Dan secara umum, skema pertukaran parameter ini cukup sederhana.
Satu-satunya kelemahan dari arsitektur ini adalah ketidakpastian dalam inisialisasi data. Sampai pembaruan pertama dengan berlangganan, nilai parameter tetap tidak terdefinisi. Tetapi ini dapat diatasi dengan mengatur nilai parameter default klien, yang diganti dengan yang sebenarnya selama pembaruan pertama.
Pemilihan teknologi
Setelah menyetujui skema tersebut, kami mulai mencari produk untuk implementasinya.
Pilih antara
Vertx.io ,
Akka.io dan
Spring Boot .

Tabel tersebut merangkum karakteristik yang menarik bagi kami. Vertx dan Akka memiliki aktor, dan Sping Boot memiliki perpustakaan layanan mikro yang pada dasarnya dekat dengan para aktor. Demikian pula dengan reaktivitas: Spring Boot memiliki perpustakaan WebFlux sendiri yang mengimplementasikan fitur yang sama. Kami memperkirakan tingkat pencahayaan di dalam tabel. Adapun bahasa, dari tiga opsi, Vertx dianggap polyglot: mendukung Java, Scala, Kotlin, dan JavaScript. Akka memiliki Scala dan Jawa; Kotlin mungkin juga bisa digunakan, tetapi tidak ada dukungan langsung. Musim semi memiliki Jawa, Kotlin, dan Groovy.
Hasilnya, Vertx menang. Ngomong-ngomong, mereka banyak berbicara tentang dia di konferensi JUG, dan memang banyak perusahaan yang menggunakannya. Ini adalah screenshot dari situs pengembang:

Di Vertx.io, skema implementasi solusi kami adalah sebagai berikut:

Kami memutuskan untuk menyimpan parameter tidak dalam database, tetapi dalam repositori Git. Kami dapat menggunakan sumber data yang relatif lambat ini dengan baik karena fakta bahwa klien tidak secara aktif meminta parameter dan jumlah klik berkurang.
Pembaca (verticle) membaca data dari repositori Git ke dalam memori aplikasi untuk mempercepat akses pengguna ke data. Ini penting, misalnya, ketika berlangganan parameter. Selain itu, pembaca memproses pembaruan - membaca kembali dan menandai data, menggantikan data lama dengan yang baru.
Event Bus adalah layanan Vertx yang mengirimkan acara antara vertikal maupun keluar melalui jembatan. Termasuk melalui jembatan websocket, yang digunakan dalam hal ini. Ketika acara perubahan parameter tiba, Bus Acara mengirimkannya ke klien.
Akhirnya, di sisi klien, klien web sederhana diimplementasikan di sini, yang berlangganan acara (perubahan parameter) dan menampilkan perubahan ini di halaman.
Bagaimana cara kerjanya
Kami menunjukkan bagaimana semuanya bekerja melalui aplikasi web.
Kami meluncurkan halaman aplikasi di browser. Kami berlangganan perubahan data utama. Lalu kita pergi ke halaman proyek dalam GitLab lokal, ubah data dalam format JSON dan simpan ke repositori. Aplikasi menampilkan perubahan yang sesuai, yang kami butuhkan.
Itu saja. Anda dapat menemukan kode sumber demo di
repositori git saya, dan bertanya di komentar.