Frontend Odnoklassniki baru: meluncurkan React in Java. Bagian I



Banyak yang telah mendengar nama GraalVM, tetapi sejauh ini tidak semua orang memiliki kesempatan untuk mencoba teknologi ini dalam produksi. Bagi Odnoklassniki, teknologi ini telah menjadi "cawan suci" yang mengubah front-end.

Pada artikel ini saya ingin berbicara tentang bagaimana kami berhasil berteman dengan Java dan JavaScript, dan mulai bermigrasi ke sistem besar dengan banyak kode lawas, dan bagaimana GraalVM membantu dengan cara ini.

Pada saat menulis artikel, ternyata seluruh volume materi tidak sesuai dengan ukuran HABR tradisional dan jika Anda memposting keseluruhan publikasi, akan butuh beberapa jam untuk membacanya. Karena itu, kami memutuskan untuk membagi artikel menjadi 2 bagian.

Dari bagian pertama Anda akan belajar tentang sejarah front-end di Odnoklassniki dan berkenalan dengan fitur-fitur historisnya, melalui jalur menemukan solusi untuk masalah yang telah terakumulasi dalam diri kami selama 11 tahun proyek, dan pada akhirnya Anda akan terjun ke fitur teknis dari implementasi server dari keputusan yang kami buat.

Latar belakang


Versi pertama Odnoklassniki muncul 13 tahun yang lalu, pada tahun 2006. Situs itu dibuat di .NET, maka tidak ada JavaScript di situs, semuanya ada di server rendering.



Setahun kemudian, Odnoklassniki memiliki lebih dari satu juta pengguna. Pada 2007, ini adalah angka yang luar biasa, dan situs itu, yang tidak mampu menahan beban, mulai turun. Pengembang memecahkan masalah dengan bantuan proyek One.lv, yang dibuat oleh perusahaan Latvia Forticom, yang kompetensi intinya ada dalam pengembangan Java. Oleh karena itu, Odnoklassniki, diputuskan untuk menulis ulang dari .NET ke Jawa.

Seiring waktu, proyek dikembangkan, dan kebutuhan muncul untuk solusi klien baru. Misalnya, ketika menavigasi situs, itu tidak sepenuhnya diperbarui, tetapi hanya bagian-bagian tertentu. Atau agar ketika mengirimkan, hanya formulir yang diperbarui, dan bukan seluruh halaman. Apalagi situs itu hanya berfungsi di Jawa.

Untuk melakukan ini, mereka datang dengan sistem di mana situs tersebut ditandai dengan blok bernama. Saat bernavigasi, permintaan dibuat ke server, yang membuat keputusan tentang apa yang harus diubah menggunakan tautan ini, dan klien diberi bagian yang diperlukan. Mesin klien hanya mengganti bagian-bagian yang diperlukan, dan dinamika klien diimplementasikan. Sangat nyaman, karena semua logika bisnis ada di server. Mesin kecil memungkinkan perusahaan untuk tetap menulis kode Java untuk mengelola klien.

Tentu saja, tanpa JavaScript minimal tidak cukup. Untuk membuat pop-up, manipulasi diperlukan: misalnya, dengan mengarahkan kursor ke layar div: blok digantung atau disembunyikan dengan tampilan: tidak ada.

Tetapi pada saat yang sama, isi pop-up diminta dari server, semua logika bisnis ada di sana dan di Jawa.



2018


Setelah 12 tahun, Odnoklassniki berubah menjadi layanan raksasa dengan lebih dari 70 juta pengguna. Kami memiliki lebih dari 7.000 mesin di 4 pusat data, dan hanya 600 ribu permintaan per detik datang ke front-end OK.RU.

Server depan Odnoklassniki terus bekerja di Jawa, dan basis kode front saja melebihi dua juta baris.



Teknologi yang diterapkan pada sisi klien juga tidak diam: banyak solusi muncul menggunakan pustaka yang berbeda: GWT, jQuery, DotJs, RequireJS dan banyak lainnya.

Pada saat itu, standar seperti React, Angular, dan Vue tidak umum, setiap pengembang mencoba menemukan solusi optimal menggunakan semua alat yang tersedia.

Menjadi jelas bahwa hidup dengan ini sangat sulit, karena sejumlah besar masalah telah menumpuk:

  • Banyak perpustakaan tua
  • Tidak ada kerangka kerja tunggal
  • Tidak ada isomorfisme (karena backend di Jawa, klien di JS)
  • Tidak ada aplikasi terstruktur tunggal pada klien
  • Responsif yang buruk
  • Alat tidak mencukupi
  • Ambang entri tinggi

Dunia sudah ada di 2018 dan itu perlu diubah.

Dengan menggunakan semua kekuatan pemikiran teknis, kami memikirkan dan merumuskan empat persyaratan dasar untuk menyelesaikan masalah:

  1. Teman sekelas harus memiliki kode isomorfik untuk UI. Karena tidak mungkin untuk terus-menerus menulis server di Jawa, dan kemudian, jika Anda perlu menambahkan semacam dinamika, mainkan hal yang sama pada klien.
  2. Diperlukan transisi yang lancar. Karena tidak mungkin untuk dengan cepat membuat versi kedua dari Odnoklassniki dan beralih
  3. Diperlukan render server (lebih lanjut tentang itu di bawah)
  4. Solusi baru, yang bekerja dengan jumlah besi yang sama, tidak boleh mengganggu kinerja dan toleransi kesalahan pada beban kami.

Mengapa rendering sisi server?


Odnoklassniki memiliki banyak pengguna yang tinggal jauh dari Moskow dan mereka tidak selalu memiliki internet yang bagus.



Render server akan membantu pengguna ini mendapatkan konten lebih cepat. Sementara gambar dimuat, mereka akan dapat mulai membaca sesuatu:



Kami melakukan serangkaian percobaan, mencoba memahami apa yang akan terjadi jika beberapa data (misalnya, rekaman) sudah dikirim ke klien, dengan harapan. Akibatnya, ternyata hal ini memengaruhi aktivitas pengguna secara negatif.

Cara kerja server sekarang


Browser membuat permintaan ke situs OK, dan sampai ke aplikasi OK-WEB, yang seluruhnya ditulis dalam Java. Aplikasi mengikuti data di API. Antara WEB dan API, transportasi biner cepat satu-nio yang dikembangkan di Odnoklassniki diimplementasikan. Permintaan diselesaikan dalam waktu kurang dari satu milidetik. Anda dapat melihat apa itu secara terpisah . One-nio memungkinkan Anda melakukan banyak pertanyaan dengan murah tanpa khawatir tentang penundaan.

API mengeluarkan data, memberikannya ke web. Web menghasilkan halaman HTML dengan mesin Java dan memberikannya ke browser.

Semua ini sekarang kurang dari 200 ms.



Cari solusinya


Pada awalnya, konsep migrasi berdasarkan widget dikembangkan.

Aplikasi akan dikirim ke situs dalam potongan-potongan kecil. Di dalamnya mereka akan ditulis pada tumpukan baru. Dan untuk sisa situs itu hanya akan menjadi elemen DOM dengan semacam perilaku khusus.



Ini akan mirip dengan tag <video>: elemen DOM khusus dengan atribut, metode, dan acara. Akibatnya, DOM API terletak di luar, sementara fungsionalitas widget diimplementasikan di dalam tumpukan baru.

Tumpukan mana yang harus dipilih?


Sekarang konsep perlu diimplementasikan, mereka mulai memilah opsi.

Kotlin


Prototipe pertama dibuat di Kotlin. Idenya adalah sebagai berikut: untuk komponen baru, tulis logika di Kotlin, dan jelaskan markup komponen dalam XML. Semuanya dapat dijalankan di server di JVM menggunakan mesin template yang ada, dan untuk klien dapat dialihkan dalam JavaScript.



Selain memperkenalkan bahasa baru dengan ambang masuk yang tinggi, Kotlin ternyata memiliki alat yang kurang dikembangkan untuk bekerja dengan JavaScript, dan masih banyak lagi yang harus dikembangkan secara mandiri.

Karena itu, sayangnya, konsep ini harus ditinggalkan.

Node.js


Pilihan lain adalah dengan meletakkan Node.js atau runtime lain, misalnya, Dart. Tetapi apa yang terjadi?

Ada dua cara untuk menggunakan Node.js.
Cara pertama adalah mendelegasikan rendering komponen ke server di Node.js yang berjalan di server yang sama dengan aplikasi Java. Jadi, kami menyimpan aplikasi di Jawa dan hanya dalam proses rendering HTML kami membuat panggilan ke layanan yang berjalan secara lokal di Node.js.

Namun, ada beberapa masalah dengan pendekatan ini:

  1. Panggilan jarak jauh ke Node.js melibatkan serialisasi / deserialisasi input. Data ini bisa sangat produktif, misalnya, dalam kasus ketika komponen baru di JS adalah pembungkus di sekitar komponen lama yang diimplementasikan di Jawa.
  2. Panggilan jarak jauh, bahkan pada mesin lokal, jauh dari gratis, dan juga memperkenalkan penundaan tambahan. Jika ada puluhan atau ratusan komponen seperti itu di halaman, bahkan yang sangat sederhana, kami akan secara signifikan meningkatkan overhead dan keterlambatan dalam memproses permintaan pengguna.
  3. Selain itu, pengoperasian sistem semacam itu sangat rumit, karena alih-alih satu proses tunggal kita perlu memiliki proses di Jawa dan beberapa proses di Node.js. Dengan demikian, semua operasi menjadi jauh lebih rumit, misalnya: penyebaran, pengumpulan indikator operasional, analisis log, pemantauan kesalahan, dll.

Cara kedua untuk menggunakan Node.js adalah meletakkannya di depan server web di Jawa dan menggunakannya untuk HTML pasca-pemrosesan. Dengan kata lain, ini adalah proksi yang mem-parsing HTML, menemukan komponen dalam JS, menggambarnya, dan mengembalikan HTML yang sudah selesai kepada pengguna. Pilihan yang menarik, tampaknya bersifat universal dan cukup berfungsi. Kerugian dari pendekatan ini adalah bahwa itu memerlukan perubahan menyeluruh dalam seluruh infrastruktur, secara signifikan meningkatkan biaya overhead dan membawa risiko serius - setiap permintaan harus melalui Node.js, yaitu, kita akan mulai sepenuhnya bergantung padanya. Sepertinya solusi yang terlalu mahal untuk menyelesaikan masalah kita.





Ternyata Node.js tidak dapat digunakan karena alasan berikut:

  • Serialisasi / deserialisasi adalah beban kerja dan keterlambatan tambahan
  • Node.js adalah komponen lain dalam sistem terdistribusi besar Odnoklassniki

Kami sudah memiliki banyak spesialis yang tahu cara "memasak" Jawa, dan sekarang kami harus menyewa staf yang akan mengoperasikan Node.js dan membuat infrastruktur lain selain yang sudah ada.

JavaScript di JVM


Tetapi bagaimana jika Anda mencoba menjalankan JavaScript di dalam JVM? Ternyata kode Java dan JavaScript akan dieksekusi dalam satu proses dan berinteraksi dengan overhead minimum.

Ini dengan lancar akan menggantikan potongan Java dengan JavaScript di dalam WEB saat ini.
Komponen JS akan menerima data dari Java dan menghasilkan HTML. Mereka akan dapat bekerja secara isomorfis pada klien dan server.

Tetapi bagaimana menjalankan JS di JVM?
Anda dapat menggunakan V8 dengan mengikuti contoh Cloudflare . Tapi ini adalah kode biner pihak ketiga ke Jawa. Oleh karena itu, di JVM tidak akan mungkin untuk menangkap kesalahan di dalam V8. Setiap crash V8 akan menghancurkan seluruh proses. Akibatnya, penggunaan V8 akan meningkatkan risiko operasional, dan ini tidak boleh diizinkan.

Ada beberapa runtime JS untuk JVM: dua badak, Nashorn dan Badak (satu dari Oracle, yang lain dari Mozilla) dan GraalVM segar.



Keuntungan JS Runtimes untuk JVM:

  • Semuanya berfungsi di JVM, dan kami memiliki banyak keahlian dalam hal ini.
  • Interaksi Java dan JavaScript gratis
  • Aman runtime
  • Kompiler Java untuk GraalVM

Selanjutnya cukup untuk membandingkan runtime ini dalam kecepatan. Ternyata GraalVM berada di depan semua orang dengan selisih yang lebar:



Apa itu GraalVM?


GraalVM adalah runtime kinerja tinggi yang mendukung program dalam berbagai bahasa. Ini memiliki kerangka kerja untuk menulis kompiler bahasa untuk JVM. Berkat ini, pelaksanaan program di Jawa, Kotlin, JS, Python dan bahasa lain dalam JVM yang sama didukung.

Anda dapat mempelajari lebih lanjut tentang fitur GraalVM dari laporan oleh Oleg Shelaev , yang bekerja di Oracle Labs, tempat GraalVM sedang dikembangkan. Disarankan untuk menonton back-end dan front-end.

GraalVM memungkinkan kita untuk menjalankan JS untuk merender UI di server. Sebagai perpustakaan, kami menggunakan Bereaksi .

Keuntungan dari bundel tersebut:

  • Tidak ada bahasa baru yang ditambahkan: masih Java dan JavaScript
  • Komunitas besar: semua orang tahu Bereaksi
  • Ambang entri rendah
  • Mudah mencari kolega di tim
  • Operasi tidak rumit

Menjalankan Bereaksi di GraalVM


Di dalam GraalVM, Anda dapat membuat Konteks - wadah terisolasi di mana program akan berjalan dalam bahasa tamu. Dalam kasus kami, bahasa tamu adalah JS:

Context context = Context.create("js"); //  global   Value js = context.getBindings("js"); 

Untuk berinteraksi dengan konteksnya, objek globalnya digunakan:

 //    global js.putMember("serverProxy", serverProxy); //    global Value app = js.getMember("app"); 

Anda dapat memuat kode modul ke dalam konteks:

 //     Value load = js.getMember("load"); //     load.execute(pathToModule); 

Atau, "zap-eval-it" adalah kode apa pun di sana:

 context.eval("js", someCode); 



Rendering server JS: konsep


Buat konteks JavaScript di JVM dan muat kode modul aplikasi Bereaksi ke dalamnya. Kami melempar dari Jawa ke JS fungsi dan metode yang diperlukan. Kemudian dari konteks ini kita mengekstrak tautan ke fungsi JS render () dari modul ini, sehingga nanti kita bisa memanggilnya dari Java.



Ketika pengguna meminta halaman, mesin templat server dimulai, ia memanggil fungsi render () dari komponen yang diperlukan dengan data yang diperlukan, menerima kode HTML dari mereka dan memberikannya bersama dengan HTML dari seluruh halaman kepada pengguna.



JS Server Rendering: Implementasi


Di mesin templat server Odnoklassniki, tata letak ditulis dalam bentuk markup HTML. Untuk membedakan aplikasi JS dari markup biasa, kami menggunakan tag khusus.
Ketika mesin template menemukan tag kustom, tugas dibuat untuk membuat modul yang sesuai. Itu dikirim ke kumpulan utas, yang masing-masing memiliki konteks JS sendiri, dieksekusi pada utas gratis, merender komponen di dalamnya, dan memberikannya kepada klien.



Mengapa saya perlu kumpulan konteks


Komponen diberikan secara bersamaan dalam satu utas. Saat ini, konteks rendering JS sedang sibuk. Oleh karena itu, dengan membuat beberapa konteks independen, Anda dapat memparalelkan rendering komponen menggunakan kemampuan multithreading Java.



Fungsi akuisisi data Java diteruskan dengan merujuk ke setiap konteks. Hasilnya adalah JavaScript multi-utas yang keren di dalam satu proses.

Bagaimana implementasi bagian klien dari frontend baru dibangun pada konsep ini, kami akan menjelaskan dalam artikel berikutnya.

Untuk dilanjutkan

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


All Articles