Bagaimana kami melihat rendering server dan apa yang terjadi

Halo semuanya! Sepanjang tahun, kami beralih ke Bereaksi dan berpikir tentang cara memastikan bahwa pengguna kami tidak menunggu templat klien, tetapi melihat laman itu secepat mungkin. Untuk tujuan ini, kami memutuskan untuk melakukan rendering sisi server (SSR - Server Side Rendering) dan mengoptimalkan SEO, karena tidak semua mesin pencari dapat mengeksekusi JS, dan mereka yang dapat menghabiskan waktu untuk eksekusi, dan waktu perayapan setiap situs terbatas.



Biarkan saya mengingatkan Anda bahwa rendering server adalah eksekusi kode JavaScript di sisi server untuk memberikan HTML siap kepada klien. Ini memengaruhi kinerja yang dirasakan pengguna, terutama pada mesin yang lebih lambat dan pada Internet yang lambat. Tidak perlu menunggu sampai JS diunduh, diuraikan dan dieksekusi. Browser hanya dapat membuat HTML dengan segera, tanpa menunggu JSa, pengguna sudah dapat membaca konten.
Dengan demikian, fase menunggu pasif berkurang. Setelah rendering, browser hanya perlu melewati DOM yang sudah selesai, periksa apakah cocok dengan yang telah dirender
pada klien, dan tambahkan pendengar acara. Proses ini disebut hidrasi . Jika dalam proses hidrasi ada perbedaan antara konten dari server dan yang dihasilkan oleh browser, kita akan mendapatkan peringatan di konsol dan penyaji tambahan pada klien. Ini tidak boleh, perlu untuk memastikan bahwa hasil dari server dan rendering klien cocok. Jika mereka berbeda, maka ini harus diperlakukan sebagai bug, karena ini meniadakan keuntungan rendering server. Jika ada elemen yang berbeda, tambahkan suppressHydrationWarning={true} .


Selain itu, ada satu peringatan: tidak ada window di server. Kode yang mengaksesnya harus dijalankan dalam metode siklus hidup yang tidak dipanggil di sisi server. Yaitu, Anda tidak dapat menggunakan window di UNSAFE_componentWillMount () atau, dalam kasus kait, gunakan efek samping .


Bahkan, proses rendering sisi-server bermuara untuk mendapatkan initialState dari backend, menjalankannya melalui renderToString() , mengambil initialState yang selesai dan HTML pada output, dan mengirimkannya ke klien.


Di hh.ru, lonjakan dari klien JS hanya diizinkan di api gateway dengan python. Ini untuk keamanan dan penyeimbangan muatan. Python sudah pergi ke backend yang diperlukan untuk data, menyiapkannya dan memberikannya ke browser. Node.js hanya digunakan untuk rendering server. Oleh karena itu, setelah menyiapkan data, python memerlukan perjalanan tambahan ke node, menunggu hasilnya dan mengirimkan respons ke klien.


Pertama, Anda harus memilih server untuk bekerja dengan HTTP. Kami berhenti di koa . Menyukai sintaks modern dengan await . Modularitas adalah middleware ringan, yang, jika perlu, dipasang secara terpisah atau mudah ditulis secara independen. Server itu sendiri ringan dan cepat . Ya, dan ditulis oleh koa oleh tim pengembangan yang sama yang mereka tulis ekspres, pengalaman mereka menawan.


Setelah kami belajar cara meluncurkan layanan kami, kami menulis kode paling sederhana di KOA, yang mampu memberikan 200, dan mengunggahnya ke prod. Itu terlihat seperti ini:


 const Koa = require('koa'); const app = new Koa(); const SERVER_PORT = 9400; app.use(async (ctx) => { ctx.body = 'Hello World'; }); app.listen(SERVER_PORT); 

Di hh.ru, semua layanan tinggal di wadah buruh pelabuhan . Sebelum rilis pertama, Anda perlu menulis buku pedoman yang memungkinkan , dengan bantuan layanan yang diluncurkan di lingkungan produksi dan di tempat uji. Setiap pengembang dan penguji memiliki lingkungan pengujian sendiri, yang paling mirip dengan prod. Kami menghabiskan sebagian besar waktu dan energi kami menulis buku pedoman. Itu terjadi karena dua render front-end melakukan ini, dan ini adalah layanan pertama pada sebuah simpul di hh.ru. Kami harus mencari tahu cara mengalihkan layanan ke mode pengembangan, melakukannya bersamaan dengan layanan tempat rendering berlangsung. Kirim file ke sebuah wadah. Luncurkan server kosong sehingga wadah buruh pelabuhan dimulai tanpa menunggu bangunan. Bangun dan bangun kembali server bersama dengan layanan yang menggunakannya. Tentukan berapa banyak RAM yang kita butuhkan.


Dalam mode pengembangan, mereka menyediakan kemungkinan untuk membangun kembali otomatis dan me-restart layanan ketika mengubah file yang termasuk dalam pembuatan akhir. Node perlu direstart untuk memuat kode yang dapat dieksekusi. Webpack memonitor perubahan dan pembangunan . Webpack diperlukan untuk mengonversi ESM ke CommonJS yang umum. Untuk me-restart, mereka mengambil nodemon , yang menjaga file yang dikumpulkan.


Kemudian kami mempelajari server perutean. Untuk penyeimbangan yang tepat, Anda harus tahu contoh server mana yang hidup. Untuk memeriksanya, detak jantung operasional pergi ke /status setiap beberapa detik sekali dan mengharapkan menerima 200 sebagai respons. Jika server tidak merespons lebih dari berapa kali ditentukan dalam konfigurasi, itu dihapus dari balancing. Ini ternyata menjadi tugas sederhana, beberapa baris dan perutean siap:


 export default async function(ctx, next) { if (routeMap[ctx.request.path]) { routeMap[ctx.request.path](ctx); } else { ctx.throw(NOT_FOUND, getStatusText(NOT_FOUND)); } next(); } 

Dan kami menjawab 200 di url yang tepat:


 export default (ctx) => { ctx.status = 200; ctx.body = '200'; }; 

Setelah itu, kami membuat server primitif yang mengembalikan status dalam <script> dan HTML siap.


Itu perlu untuk mengontrol cara kerja server. Untuk melakukan ini, Anda perlu mempercepat logging dan pemantauan. Log tidak ditulis dalam JSON, tetapi untuk mendukung format log dari layanan kami yang lain, terutama Java. Log4js dipilih sesuai tolok ukur - cepat, mudah untuk mengkonfigurasi dan menulis dalam format yang kita butuhkan. Format log umum diperlukan untuk menyederhanakan dukungan pemantauan - tidak perlu menulis pelanggan tetap tambahan untuk mem-parsing log. Selain log, kami masih menulis kesalahan pada penjaga . Saya tidak akan memberikan kode penebang, itu sangat sederhana, pada dasarnya, ada pengaturan.


Maka itu perlu untuk menyediakan shutdown anggun - ketika server menjadi sakit, atau ketika rilis bergulir, server tidak menerima lagi koneksi masuk, tetapi melakukan semua permintaan tergantung padanya. Ada banyak solusi siap pakai untuk sebuah simpul. Mereka mengambil http-anggun-shutdown , yang harus dilakukan hanyalah membungkus panggilan gracefulShutdown(app.listen(SERVER_PORT))


Pada titik ini, kami mendapat solusi siap-produksi. Untuk memeriksa cara kerjanya, mereka mengaktifkan rendering server untuk 5% pengguna di satu halaman. Kami melihat metrik, menyadari bahwa mereka secara signifikan meningkatkan FMP untuk ponsel, untuk desktop nilainya tidak berubah. Mereka mulai menguji kinerja, menemukan bahwa satu server menampung ~ 20 RPS (fakta ini sangat menghibur bagi orang Jawa). Menemukan alasan mengapa demikian:


  • Salah satu masalah utama ternyata adalah mereka dibangun tanpa NODE_ENV = produksi (kami menetapkan ENV yang kami butuhkan untuk pembuatan server). Dalam hal ini, reaksi memberikan rakitan non-produksi, yang berjalan sekitar 30% lebih lambat.


  • Kami menaikkan versi node dari 8 menjadi 10, mendapat 20-25% kinerja lagi.


  • Apa yang kami tinggalkan untuk terakhir kali adalah meluncurkan sebuah node pada beberapa kernel. Kami curiga itu sangat sulit, tetapi di sini juga, semuanya ternyata sangat biasa-biasa saja. Node memiliki mekanisme bawaan - cluster . Ini memungkinkan Anda untuk menjalankan sejumlah proses independen yang diperlukan, termasuk proses master yang mencerai-beraikan tugas untuk mereka.



 if (cluster.isMaster) { cluster.on('exit', (worker, exitCode) => { if (exitCode !== SUCCESS) { cluster.fork(); } }); for (let i = 0; i < serverConfig.cpuCores; i++) { cluster.fork(); } } else { runApp(); } 

Dalam kode ini, proses master dimulai, proses dimulai sesuai dengan jumlah CPU yang dialokasikan untuk server. Jika salah satu proses anak macet - kode keluar bukan 0 (kami sendiri mematikan server), proses master me-restart itu.
Dan kinerja meningkat sekitar jumlah CPU yang dialokasikan untuk server.


Seperti yang saya tulis di atas, sebagian besar waktu dihabiskan untuk menulis buku pedoman asli - sekitar 3 minggu. Butuh sekitar 2 minggu untuk menulis seluruh SSR, dan selama sekitar satu bulan kami perlahan-lahan memikirkannya. Semua ini dilakukan oleh kekuatan 2 front, tanpa pengalaman perusahaan node js. Jangan takut untuk melakukan SSR, yang paling penting - jangan lupa untuk menentukan NODE_ENV=production , tidak ada yang rumit tentang hal itu. Pengguna dan SEO akan berterima kasih.

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


All Articles