Apakah Anda selalu membutuhkan Docker, layanan mikro, dan pemrograman reaktif?



Diposting oleh Denis Tsyplakov , Solution Architect, DataArt

Di DataArt, saya bekerja dalam dua cara. Yang pertama, saya membantu orang memperbaiki sistem yang rusak dengan satu atau lain cara dan karena berbagai alasan. Pada bagian kedua, saya membantu merancang sistem baru agar tidak rusak di masa depan, atau, lebih realistis, memecahkannya lebih sulit.

Jika Anda tidak melakukan sesuatu yang pada dasarnya baru, misalnya, mesin pencari Internet pertama di dunia atau kecerdasan buatan untuk mengendalikan peluncuran rudal nuklir, membuat desain sistem yang baik cukup sederhana. Cukup dengan mempertimbangkan semua persyaratan, lihat desain sistem yang sama dan lakukan hal yang sama, tanpa membuat kesalahan serius. Kedengarannya seperti penyederhanaan masalah yang berlebihan, tetapi mari kita ingat bahwa di halaman adalah tahun 2019, dan ada "resep standar" untuk desain sistem untuk hampir semua hal. Sebuah bisnis dapat menangani tugas teknis yang rumit - katakanlah, proses satu juta file PDF yang heterogen dan keluarkan tabel pengeluaran dari mereka - tetapi arsitektur sistem jarang sangat orisinal. Hal utama di sini adalah jangan membuat kesalahan dalam menentukan sistem yang sedang kita bangun, dan jangan sampai melewatkan pilihan teknologi.

Kesalahan umum terjadi secara teratur pada paragraf terakhir, beberapa di antaranya akan saya bahas dalam sebuah artikel.

Apa kesulitan memilih tumpukan teknis? Menambahkan teknologi apa pun ke proyek membuatnya lebih sulit dan membawa semacam batasan. Karenanya, menambahkan alat baru (kerangka kerja, pustaka) hanya boleh dilakukan bila alat ini lebih bermanfaat daripada berbahaya. Dalam percakapan dengan anggota tim tentang menambahkan perpustakaan dan kerangka kerja, saya sering bergurau menggunakan trik berikut: โ€œJika Anda ingin menambahkan ketergantungan baru ke proyek, Anda menaruh sekotak bir untuk tim. Jika Anda berpikir bahwa ketergantungan pada peti bir ini tidak sepadan, jangan tambahkan. "

Misalkan kita membuat aplikasi tertentu, katakanlah, di Jawa, dan untuk memanipulasi tanggal kita menambahkan perpustakaan TimeMagus (contoh fiktif) ke proyek. Perpustakaannya sangat baik, menyediakan banyak fitur yang tidak tersedia di perpustakaan kelas standar. Bagaimana keputusan seperti itu bisa berbahaya? Mari kita lihat skenario yang mungkin:

  1. Tidak semua pengembang tahu pustaka non-standar, ambang entri untuk pengembang baru akan lebih tinggi. Kemungkinan meningkat bahwa pengembang baru akan membuat kesalahan ketika memanipulasi tanggal menggunakan perpustakaan yang tidak dikenal.
  2. Ukuran distribusi meningkat. Ketika ukuran aplikasi rata-rata di Spring Boot dapat dengan mudah tumbuh hingga 100 MB, ini tidak berarti sepele. Saya melihat kasus ketika, demi satu metode, perpustakaan 30 MB ditarik ke kit distribusi. Mereka membenarkannya seperti ini: "Saya menggunakan perpustakaan ini dalam proyek sebelumnya, dan ada metode yang nyaman di sana."
  3. Tergantung pada perpustakaan, waktu mulai dapat meningkat secara signifikan.
  4. Pengembang perpustakaan dapat meninggalkan gagasannya, maka perpustakaan akan mulai bertentangan dengan versi baru Java, atau bug akan terdeteksi di dalamnya (disebabkan oleh, misalnya, mengubah zona waktu), dan tidak ada patch yang akan dirilis.
  5. Lisensi perpustakaan pada suatu saat akan bertentangan dengan lisensi produk Anda (apakah Anda memeriksa lisensi untuk semua produk yang Anda gunakan?).
  6. Jar neraka - Pustaka TimeMagus membutuhkan versi terbaru dari pustaka SuperCollections, lalu setelah beberapa bulan Anda perlu menghubungkan pustaka untuk integrasi dengan API pihak ketiga, yang tidak berfungsi dengan versi terbaru dari SuperCollections, dan hanya berfungsi dengan versi 2.x. Anda tidak dapat menghubungkan API. Tidak ada perpustakaan lain untuk bekerja dengan API ini.

Di sisi lain, perpustakaan standar memberi kami alat yang nyaman untuk memanipulasi tanggal, dan jika Anda tidak perlu mempertahankan, misalnya, kalender eksotis atau menghitung jumlah hari dari hari ini hingga "hari kedua bulan baru ketiga di tahun sebelumnya elang yang melonjak", mungkin layak jangan menggunakan perpustakaan pihak ketiga. Bahkan jika itu benar-benar luar biasa dan dalam skala proyek, itu akan menghemat sebanyak 50 baris kode.

Contoh yang dipertimbangkan cukup sederhana, dan saya pikir mudah untuk mengambil keputusan. Tetapi ada sejumlah teknologi yang tersebar luas, oleh telinga semua orang, dan penggunaannya jelas, yang membuat pilihan lebih sulit - mereka benar-benar memberikan keuntungan serius bagi pengembang. Tetapi ini tidak selalu harus menjadi kesempatan untuk menyeret mereka ke dalam proyek Anda. Mari kita lihat beberapa di antaranya.

Docker


Sebelum munculnya teknologi yang sangat keren ini, ketika menggunakan sistem, ada banyak masalah yang tidak menyenangkan dan rumit terkait dengan konflik versi dan ketergantungan yang tidak jelas. Docker memungkinkan Anda untuk mengemas snapshot dari status sistem, menggulungnya menjadi produksi dan menjalankannya di sana. Ini memungkinkan konflik yang disebutkan untuk dihindari, yang tentu saja bagus.

Sebelumnya, ini dilakukan dengan cara yang mengerikan, dan beberapa tugas tidak diselesaikan sama sekali. Misalnya, Anda memiliki aplikasi PHP yang menggunakan pustaka ImageMagick untuk bekerja dengan gambar, aplikasi Anda juga memerlukan pengaturan php.ini tertentu, dan aplikasi itu sendiri di-host menggunakan Apache httpd. Tetapi ada masalah: beberapa rutin rutin diimplementasikan dengan menjalankan skrip Python dari cron, dan pustaka yang digunakan oleh skrip ini bertentangan dengan versi pustaka yang digunakan dalam aplikasi Anda. Docker memungkinkan Anda untuk mengemas seluruh aplikasi Anda, bersama dengan pengaturan, pustaka, dan server HTTP, ke dalam satu wadah yang melayani permintaan pada port 80, dan rutin ke wadah lain. Semua bersama-sama akan bekerja dengan sempurna, dan Anda bisa melupakan konflik perpustakaan.

Haruskah saya menggunakan Docker untuk mengemas setiap aplikasi? Pendapat saya: tidak, tidak sepadan. Gambar menunjukkan komposisi khas dari aplikasi buruh pelabuhan yang digunakan di AWS. Kotak di sini menunjukkan lapisan insulasi yang kita miliki.



Persegi panjang terbesar adalah mesin fisik. Berikutnya adalah sistem operasi mesin fisik. Lalu - virtualizer Amazonian, kemudian OS mesin virtual, lalu wadah buruh pelabuhan, diikuti oleh wadah OS, JVM, lalu wadah Servlet (jika itu adalah aplikasi web), dan kode aplikasi Anda sudah ada di dalamnya. Artinya, kita sudah melihat beberapa lapisan isolasi.

Situasi akan terlihat lebih buruk jika kita melihat akronim JVM. JVM, anehnya, Java Virtual Machine, yaitu, pada kenyataannya, kami selalu memiliki setidaknya satu mesin virtual di Jawa. Menambahkan di sini wadah Docker tambahan, pertama, sering tidak memberikan keuntungan yang nyata, karena JVM dengan sendirinya sudah mengisolasi kami dengan cukup baik dari lingkungan eksternal, dan kedua, ini bukan tanpa biaya.

Saya mengambil angka dari studi IBM, jika tidak salah, dua tahun lalu. Secara singkat, jika kita berbicara tentang operasi disk, penggunaan prosesor atau akses memori, Docker hampir tidak menambahkan overhead (secara harfiah sepersekian persen), tetapi jika kita berbicara tentang latensi jaringan, penundaannya cukup mencolok. Mereka bukan raksasa, tetapi tergantung pada aplikasi apa yang Anda miliki, mereka mungkin mengejutkan Anda dengan tidak menyenangkan.



Plus, Docker memakan ruang disk tambahan, menghabiskan sebagian memori, menambah waktu mulai. Ketiga poin tersebut tidak kritis untuk sebagian besar sistem - biasanya ada banyak ruang disk dan memori. Waktu peluncuran, sebagai suatu peraturan, juga bukan masalah kritis, yang terpenting adalah aplikasi dimulai. Tetapi masih ada situasi di mana memori mungkin habis, dan waktu mulai total sistem, yang terdiri dari dua puluh layanan dependen, sudah cukup besar. Selain itu, ini mempengaruhi biaya hosting. Dan jika Anda terlibat dalam perdagangan frekuensi tinggi, Docker pasti tidak cocok untuk Anda. Dalam kasus umum, lebih baik untuk tidak mengurangi aplikasi yang sensitif terhadap penundaan jaringan hingga 250-500 ms.

Juga, dengan buruh pelabuhan, analisis masalah dalam protokol jaringan terasa rumit, tidak hanya penundaan tumbuh, tetapi semua waktu menjadi berbeda.

Kapan Docker benar-benar dibutuhkan?


Ketika kita memiliki versi JRE yang berbeda, dan alangkah baiknya menyeret JRE. Ada kalanya Anda perlu menjalankan versi Java tertentu (bukan โ€œJava terbaru 8โ€, tetapi sesuatu yang lebih spesifik). Dalam hal ini, ada baiknya untuk mengemas JRE dengan aplikasi dan dijalankan sebagai wadah. Pada prinsipnya, jelas bahwa versi Java yang berbeda dapat diletakkan pada sistem target karena JAVA_HOME, dll. Tetapi Docker dalam pengertian ini jauh lebih nyaman, karena Anda tahu versi JRE yang tepat, semuanya dikemas bersama-sama dan dengan JRE lain aplikasi tidak akan memulai secara kebetulan. .

Docker juga diperlukan jika Anda memiliki dependensi pada beberapa perpustakaan biner, misalnya, untuk pemrosesan gambar. Dalam hal ini, mungkin ide yang baik untuk mengemas semua perpustakaan yang diperlukan dengan aplikasi Java itu sendiri.

Kasus berikut mengacu pada sistem yang merupakan gabungan kompleks dari berbagai layanan yang ditulis dalam berbagai bahasa. Anda memiliki bagian di Node.js, bagian di Jawa, perpustakaan di Go, dan, di samping itu, semacam Machine Learning dengan Python. Seluruh kebun binatang ini harus disetel dengan hati-hati dan hati-hati untuk mengajarkan elemen-elemennya untuk saling melihat. Ketergantungan, jalur, alamat IP - semua ini harus dicat dan hati-hati dalam produksi. Tentu saja, dalam hal ini, Docker akan banyak membantu Anda. Selain itu, melakukannya tanpa bantuannya sangat menyakitkan.

Docker dapat memberikan kenyamanan ketika Anda perlu menentukan banyak parameter berbeda pada baris perintah untuk memulai aplikasi. Di sisi lain, skrip bash melakukan ini dengan sangat baik, seringkali dari satu baris. Putuskan mana yang akan digunakan dengan lebih baik.

Hal terakhir yang terlintas dalam pikiran sekaligus adalah situasi ketika Anda menggunakan, katakanlah, Kubernetes, dan Anda perlu melakukan orkestrasi sistem, yaitu, meningkatkan sejumlah layanan Microsoft yang berbeda yang secara otomatis skala menurut aturan tertentu.

Dalam semua kasus lain, Spring Boot sudah cukup untuk mengemas semuanya menjadi satu file jar. Dan, pada prinsipnya, tabung springboot adalah metafora yang bagus untuk wadah Docker. Ini, tentu saja, bukan hal yang sama, tetapi dalam hal kemudahan penyebaran, mereka sangat mirip.

Kubernetes


Bagaimana jika kita menggunakan Kubernet? Untuk memulainya, teknologi ini memungkinkan Anda untuk menyebarkan sejumlah besar layanan microsoft ke mesin yang berbeda, mengelolanya, melakukan autoscaling, dll. Namun, ada banyak aplikasi yang memungkinkan Anda untuk mengontrol orkestrasi, misalnya, Wayang, mesin CF, SaltStack, dan lainnya. Kubernetes sendiri memang baik, tetapi dapat menambah overhead yang signifikan, yang tidak setiap proyek siap untuk hidup dengannya.

Alat favorit saya adalah Ansible, dikombinasikan dengan Terraform di mana Anda membutuhkannya. Ansible adalah alat ringan deklaratif yang cukup sederhana. Itu tidak memerlukan instalasi agen khusus dan memiliki sintaks yang dapat dimengerti dari file konfigurasi. Jika Anda terbiasa dengan penulisan Docker, Anda akan segera melihat bagian yang tumpang tindih. Dan jika Anda menggunakan Ansible, tidak perlu melakukan pra-reservasi - Anda dapat menggunakan sistem menggunakan cara yang lebih klasik.

Jelas bahwa semua sama, ini adalah teknologi yang berbeda, tetapi ada beberapa tugas yang dapat dipertukarkan. Dan pendekatan yang teliti untuk merancang membutuhkan analisis teknologi mana yang lebih cocok untuk sistem yang sedang dikembangkan. Dan bagaimana akan lebih baik untuk mencocokkannya dalam beberapa tahun.

Jika jumlah layanan yang berbeda pada sistem Anda kecil dan konfigurasinya relatif sederhana, misalnya, Anda hanya memiliki satu file jar, dan Anda tidak melihat adanya peningkatan kompleksitas yang tiba-tiba dan eksplosif, Anda mungkin perlu bertahan dengan mekanisme penyebaran klasik.

Ini menimbulkan pertanyaan, "tunggu, bagaimana satu file jar?". Sistem harus terdiri dari sebanyak mungkin layanan microser atom! Mari kita lihat siapa dan apa sistem seharusnya dengan layanan microser.

Layanan microser


Pertama-tama, layanan microser memungkinkan pencapaian fleksibilitas dan skalabilitas yang lebih besar, dan memungkinkan versi fleksibel dari setiap bagian sistem. Misalkan kita memiliki beberapa jenis aplikasi yang telah diproduksi selama bertahun-tahun. Fungsionalitas sedang tumbuh, tetapi kita tidak dapat terus-menerus mengembangkannya dengan cara yang luas. Sebagai contoh.

Kami memiliki aplikasi di Spring Boot 1 dan Java 8. Kombinasi yang luar biasa dan stabil. Tetapi tahun ini adalah 2019, dan apakah kita suka atau tidak, kita perlu bergerak menuju Spring Boot 2 dan Java 12. Bahkan transisi yang relatif sederhana dari sistem besar ke versi baru Spring Boot bisa sangat melelahkan, tetapi tentang melompati jurang dari Java 8 ke Java 12 Saya tidak ingin bicara. Artinya, dalam teori semuanya sederhana: kita bermigrasi, memperbaiki masalah yang muncul, kita menguji semuanya dan menjalankannya dalam produksi. Dalam praktiknya, ini bisa berarti beberapa bulan kerja yang tidak membawa fungsionalitas baru ke bisnis. Sedikit pindah ke Java 12, seperti yang Anda tahu, juga tidak berhasil. Di sini arsitektur microservice dapat membantu kami.

Kami dapat mengalokasikan beberapa grup fungsi yang kompak dari aplikasi kami ke layanan yang terpisah, memigrasikan grup fungsi ini ke tumpukan teknis baru, dan menggulungnya menjadi produksi dalam waktu yang relatif singkat. Ulangi proses itu sedikit demi sedikit sampai teknologi lama benar-benar habis.

Juga, layanan microser dapat memberikan isolasi kesalahan, ketika satu komponen jatuh tidak merusak seluruh sistem.

Layanan Microsoft memungkinkan kita untuk memiliki tumpukan teknis yang fleksibel, yaitu, untuk tidak menulis semuanya secara monolitik dalam satu bahasa dan satu versi, dan jika perlu gunakan tumpukan teknis yang berbeda untuk masing-masing komponen. Tentu saja, lebih baik ketika Anda menggunakan tumpukan teknis yang seragam, tetapi ini tidak selalu memungkinkan, dan dalam hal ini, layanan microser dapat membantu.

Layanan microser juga memungkinkan cara teknis untuk memecahkan sejumlah masalah manajerial. Misalnya, ketika tim besar Anda terdiri dari grup terpisah yang bekerja di perusahaan yang berbeda (duduk di zona waktu yang berbeda dan berbicara dalam bahasa yang berbeda). Layanan microser membantu mengisolasi keragaman organisasi ini dengan komponen yang akan dikembangkan secara terpisah. Masalah salah satu bagian tim akan tetap berada di dalam satu layanan, dan tidak menyebar ke seluruh aplikasi.

Tetapi layanan microser bukan satu-satunya cara untuk memecahkan masalah ini. Anehnya, beberapa dekade yang lalu, bagi setengah dari mereka, orang-orang datang dengan kelas, dan sedikit kemudian - komponen dan pola Inversion of Control.

Jika kita melihat Spring, kita melihat bahwa sebenarnya itu adalah arsitektur microservice di dalam proses Java. Kita dapat mendeklarasikan komponen, yang pada dasarnya adalah layanan. Kami memiliki kemampuan untuk melakukan pencarian melalui @Autowired, ada alat untuk mengelola siklus hidup komponen dan kemampuan untuk secara terpisah mengkonfigurasi komponen dari selusin sumber berbeda. Pada prinsipnya, kami mendapatkan hampir semua yang kami miliki dengan layanan microser - hanya di dalam satu proses, yang secara signifikan mengurangi biaya. Kelas Java reguler adalah kontrak API yang sama yang juga memungkinkan Anda untuk mengisolasi detail implementasi.

Sebenarnya, di dunia Java, layanan microser paling mirip dengan OSGi - di sana kami memiliki salinan hampir semua yang ada di layanan microser, kecuali, di samping kemungkinan menggunakan bahasa pemrograman yang berbeda dan eksekusi kode pada server yang berbeda. Tetapi bahkan tetap dengan kemampuan kelas Java, kami memiliki alat yang cukup kuat untuk memecahkan sejumlah besar masalah isolasi.

Bahkan dalam skenario "manajerial" dengan isolasi tim, kita dapat membuat repositori terpisah yang berisi modul Java terpisah dengan kontrak eksternal yang jelas dan serangkaian tes. Ini secara signifikan akan mengurangi kemampuan satu tim untuk secara tidak sengaja mempersulit kehidupan tim lain.



Saya telah berulang kali mendengar bahwa tidak mungkin mengisolasi detail implementasi tanpa layanan microser. Tapi saya bisa menjawab bahwa seluruh industri perangkat lunak hanya mengisolasi implementasi. Untuk ini, subrutin pertama kali ditemukan (pada 50-an abad terakhir), kemudian fungsi, prosedur, kelas, dan kemudian layanan mikro. Tetapi fakta bahwa layanan microser dalam seri ini muncul terakhir tidak menjadikan mereka titik pengembangan tertinggi dan tidak mengharuskan kita untuk selalu menggunakan bantuan mereka.

Saat menggunakan layanan microser, seseorang juga harus mempertimbangkan bahwa panggilan di antara mereka membutuhkan waktu. Ini sering tidak penting, tetapi saya telah melihat kasus ketika pelanggan perlu menyesuaikan waktu respons sistem 3 detik. Merupakan kewajiban kontraktual untuk terhubung ke sistem pihak ketiga. Rantai panggilan melewati beberapa lusin layanan microser atom, dan overhead membuat panggilan HTTP tidak memungkinkan untuk menyusut dalam 3 detik. Secara umum, Anda perlu memahami bahwa setiap pembagian kode monolitik menjadi sejumlah layanan pasti mempengaruhi kinerja sistem secara keseluruhan. Hanya karena data tidak dapat diteleportasi antara proses dan server "gratis."

Kapan layanan microser diperlukan?


Dalam kasus apa aplikasi monolitik benar-benar perlu dibagi menjadi beberapa layanan mikro? Pertama, ketika ada penggunaan sumber daya yang tidak seimbang di bidang fungsional.

Misalnya, kami memiliki sekelompok panggilan API yang melakukan perhitungan yang membutuhkan banyak waktu prosesor. Dan ada sekelompok panggilan API yang dijalankan dengan sangat cepat, tetapi membutuhkan struktur data 64 GB yang rumit untuk disimpan dalam memori. Untuk kelompok pertama, kita membutuhkan sekelompok mesin dengan total 32 prosesor, untuk mesin yang kedua sudah cukup (OK, biarkan ada dua mesin untuk toleransi kesalahan) dengan memori 64 GB. Jika kita memiliki aplikasi monolitik, maka kita akan membutuhkan 64 GB memori pada setiap mesin, yang meningkatkan biaya setiap mesin. Jika fungsi-fungsi ini dibagi menjadi dua layanan terpisah, kita dapat menghemat sumber daya dengan mengoptimalkan server untuk fungsi tertentu. Konfigurasi server mungkin terlihat seperti ini:



Layanan microser diperlukan dan jika kita perlu secara serius mengukur beberapa area fungsional yang sempit. Misalnya, seratus metode API disebut 10 kali per detik, dan, katakanlah, empat metode API disebut 10 ribu kali per detik. Meningkatkan seluruh sistem sering kali tidak perlu, yaitu, kita, tentu saja, dapat melipatgandakan semua 100 metode ke banyak server, tetapi ini, sebagai suatu peraturan, terasa lebih mahal dan lebih rumit daripada penskalaan sekelompok metode yang sempit. Kami dapat memisahkan keempat panggilan ini menjadi layanan terpisah dan menskalakannya hanya ke sejumlah besar server.

Juga jelas bahwa kita mungkin memerlukan microservice jika kita telah menulis area fungsional yang terpisah, misalnya, dengan Python. Karena beberapa perpustakaan (misalnya, untuk Pembelajaran Mesin) ternyata hanya tersedia dalam Python, dan kami ingin memisahkannya menjadi layanan terpisah. Masuk akal juga untuk membuat microservice jika beberapa bagian dari sistem rentan terhadap kegagalan. Tentu saja baik untuk menulis kode sehingga tidak ada kegagalan pada prinsipnya, tetapi alasannya bisa eksternal. Dan tidak ada yang selamat dari kesalahan mereka sendiri. Dalam hal ini, bug dapat diisolasi di dalam proses terpisah.

Jika aplikasi Anda tidak memiliki salah satu di atas dan tidak diharapkan dalam waktu dekat, kemungkinan besar, aplikasi monolitik akan cocok untuk Anda. Satu-satunya hal - saya sarankan menulisnya sehingga area fungsional yang tidak terkait satu sama lain tidak tergantung satu sama lain dalam kode. Sehingga, jika perlu, area fungsional yang tidak saling terhubung dapat dipisahkan satu sama lain. Namun, ini selalu merupakan rekomendasi yang baik, mengikuti yang meningkatkan konsistensi internal dan mengajarkan Anda untuk dengan hati-hati merumuskan kontrak modul.

Arsitektur reaktif dan pemrograman reaktif


Pendekatan reaktif adalah hal yang relatif baru. Momen kemunculannya dapat dianggap sebagai tahun 2014, ketika The Reactive Manifesto diterbitkan. Dua tahun setelah penerbitan manifesto, dia dikenal semua orang. Ini adalah pendekatan yang benar-benar revolusioner untuk desain sistem. , , , , .



, . , , : ยซ , !?ยป , , , , ยซยป. , 100% , , .

โ€” , โ€” . .

? , .

- , - . - -, , HTTP-. , . , . , , , .

? , HTTP- , ( callback) ( ) . , - ( , HTTP-) .

โ€” . . . . 3 Ghz , , . . . , Java-, HTTP- โ€” 5-10%. , , , , 100 50 $/ โ€” $500 . , , .

, ? .

, . , , , , , , . , , . .

- . , JDBC ( . ADA, R2DBC, ). 90 % , . โ€” HTTP- , . , .



?


, , , ( ) . โ€” - , . , , , HTTP.

, , , , , , .

. , ยซ , ยป , , , . , , , 10 11 , , , .

Kesimpulan


, . , , , .

Source: https://habr.com/ru/post/id436346/


All Articles