Apa cara terbaik untuk meningkatkan konkurensi dalam layanan Node.js yang digunakan dalam produksi? Ini adalah pertanyaan yang perlu dijawab oleh tim saya beberapa bulan yang lalu.
Kami telah meluncurkan 4000 kontainer Node (atau "pekerja"), yang memastikan pengoperasian layanan integrasi kami dengan bank. Layanan ini awalnya dirancang sehingga setiap pekerja dirancang untuk memproses hanya satu permintaan pada suatu waktu. Ini mengurangi dampak pada sistem operasi yang secara tak terduga dapat
memblokir siklus peristiwa dan memungkinkan kami untuk mengabaikan perbedaan dalam penggunaan sumber daya oleh berbagai operasi serupa. Tetapi, karena kapasitas kami terbatas pada eksekusi secara serentak hanya 4.000 permintaan, sistem tidak dapat diskalakan secara memadai. Kecepatan respons terhadap sebagian besar permintaan tidak tergantung pada kapasitas peralatan, tetapi pada kemampuan jaringan. Oleh karena itu, kami dapat meningkatkan sistem dan mengurangi biaya dukungannya jika kami dapat menemukan cara untuk memproses permintaan secara andal secara paralel.

Setelah mempelajari masalah ini, kami tidak dapat menemukan panduan yang baik yang akan membahas transisi dari "kurangnya paralelisme" di Node.js ke "tingkat paralelisme yang tinggi". Sebagai hasilnya, kami mengembangkan strategi migrasi kami sendiri, yang didasarkan pada perencanaan yang cermat, alat yang baik, alat pemantauan, dan dosis debugging yang sehat. Sebagai hasilnya, kami berhasil meningkatkan tingkat paralelisme sistem kami sebanyak 30 kali. Ini setara dengan mengurangi biaya pemeliharaan sistem sekitar 300 ribu dolar per tahun.
Materi ini didedikasikan untuk kisah tentang bagaimana kami meningkatkan produktivitas dan efektivitas pekerja Node.js kami, dan tentang apa yang kami pelajari dengan menggunakan cara ini.
Mengapa kami memutuskan untuk berinvestasi dalam paralelisme?
Tampaknya mengejutkan bahwa kita telah tumbuh ke dimensi seperti itu tanpa menggunakan paralelisme. Bagaimana itu terjadi? Hanya 10% operasi pemrosesan data yang dilakukan oleh alat-alat Kotak-kotak dimulai oleh pengguna yang duduk di depan komputer dan telah menghubungkan akun mereka ke aplikasi. Yang lainnya adalah transaksi untuk memperbarui transaksi secara berkala yang dilakukan tanpa kehadiran pengguna. Logika ditambahkan ke sistem penyeimbangan beban yang kami gunakan untuk memastikan bahwa permintaan yang dibuat oleh pengguna diutamakan daripada permintaan pembaruan transaksi. Ini memungkinkan kami untuk menangani ledakan aktivitas operasi akses API dalam 1000% atau bahkan lebih. Ini dilakukan melalui transaksi yang bertujuan memperbarui data.
Meskipun skema kompromi ini telah bekerja sejak lama, adalah mungkin untuk melihat beberapa momen yang tidak menyenangkan di dalamnya. Kami tahu bahwa mereka, pada akhirnya, dapat mempengaruhi keandalan layanan.
- Puncak permintaan API yang berasal dari klien semakin tinggi. Kami khawatir bahwa lonjakan besar dalam aktivitas dapat menguras kemampuan pemrosesan permintaan kami.
- Peningkatan keterlambatan yang tiba-tiba dalam memenuhi permintaan ke bank juga menyebabkan penurunan kapasitas pekerja. Karena kenyataan bahwa bank menggunakan berbagai solusi infrastruktur, kami menetapkan batas waktu yang sangat konservatif untuk permintaan keluar. Akibatnya, diperlukan beberapa menit untuk menyelesaikan operasi pemuatan data tertentu. Jika kebetulan keterlambatan dalam melakukan banyak permintaan ke bank tiba-tiba akan meningkat pesat, ternyata banyak pekerja yang hanya akan terjebak menunggu jawaban.
- Penempatan di ECS menjadi terlalu lambat, dan meskipun kami telah meningkatkan kecepatan penerapan sistem, kami tidak ingin terus meningkatkan ukuran cluster.
Kami memutuskan bahwa cara terbaik untuk mengatasi hambatan aplikasi dan meningkatkan keandalan sistem adalah dengan meningkatkan tingkat paralelisme dalam memproses permintaan. Selain itu, kami berharap bahwa, sebagai efek samping, ini akan memungkinkan kami untuk mengurangi biaya infrastruktur dan membantu menerapkan alat yang lebih baik untuk memantau sistem. Baik itu dan yang lain di masa depan akan berbuah.
Bagaimana kami memperkenalkan pembaruan, menjaga keandalan
โ Alat dan Pemantauan
Kami memiliki penyeimbang beban kami sendiri, yang mengalihkan permintaan ke pekerja Node.js. Setiap pekerja menjalankan server gRPC yang digunakan untuk memproses permintaan. Pekerja menggunakan Redis untuk memberi tahu penyeimbang beban bahwa ia tersedia. Ini berarti bahwa menambahkan paralelisme ke sistem diturunkan hanya dengan mengubah beberapa baris kode. Yaitu, pekerja itu, bukannya menjadi tidak dapat diakses setelah permintaan diajukan kepadanya, harus memberi tahu bahwa ia tersedia sampai ia ditemukan sibuk memproses N permintaan yang datang kepadanya (masing-masing dari mereka). diwakili oleh objek Janji sendiri).
Benar, pada kenyataannya, tidak semuanya begitu sederhana. Saat menggunakan pembaruan sistem, kami selalu menganggapnya sebagai tujuan utama kami untuk mempertahankan keandalannya. Oleh karena itu, kami tidak bisa hanya mengambil dan, dipandu oleh sesuatu seperti prinsip YOLO, menempatkan sistem dalam mode pemrosesan kueri paralel. Kami berharap bahwa peningkatan sistem seperti itu akan sangat berisiko. Faktanya adalah bahwa ini akan memiliki efek yang tidak terduga pada penggunaan prosesor, memori, dan keterlambatan dalam melakukan tugas. Karena
mesin V8 yang digunakan di Node.js menangani tugas dalam loop acara, perhatian utama kami adalah bahwa mungkin ternyata kami melakukan terlalu banyak pekerjaan dalam loop acara dan dengan demikian mengurangi throughput sistem.
Untuk mengurangi risiko ini, kami, bahkan sebelum pekerja paralel pertama mulai berproduksi, memastikan ketersediaan alat dan alat pemantauan berikut dalam sistem:
- Tumpukan ELK yang sudah digunakan oleh kami memberi kami informasi login yang cukup, yang dapat berguna untuk dengan cepat mengetahui apa yang terjadi dalam sistem.
- Kami telah menambahkan beberapa metrik Prometheus ke sistem. Termasuk yang berikut ini:
- Ukuran tumpukan V8 diperoleh menggunakan
process.memoryUsage()
. - Informasi tentang operasi pengumpulan sampah menggunakan paket gc-stats .
- Data waktu yang dibutuhkan untuk menyelesaikan tugas, dikelompokkan berdasarkan jenis operasi yang terkait dengan integrasi dengan bank, dan menurut tingkat konkurensi. Kami membutuhkan ini untuk mengukur andal bagaimana konkurensi mempengaruhi throughput sistem.
- Kami menciptakan panel kontrol Grafana , yang dirancang untuk mempelajari tingkat dampak konkurensi pada sistem.
- Bagi kami, kemampuan untuk mengubah perilaku aplikasi tanpa perlu memindahkan layanan sangat penting. Oleh karena itu, kami membuat satu set bendera LaunchDarkly yang dirancang untuk mengontrol berbagai parameter. Dengan pendekatan ini, pemilihan parameter pekerja, dihitung sehingga mereka akan mencapai tingkat paralelisme maksimum, memungkinkan kami untuk dengan cepat melakukan percobaan dan menemukan parameter terbaik, menghabiskan beberapa menit untuk ini.
- Untuk mengetahui bagaimana berbagai bagian aplikasi memuat prosesor, kami telah membangun alat pengumpulan data layanan produksi, berdasarkan diagram nyala api yang dibangun.
- Kami menggunakan paket 0x karena alat Node.js mudah diintegrasikan ke dalam layanan kami, dan karena visualisasi data HTML akhir mendukung pencarian dan memberi kami tingkat detail yang baik.
- Kami menambahkan mode profil ke sistem ketika pekerja mulai dengan paket 0x dihidupkan dan, ketika keluar, menuliskan data akhir dalam S3. Kemudian kita bisa mengunduh log yang kita butuhkan dari S3 dan melihatnya secara lokal menggunakan perintah dari form
0x --visualize-only ./flamegraph
. - Kami, dalam periode waktu tertentu, mulai membuat profil hanya untuk satu pekerja. Pembuatan profil meningkatkan konsumsi sumber daya dan mengurangi produktivitas, jadi kami ingin membatasi efek negatif ini untuk satu pekerja.
โ Mulai Penempatan
Setelah menyelesaikan persiapan awal, kami membuat cluster ECS baru untuk "pekerja paralel". Ini adalah para pekerja yang menggunakan bendera LaunchDarkly untuk secara dinamis mengatur tingkat paralelisme maksimum mereka.
Rencana penerapan sistem kami mencakup pengalihan bertahap pertumbuhan volume lalu lintas dari cluster lama ke yang baru. Selama ini, kami akan memonitor kinerja cluster baru. Di setiap tingkat beban, kami berencana untuk meningkatkan tingkat paralelisme setiap pekerja, membawanya ke nilai maksimum di mana tidak ada peningkatan dalam durasi tugas atau penurunan indikator lainnya. Jika kami dalam masalah, kami dapat, dalam beberapa detik, secara dinamis mengarahkan lalu lintas ke cluster lama.
Seperti yang diharapkan, kami mengalami beberapa masalah rumit. Kami perlu menyelidiki dan menghilangkannya untuk memastikan operasi yang benar dari sistem yang diperbarui. Di sinilah kesenangan dimulai.
Perluas, Jelajahi, Ulangi
โMeningkatkan ukuran tumpukan maksimum Node.js
Ketika kami mulai menerapkan sistem baru, kami mulai menerima pemberitahuan penyelesaian tugas dengan kode keluar yang tidak nol. Apa yang bisa saya katakan - awal yang menjanjikan. Kemudian kami mengubur di Kibana dan menemukan log yang diperlukan:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - Javascript heap out of memory 1: node::Abort() 2: node::FatalException(v8::Isolate*, v8::Local, v8::Local) 3: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) 4: v8::internal::Factory::NewFixedArray(int, v8::internal::PretenureFlag)
Itu mengingatkan pada efek kebocoran memori yang telah kami temui ketika proses dihentikan tiba-tiba, memberikan pesan kesalahan yang sama. Ini tampaknya cukup diharapkan: peningkatan tingkat paralelisme menyebabkan peningkatan tingkat penggunaan memori.
Kami menyarankan bahwa meningkatkan ukuran maksimum tumpukan Node.js, yang diatur ke 1,7 GB secara default, dapat membantu menyelesaikan masalah ini. Kemudian kami mulai menjalankan Node.js, mengatur ukuran heap maksimum menjadi 6 GB (menggunakan flag baris perintah
--max-old-space-size=6144
). Ini adalah nilai terbesar yang cocok untuk instance EC2 kami. Kami senang, langkah seperti itu memungkinkan kami mengatasi kesalahan di atas yang terjadi dalam produksi.
โ Memory Bottleneck Identification
Setelah kami memecahkan masalah dengan alokasi memori, kami mulai menemukan throughput tugas yang buruk pada pekerja paralel. Pada saat yang sama, salah satu grafik di panel kontrol segera menarik perhatian kami. Ini adalah laporan tentang bagaimana proses pekerja paralel menggunakan banyak.
Penumpukan penggunaanBeberapa kurva grafik ini terus naik - sampai mereka berubah, pada tingkat ukuran tumpukan maksimum, menjadi garis hampir horizontal. Kami benar-benar tidak menyukainya.
Kami menggunakan metrik sistem di Prometheus untuk menghilangkan kebocoran deskriptor file atau soket jaringan dari penyebab perilaku sistem tersebut. Asumsi kami yang paling tepat adalah bahwa pengumpulan sampah tidak cukup sering dilakukan untuk benda-benda tua. Ini dapat mengarah pada fakta bahwa ketika tugas-tugas diproses, pekerja akan mengumpulkan semakin banyak memori yang dialokasikan untuk objek yang sudah tidak perlu. Kami berasumsi bahwa operasi sistem, di mana throughputnya menurun, terlihat seperti ini:
- Pekerja menerima tugas baru dan melakukan tindakan tertentu.
- Dalam proses menjalankan tugas, memori dialokasikan pada heap untuk objek.
- Karena fakta bahwa operasi tertentu yang mereka kerjakan pada prinsip "dilakukan dan dilupakan" (maka belum jelas yang mana) tidak lengkap, referensi ke objek disimpan bahkan setelah tugas selesai.
- Pengumpulan sampah melambat karena fakta bahwa V8 harus memindai semakin banyak objek di tumpukan.
- Karena V8 mengimplementasikan sistem pengumpulan sampah yang bekerja sesuai dengan skema stop-the-world (menghentikan program selama pengumpulan sampah), tugas-tugas baru pasti akan menerima lebih sedikit waktu prosesor, yang mengurangi throughput pekerja.
Kami mulai mencari dalam kode kami untuk operasi yang dilakukan berdasarkan prinsip "selesai dan dilupakan". Mereka juga disebut "janji mengambang" ("janji mengambang"). Itu sederhana - itu cukup untuk menemukan garis-garis di mana aturan
tanpa-mengambang-janji dinonaktifkan. Salah satu metode menarik perhatian kami. Dia membuat panggilan untuk
compressAndUploadDebuggingPayload
tanpa menunggu hasil. Tampaknya panggilan seperti itu dapat dengan mudah berlanjut untuk waktu yang lama bahkan setelah pemrosesan tugas selesai.
const postTaskDebugging = async (data: TypedData) => { const payload = await generateDebuggingPayload(data);
Kami ingin menguji hipotesis bahwa janji mengambang seperti itu adalah sumber utama masalah. Jika Anda tidak memenuhi tantangan ini, yang tidak memengaruhi operasi sistem yang benar, dapatkah kami meningkatkan kecepatan tugas? Inilah yang tampak seperti tumpukan informasi penggunaan setelah kami menyingkirkan
postTaskDebugging
panggilan
postTaskDebugging
.
Menggunakan heap setelah menonaktifkan postTaskDebuggingTernyata! Sekarang tingkat pemanfaatan tumpukan pekerja paralel tetap stabil selama periode waktu yang lama.
Ada perasaan bahwa dalam sistem, saat tugas selesai, "hutang" panggilan
compressAndUploadDebuggingPayload
secara bertahap diakumulasikan. Jika pekerja menerima tugas lebih cepat daripada dia mampu "melunasi" ini "hutang," maka objek di mana memori dialokasikan tidak dikenai operasi pengumpulan sampah. Hal ini menyebabkan mengisi tumpukan ke bagian paling atas, yang kami pertimbangkan di atas, menganalisis grafik sebelumnya.
Kami mulai bertanya-tanya apa yang membuat janji-janji mengambang ini begitu lambat. Kami tidak ingin menghapus sepenuhnya
compressAndUploadDebuggingPayload
dari kode, karena panggilan ini sangat penting sehingga teknisi kami dapat men-debug tugas-tugas produksi pada mesin lokal mereka. Dari sudut pandang teknis, kita bisa menyelesaikan masalah dengan menunggu hasil panggilan ini dan setelah itu menyelesaikan tugas, dengan demikian menyingkirkan janji mengambang. Tetapi ini akan sangat meningkatkan waktu pelaksanaan setiap tugas yang kami proses.
Setelah memutuskan bahwa kami akan menggunakan solusi untuk masalah hanya sebagai upaya terakhir, kami mulai berpikir untuk mengoptimalkan kode. Bagaimana cara mempercepat operasi ini?
โFix bottleneck S3
Logika dari
compressAndUploadDebuggingPayload
mudah diketahui. Di sini kita kompres data debug, dan ini bisa sangat besar, karena itu termasuk lalu lintas jaringan. Lalu kami mengunggah data yang dikompresi ke S3.
export const compressAndUploadDebuggingPayload = async ( logger: Logger, data: any, ) => { const compressionStart = Date.now(); const base64CompressedData = await streamToString( bfj.streamify(data) .pipe(zlib.createDeflate()) .pipe(new b64.Encoder()), ); logger.trace('finished compressing data', { compression_time_ms: Date.now() - compressionStart, ); const uploadStart = Date.now(); s3Client.upload({ Body: base64CompressedData, Bucket: bucket, Key: key, }); logger.trace('finished uploading data', { upload_time_ms: Date.now() - uploadStart, ); }
Dari log Kibana, jelas bahwa mengunduh data ke S3, meskipun volumenya kecil, membutuhkan banyak waktu. Kami awalnya tidak berpikir bahwa soket dapat menjadi hambatan dalam sistem, karena agen HTTPS Node.js standar menetapkan parameter
maxSockets ke
Infinity
. Namun, pada akhirnya, kami membaca dokumentasi AWS di Node.js dan menemukan sesuatu yang mengejutkan bagi kami: klien S3 mengurangi nilai parameter
maxSockets
menjadi
50
. Tidak perlu dikatakan, perilaku ini tidak bisa disebut intuitif.
Karena kami membawa pekerja ke keadaan di mana di dalamnya, dalam mode kompetitif, lebih dari 50 tugas dilakukan, langkah pengunduhan menjadi hambatan: menyediakan waktu tunggu soket untuk dilepas untuk mengunggah data ke S3. Kami meningkatkan waktu pemuatan data dengan melakukan perubahan berikut pada kode inisialisasi klien S3:
const s3Client = new AWS.S3({ httpOptions: { agent: new https.Agent({
โ Mempercepat serialisasi JSON
Peningkatan kode S3 telah memperlambat pertumbuhan ukuran tumpukan, tetapi tidak menyebabkan solusi lengkap untuk masalah ini. Ada gangguan lain yang jelas: menurut metrik kami, langkah kompresi data dalam kode di atas pernah berlangsung 4 menit. Itu jauh lebih lama dari waktu penyelesaian tugas biasa, yaitu 4 detik. Tidak mempercayai mata kami, tidak mengerti bagaimana ini bisa memakan waktu 4 menit, kami memutuskan untuk menggunakan tolok ukur lokal dan mengoptimalkan blok kode lambat.
Kompresi data terdiri dari tiga tahap (di sini, untuk membatasi penggunaan memori, Node.js
stream digunakan). Yaitu, pada tahap pertama, data string JSON dihasilkan, pada tahap kedua, data dikompres menggunakan zlib, pada tahap ketiga, data tersebut dikonversi ke pengkodean base64. Kami menduga bahwa sumber masalahnya bisa berupa perpustakaan pihak ketiga yang kami gunakan untuk menghasilkan string JSON -
bfj . Kami menulis skrip yang memeriksa kinerja berbagai pustaka untuk menghasilkan data string JSON menggunakan stream (kode yang sesuai dapat ditemukan di
sini ). Ternyata paket JSON Besar Ramah yang kami gunakan sama sekali tidak ramah. Lihat saja hasil dari beberapa pengukuran yang diperoleh selama percobaan:
benchBFJ*100: 67652.616ms benchJSONStream*100: 14094.825ms
Hasil luar biasa. Bahkan dalam tes sederhana, paket bfj ternyata 5 kali lebih lambat daripada paket lainnya, JSONStream. Mengetahui hal ini, kami dengan cepat mengubah bfj menjadi
JSONStream dan segera melihat peningkatan kinerja yang signifikan.
โ Mengurangi waktu yang dibutuhkan untuk pengumpulan sampah
Setelah kami memecahkan masalah dengan memori, kami mulai memperhatikan perbedaan waktu yang diperlukan untuk memproses tugas dengan jenis yang sama antara pekerja reguler dan paralel. Perbandingan ini benar-benar sah, menurut hasilnya kami dapat menilai efektivitas sistem baru. Jadi, jika rasio antara pekerja reguler dan paralel adalah sekitar 1, ini akan memberi kami keyakinan bahwa kami dapat mengarahkan lalu lintas ke pekerja ini dengan aman. Tetapi selama sistem pertama diluncurkan, grafik yang sesuai di panel kontrol Grafana tampak seperti yang ditunjukkan di bawah ini.
Rasio waktu pelaksanaan tugas oleh pekerja konvensional dan paralelHarap dicatat bahwa kadang-kadang indikatornya berada di wilayah 8: 1, dan ini terlepas dari kenyataan bahwa tingkat rata-rata paralelisasi tugas relatif rendah dan berada di wilayah 30. Kami menyadari bahwa tugas yang kami selesaikan mengenai interaksi dengan bank tidak membuat beban berat pada prosesor. Kami juga tahu bahwa wadah "paralel" kami tidak dibatasi dengan cara apa pun. Tidak tahu ke mana harus mencari penyebab masalahnya, kami pergi membaca materi tentang mengoptimalkan proyek Node.js. Terlepas dari sejumlah kecil artikel semacam itu, kami menemukan materi
ini , yang membahas pencapaian 600 ribu koneksi soket web kompetitif di Node.js.
Secara khusus, perhatian kami tertuju pada penggunaan
--nouse-idle-notification
. Bisakah proses Node.js kami menghabiskan begitu banyak waktu mengumpulkan sampah? Ngomong-ngomong, paket gc-stats memberi kami kesempatan untuk melihat waktu rata-rata yang dihabiskan untuk pengumpulan sampah.
Analisis waktu yang dihabiskan untuk pengumpulan sampahAda perasaan bahwa proses kami menghabiskan sekitar 30% dari waktu mengumpulkan sampah menggunakan algoritma Pemulung. Di sini kita tidak akan menjelaskan detail teknis mengenai berbagai jenis pengumpulan sampah di Node.js. Jika Anda tertarik dengan topik ini - lihat materi
ini . Inti dari algoritma Scavenge adalah bahwa pengumpulan sampah sering mulai membersihkan memori yang ditempati oleh benda-benda kecil di tumpukan Node.js yang disebut "ruang baru".
Jadi, ternyata di Node.js kami proses pengumpulan sampah terlalu sering dimulai. Bisakah saya menonaktifkan pengumpulan sampah V8 dan menjalankannya sendiri? Apakah ada cara untuk
mengurangi frekuensi panggilan pengumpulan sampah? Ternyata yang pertama di atas tidak dapat dilakukan, tetapi yang terakhir - itu mungkin! Kita cukup meningkatkan ukuran area "ruang baru" dengan meningkatkan batas area "ruang semi" di Node.js menggunakan flag baris perintah
--max-semi-space-size=1024
. Ini memungkinkan Anda untuk melakukan lebih banyak operasi alokasi memori untuk objek yang berumur pendek hingga V8 memulai pengumpulan sampah. Akibatnya, frekuensi peluncuran operasi tersebut berkurang.
Hasil optimasi pengumpulan sampahKemenangan lain! Peningkatan area "ruang baru" menyebabkan pengurangan yang signifikan dalam jumlah waktu yang dihabiskan untuk pengumpulan sampah menggunakan algoritma Scavenge - dari 30% menjadi 2%.
โOptimasi penggunaan prosesor
Setelah semua pekerjaan ini selesai, hasilnya cocok untuk kami. Tugas dilakukan pada pekerja paralel, dengan paralelisasi kerja 20 kali, berfungsi hampir secepat yang dilakukan secara terpisah pada pekerja terpisah. Tampaknya bagi kami bahwa kami telah mengatasi semua kemacetan, tetapi kami masih tidak tahu persis operasi mana yang memperlambat sistem dalam produksi. Karena tidak ada lagi tempat dalam sistem yang jelas membutuhkan optimasi, kami memutuskan untuk mempelajari bagaimana pekerja menggunakan sumber daya prosesor.
Berdasarkan data yang dikumpulkan pada salah satu pekerja paralel kami, jadwal berapi-api dibuat. Kami memiliki visualisasi yang rapi, yang dapat digunakan pada mesin lokal. Ya, inilah detail yang menarik: ukuran data ini adalah 60 MB. Inilah yang kami lihat dengan mencari kata
logger
di grafik 0x yang berapi-api.
Analisis Data dengan 0x ToolsArea biru-hijau yang disorot di kolom menunjukkan bahwa setidaknya 15% dari waktu prosesor dihabiskan untuk menghasilkan log pekerja. Hasilnya, kami dapat mengurangi waktu ini hingga 75%. Benar, kisah tentang bagaimana kami melakukan ini menarik ke artikel terpisah. (Petunjuk: kami menggunakan ekspresi reguler dan melakukan banyak pekerjaan dengan properti).
Setelah optimasi ini, kami dapat secara bersamaan memproses hingga 30 tugas dalam satu pekerja tanpa merusak kinerja sistem.
Ringkasan
Beralih ke pekerja paralel telah mengurangi biaya tahunan untuk EC2 sekitar 300 ribu dolar dan sangat menyederhanakan arsitektur sistem. Sekarang kami menggunakan dalam produksi sekitar 30 kali lebih sedikit kontainer dari sebelumnya. Sistem kami lebih tahan terhadap keterlambatan dalam memproses permintaan keluar dan memuncak permintaan API yang berasal dari pengguna.
Saat memparalelkan layanan integrasi kami dengan bank, kami belajar banyak hal baru:
- Jangan pernah meremehkan pentingnya memiliki metrik sistem tingkat rendah. Kemampuan untuk memantau data yang berkaitan dengan pengumpulan sampah dan penggunaan memori telah memberikan kami bantuan luar biasa dalam menyebarkan sistem dan menyelesaikannya.
- Grafik flaming adalah alat yang hebat. Sekarang kita telah belajar bagaimana menggunakannya, kita dapat dengan mudah mengidentifikasi kemacetan baru dalam sistem dengan bantuan mereka.
- Memahami mekanisme runtime Node.js memungkinkan kami untuk menulis kode yang lebih baik. Sebagai contoh, mengetahui bagaimana V8 mengalokasikan memori untuk objek, dan bagaimana pengumpulan sampah bekerja, kami melihat titik dalam menggunakan teknik reuse objek seluas mungkin. Terkadang, untuk lebih memahami semua ini, Anda harus bekerja secara langsung dengan V8 atau bereksperimen dengan flag baris perintah Node.js.
- , .
maxSocket
, Node.js, , , , AWS Node.js . , , , .
Pembaca yang budiman! Node.js-?
