Kemarin, seorang teman bertanya bagaimana cara menerapkan proyek ekspres. Jadi saya menyusun artikel ini, yang terutama membahas tentang cara menyebarkan program server yang dikembangkan berdasarkan nodejs untuk referensi teman-teman yang membutuhkan.
Artikel ini berisi beberapa bagian:
(proses) adalah komputer alokasi sistem operasi dan unit dasar tugas penjadwalan . Buka task manager dan Anda dapat melihat bahwa sebenarnya ada banyak program yang berjalan di latar belakang komputer, dan setiap program adalah sebuah proses.
Peramban modern pada dasarnya memiliki arsitektur multi-proses. Dengan mengambil contoh peramban Chrome, buka "Alat Lainnya" - "Pengelola Tugas" dan Anda dapat melihat informasi proses dari peramban saat ini Selain itu, ada proses jaringan, proses GPU, dll.
Arsitektur multi-proses memastikan pengoperasian aplikasi yang lebih stabil. Ambil contoh browser, jika semua program dijalankan dalam satu proses, jika terjadi kegagalan jaringan atau kesalahan rendering halaman, maka akan menyebabkan seluruh browser crash. Melalui arsitektur multi-proses, meskipun proses jaringan mogok, hal itu tidak akan memengaruhi tampilan halaman yang ada, dan yang terburuk, untuk sementara tidak dapat mengakses jaringan.
Thread adalah unit terkecil yang sistem operasi dapat melakukan penjadwalan komputasi . Ini termasuk dalam proses dan merupakan unit operasi sebenarnya dalam proses tersebut. Misalnya, suatu program seperti sebuah perusahaan dengan banyak departemen, yang merupakan proses; kerja sama setiap departemen memungkinkan perusahaan berjalan normal, dan benang merahnya adalah karyawan, orang-orang yang melakukan pekerjaan tertentu.
Kita semua tahu bahwa JavaScript adalah bahasa single-threaded. Desain ini karena pada awalnya, JS terutama digunakan untuk menulis skrip dan bertanggung jawab untuk mewujudkan efek interaktif halaman. Jika dirancang sebagai bahasa multi-thread, pertama, tidak diperlukan, dan kedua, beberapa thread secara bersama-sama mengoperasikan node DOM, lalu saran siapa yang harus didengarkan oleh browser? Tentunya dengan berkembangnya teknologi, JS kini juga mendukung multi-threading, namun hanya digunakan untuk menangani beberapa logika yang tidak terkait dengan operasi DOM.
Thread tunggal dan proses tunggal membawa masalah serius Setelah thread utama dari program node.js yang sedang berjalan terhenti, prosesnya juga akan terhenti, dan seluruh aplikasi juga akan terhenti. Selain itu, sebagian besar komputer modern memiliki CPU multi-inti, dengan empat inti dan delapan thread, serta delapan inti dan enam belas thread, yang merupakan perangkat yang sangat umum. Sebagai program proses tunggal, node.js menyia-nyiakan kinerja CPU multi-core.
Menanggapi situasi ini, kita memerlukan model multi-proses yang sesuai untuk mengubah program node.js proses tunggal menjadi arsitektur multi-proses.
Ada dua solusi umum untuk mengimplementasikan arsitektur multi-proses di Node.js, keduanya menggunakan modul asli, yaitu modul child_process
dan modul cluster
.
child_process
adalah modul bawaan node.js. Anda dapat menebak dari namanya bahwa modul ini bertanggung jawab untuk hal-hal yang berkaitan dengan proses anak.
Kami tidak akan menguraikan secara spesifik penggunaan modul ini, pada kenyataannya modul ini hanya memiliki sekitar enam atau tujuh metode yang masih sangat mudah untuk dipahami. Kami menggunakan salah satu metode fork
untuk mendemonstrasikan cara mengimplementasikan banyak proses dan komunikasi antara banyak proses.
Pertama-tama mari kita lihat struktur direktori dari kasus demonstrasi yang telah disiapkan:
Kami menggunakan modul http
untuk membuat server http. Ketika permintaan /sum
masuk, proses anak akan dibuat melalui modul child_process
dan proses anak akan diberitahu untuk melakukan logika perhitungan pada saat yang sama, proses induk juga harus mendengarkan pesan yang dikirim oleh proses anak:
/ /child_process.js const http = memerlukan('http') const { garpu } = memerlukan('proses_anak') const server = http.createServer((req, res) => { if (permintaan.url == '/jumlah') { // Metode fork menerima jalur modul, kemudian memulai proses anak, dan menjalankan modul dalam proses anak // childProcess mewakili proses anak yang dibuat let childProcess = fork('./sum.js') //Kirim pesan ke proses anak childProcess.send('Proses anak mulai menghitung') // Pantau pesan dari proses anak dalam proses induk childProcess.on('message', (data) => { res.end(data + '') }) //Dengarkan event penutupan proses anak childProcess.on('close', () => { // Jika proses anak keluar secara normal atau melaporkan kesalahan dan terhenti, proses tersebut akan menuju ke sini console.log('proses anak ditutup') Proses anak.membunuh() }) //Dengarkan kejadian kesalahan pada proses anak childProcess.on('error', () => { console.log('kesalahan proses anak') Proses anak.membunuh() }) } if (req.url == '/halo') { res.end('halo') } // Simulasikan proses induk untuk melaporkan kesalahan if (req.url == '/error') { melempar Kesalahan baru('Kesalahan proses induk') res.end('halo') } }) server.dengarkan(3000, () => { console.log('Server berjalan pada 3000') })
sum.js
digunakan untuk mensimulasikan tugas yang akan dilakukan oleh proses anak. Proses anak mendengarkan pesan yang dikirim oleh proses induk, memproses tugas perhitungan, dan kemudian mengirimkan hasilnya ke proses induk:
// sum.js fungsi getJumlah() { misalkan jumlah = 0 untuk (misalkan i = 0; i < 10000 * 1000 * 100; i++) { jumlah += 1 } jumlah pengembalian } // proses adalah objek global di node.js, mewakili proses saat ini. Ini dia proses anak. // Dengarkan pesan yang dikirim oleh proses utama process.on('message', (data) => { console.log('Pesan dari proses utama:', data) hasil const = getSum() //Kirim hasil perhitungan ke proses induk process.send(hasil) })
Buka terminal dan jalankan perintah node 1.child_process
:
Kunjungi peramban:
Selanjutnya, simulasikan situasi di mana proses anak melaporkan kesalahan:
// sum.js fungsi getJumlah() { // .... } // Setelah proses anak berjalan selama 5 detik, proses simulasi terhenti setTimeout(() => { melempar Kesalahan baru('laporan kesalahan') }, 1000*5) proses.pada('pesan', (data) => { // ... })
Kunjungi browser lagi dan amati konsol setelah 5 detik:
Proses anak telah mati, dan kemudian mengakses url lain: /hello
,
Terlihat bahwa proses induk masih dapat menangani permintaan dengan benar, hal ini menunjukkan bahwa kesalahan yang dilaporkan oleh proses anak tidak akan mempengaruhi pengoperasian proses induk .
Selanjutnya, kita akan menyimulasikan skenario di mana proses induk melaporkan kesalahan, mengomentari laporan kesalahan simulasi modul sum.js
, lalu memulai ulang layanan, dan mengakses /error
dengan browser:
Setelah mengetahui bahwa proses induk terhenti, seluruh program node.js secara otomatis keluar, dan layanan benar-benar runtuh, sehingga tidak ada ruang untuk pemulihan.
Terlihat bahwa tidak rumit untuk mengimplementasikan arsitektur multi-proses node.js melalui metode fork
child_process
. Komunikasi antar proses terutama melalui metode send
dan on
. Dari penamaan ini, kita juga dapat mengetahui bahwa lapisan paling bawah harus berupa model terbitkan-berlangganan.
Namun ada masalah yang serius. Meskipun proses anak tidak mempengaruhi proses induk, begitu proses induk membuat kesalahan dan terhenti, semua proses anak akan "dimatikan dalam satu pot". Oleh karena itu, solusi ini cocok untuk membagi beberapa operasi yang rumit dan memakan waktu ke dalam sub-proses terpisah . Lebih tepatnya penggunaan ini digunakan untuk menggantikan penerapan multi-threading, bukan multi-processing.
menggunakan modul child_process
untuk mengimplementasikan multi-proses, yang tampaknya tidak berguna. Oleh karena itu, secara umum disarankan untuk menggunakan modul cluster
untuk mengimplementasikan model multi-proses node.js.
cluster
artinya cluster. Saya yakin semua orang sudah familiar dengan istilah ini. Misalnya, dulu perusahaan hanya memiliki satu meja depan, dan terkadang terlalu sibuk untuk menerima pengunjung tepat waktu. Kini perusahaan telah mengalokasikan empat front desk. Meski tiga front desk sibuk, masih ada satu yang bisa menerima pengunjung baru. Clustering secara kasar berarti demikian. Untuk hal yang sama, wajar saja jika ditugaskan kepada orang yang berbeda untuk melakukannya, sehingga dapat dipastikan bahwa hal tersebut dapat dilakukan dengan sebaik-baiknya.
Penggunaan modul cluster
juga relatif sederhana. Jika proses saat ini adalah proses utama, buatlah jumlah sub-proses yang sesuai berdasarkan jumlah inti CPU, dan dengarkan kejadian exit
dari sub-proses tersebut. Jika ada sub-proses yang keluar, lakukan fork ulang pada sub-proses yang baru -proses. Jika ini bukan proses anak, bisnis sebenarnya akan diproses.
const http = memerlukan('http') const cluster = memerlukan('cluster') const cpus = memerlukan('os').cpus() if (cluster.isMaster) { // Saat program dimulai, pertama-tama ia masuk ke sini dan membuat beberapa sub-proses sesuai dengan jumlah inti CPU untuk (misalkan i = 0; i < cpus.length; i++) { //Buat cluster proses anak.fork() } // Ketika proses anak terhenti, modul cluster akan mengeluarkan event 'keluar'. Pada titik ini, proses dimulai kembali dengan memanggil fork lagi. cluster.pada('keluar', () => { cluster.garpu() }) } kalau tidak { // Metode fork dijalankan untuk membuat proses anak, dan modul akan dieksekusi lagi. Saat ini, logikanya akan muncul di sini const server = http.createServer((req, res) => {. konsol.log(proses.pid) res.end('ok') }) server.dengarkan(3000, () => { console.log('Server berjalan pada 3000', 'pid: ' + proses.pid) }) }
Mulai layanan:
Seperti yang Anda lihat, modul cluster
telah membuat banyak proses anak, dan tampaknya setiap proses anak menjalankan layanan web yang sama.
Perlu dicatat bahwa proses anak ini tidak mendengarkan port yang sama saat ini. Server yang dibuat dengan metode createServer masih bertanggung jawab untuk memantau port dan meneruskan permintaan ke setiap proses anak.
Mari kita tulis skrip permintaan untuk meminta layanan di atas dan lihat efeknya.
// permintaan.js const http = memerlukan('http') untuk (misalkan i = 0; i < 1000; i++) { http.mendapatkan('http://localhost:3000') }
Modul http tidak hanya dapat membuat server http, tetapi juga dapat digunakan untuk mengirim permintaan http. Axios mendukung lingkungan browser dan server. Di sisi server, modul http digunakan untuk mengirim permintaan http.
Gunakan perintah node
untuk mengeksekusi file dan lihat konsol asli:
ID proses dari sub-proses berbeda yang secara khusus menangani permintaan tersebut akan dicetak.
Ini adalah arsitektur multi-proses nodd.js yang diimplementasikan melalui modul cluster
.
Tentu saja, saat kami menerapkan proyek node.js, kami tidak akan menulis dan menggunakan modul cluster
begitu saja. Ada alat yang sangat berguna yang disebut PM2 , yang merupakan alat manajemen proses berdasarkan modul cluster. Penggunaan dasarnya akan diperkenalkan di bab-bab berikutnya.
Sejauh ini, kami telah menghabiskan sebagian artikel untuk memperkenalkan pengetahuan multi-proses di node.js. Sebenarnya, kami hanya ingin menjelaskan mengapa kami perlu menggunakan pm2 untuk mengelola aplikasi node.js. Karena terbatasnya ruang artikel ini dan kurangnya uraian yang akurat/detail, artikel ini hanya memberikan pengenalan singkat. Jika Anda baru pertama kali melihat konten ini, Anda mungkin belum memahaminya dengan baik, jadi jangan khawatir, nanti akan ada artikel yang lebih detail.
Artikel ini telah menyiapkan contoh program yang dikembangkan menggunakan ekspres, klik di sini untuk mengakses.
Ini terutama mengimplementasikan layanan antarmuka. Saat mengakses /api/users
, mockjs
digunakan untuk mensimulasikan 10 data pengguna dan mengembalikan daftar pengguna. Pada saat yang sama, pengatur waktu akan dimulai untuk mensimulasikan situasi kesalahan:
const express = require('express') const Mock = memerlukan('mockjs') aplikasi const = ekspres() app.get("/api/users", (permintaan, res) => { const Daftar Pengguna = Mock.mock({ 'Daftar Pengguna|10': [{ 'id|+1': 1, 'nama': '@cname', 'email': '@email' }] }) setWaktu habis(()=> { melempar Kesalahan baru ('Kegagalan server') }, 5000) res.status(200) res.json(Daftar Pengguna) }) aplikasi.dengarkan(3000, () => { console.log("Layanan dimulai: 3000") })
Uji secara lokal dan jalankan perintah di terminal:
node server.js
Buka browser dan akses antarmuka daftar pengguna:
Setelah lima detik, server akan hang:
Kita bisa mengatasi masalah ini nanti ketika kita menggunakan pm2 untuk mengelola aplikasi.
Biasanya setelah menyelesaikan proyek vue/react, kami akan mengemasnya terlebih dahulu lalu mempublikasikannya. Faktanya, proyek front-end perlu dikemas terutama karena lingkungan akhir program yang berjalan adalah browser, dan browser memiliki berbagai masalah kompatibilitas dan masalah kinerja, seperti:
.vue
, .jsx
, .ts
perlu dikompilasiProyek yang dikembangkan menggunakan express.js atau koa.js tidak memiliki masalah-masalah ini. Selain itu, Node.js mengadopsi spesifikasi modular CommonJS dan memiliki mekanisme caching; pada saat yang sama, modul hanya akan diimpor saat digunakan . Jika Anda mengemasnya menjadi sebuah file, keuntungan ini justru terbuang percuma. Jadi untuk proyek node.js tidak perlu melakukan paket.
Artikel ini menggunakan sistem CentOS sebagai contoh untuk mendemonstrasikan
Untuk memfasilitasi peralihan versi node, kami menggunakan nvm untuk mengelola node.
Nvm (Node Version Manager) adalah alat manajemen versi Node.js. Melalui itu, node dapat secara sewenang-wenang dialihkan di antara beberapa versi, menghindari pengunduhan berulang dan operasi instalasi ketika peralihan versi diperlukan.
Repositori resmi Nvm adalah github.com/nvm-sh/nvm. Karena skrip instalasinya disimpan di situs konten githubusercontent
, seringkali skrip tersebut tidak dapat diakses. Jadi saya membuat repositori mirror baru di gitee, sehingga saya dapat mengakses skrip instalasinya dari gitee.
Unduh skrip instalasi melalui perintah curl
dan gunakan bash
untuk menjalankan skrip, yang secara otomatis akan menyelesaikan instalasi nvm:
# curl -o- https://gitee.com/hsyq/nvm/raw/master/install.sh | bash
Ketika instalasi selesai Setelah itu, kita membuka jendela baru untuk menggunakan nvm:
[root@ecs-221238 ~]# nvm -v0.39.1
dapat mencetak nomor versi secara normal, menunjukkan bahwa nvm telah berhasil diinstal.
Sekarang Anda dapat menggunakan nvm untuk menginstal dan mengelola node.js.
Lihat versi node yang tersedia:
# nvm ls-remote
Instal node:
# nvm install 18.0.0
Lihat versi node yang diinstal:
[root@ecs-221238 ~]# nvm list -> v18.0.0 bawaan -> 18.0.0 (-> v18.0.0) iojs -> T/A (default) tidak stabil -> T/A (default) simpul -> stabil (-> v18.0.0) (default) stable -> 18.0 (-> v18.0.0) (default)
Pilih versi yang akan digunakan:
# nvm use 18.0.0
Satu hal yang perlu diperhatikan adalah saat menggunakan nvm di Windows, Anda perlu menggunakan hak administrator untuk menjalankan perintah nvm. Di CentOS, saya login sebagai pengguna root secara default, jadi tidak ada masalah. Jika Anda menemukan kesalahan yang tidak diketahui saat menggunakannya, Anda dapat mencari solusi atau mencoba melihat apakah masalah tersebut disebabkan oleh izin.
Saat menginstal node, npm akan diinstal secara otomatis. Periksa nomor versi node dan npm:
[root@ecs-221238 ~]# node -v v18.0.0 [root@ecs-221238 ~]#npm -v
Sumber gambar npm default
di 8.6.0adalah alamat resmi:
[root@ecs-221238 ~]# npm config get registry https://registry.npmjs.org/
Beralih ke sumber mirror Taobao domestik:
[root@ecs-221238 ~]# npm config set registry https://registry.npmmirror.com
Pada titik ini, server telah menginstal node The lingkungan dan npm dikonfigurasi.
Ada banyak cara, baik mengunduh ke server dari repositori Github/GitLab/Gitee, atau mengunggah secara lokal melalui alat ftp. Langkah-langkahnya sangat sederhana dan tidak akan diperlihatkan lagi.
Proyek demo ditempatkan di direktori /www
:
Umumnya, server cloud hanya membuka port 22 untuk login jarak jauh. Port yang umum digunakan seperti 80 dan 443 tidak terbuka. Selain itu, proyek ekspres yang kami siapkan berjalan pada port 3000. Jadi, Anda harus terlebih dahulu masuk ke konsol server cloud, mencari grup keamanan, menambahkan beberapa aturan, dan membuka port 80 dan 3000.
Selama fase pengembangan, kita dapat menggunakan nodemon
untuk pemantauan waktu nyata dan restart otomatis untuk meningkatkan efisiensi pengembangan. Dalam lingkungan produksi, Anda perlu menggunakan pembunuh terbesar—PM2.
pertama-tama instal pm2 secara global:
# npm i -g pm2
Jalankan perintah pm2 -v
untuk memeriksa apakah instalasi berhasil:
[root@ecs-221238 ~]# pm2 -v5.2.0
Beralih ke direktori proyek dan instal dependensinya terlebih dahulu:
cd /www/express-demo npm install
dan kemudian gunakan perintah pm2
untuk memulai aplikasi.
pm2 mulai app.js -i maks // Atau pm2 start server.js -i 2
Aplikasi manajemen PM2 memiliki dua mode: fork dan cluster. Saat memulai aplikasi, dengan menggunakan parameter -i untuk menentukan jumlah instance, mode cluster akan diaktifkan secara otomatis. Pada titik ini, kemampuan penyeimbangan beban telah tersedia.
-i: contoh, jumlah contoh. Anda dapat menulis nomor tertentu atau mengkonfigurasinya ke maks.
PM2
akan secara otomatis memeriksa jumlah CPU yang tersedia dan kemudian memulai proses sebanyak mungkin.
Aplikasi sekarang dimulai. PM2 akan mengelola aplikasi dalam bentuk proses daemon. Tabel ini menampilkan beberapa informasi tentang aplikasi yang berjalan, seperti status berjalan, penggunaan CPU, penggunaan memori, dll.
Akses antarmuka di browser lokal:
Mode cluster adalah model multi-proses dan multi-instance . Ketika permintaan masuk, permintaan tersebut akan ditugaskan ke salah satu proses untuk diproses. Sama seperti penggunaan modul cluster
yang telah kita lihat sebelumnya, karena pengawasan pm2, meskipun suatu proses mati, proses akan segera dimulai ulang.
Kembali ke terminal server dan jalankan perintah pm2 logs
untuk melihat log pm2:
Terlihat bahwa instance aplikasi dengan id 1 hang, dan pm2 akan segera memulai ulang instance tersebut. Perhatikan bahwa id di sini adalah id instance aplikasi, bukan id proses.
Pada titik ini, penerapan sederhana proyek ekspres telah selesai. Dengan menggunakan alat pm2, pada dasarnya kami dapat memastikan bahwa proyek kami dapat berjalan dengan stabil dan andal.
Berikut adalah ringkasan beberapa perintah alat pm2 yang umum digunakan sebagai referensi.
# Mode fork pm2 start app.js --name app # Tetapkan nama aplikasi ke app #Mode cluster# Gunakan penyeimbangan beban untuk memulai 4 proses pm2 mulai app.js -i 4 # Akan memulai 4 proses menggunakan penyeimbangan beban, tergantung pada CPU yang tersedia pm2 mulai app.js -i 0 # Setara dengan efek perintah di atas pm2 start app.js -i max # Perluas aplikasi dengan 3 proses tambahan aplikasi skala pm2 +3 # Perluas atau kecilkan aplikasi menjadi 2 proses aplikasi skala pm2 2 # Lihat status aplikasi # Tampilkan status semua proses daftar pm2 # Cetak daftar semua proses dalam format JSON mentah pm2 jlist # Gunakan JSON yang dipercantik untuk mencetak daftar semua proses daftar cantik pm2 # Tampilkan semua informasi tentang proses tertentu pm2 jelaskan 0 # Gunakan dashboard untuk memonitor semua proses pm2 monit #Manajemen log# Menampilkan semua log aplikasi log pm2 secara real time # Tampilkan log aplikasi aplikasi secara real time aplikasi log pm2 # Gunakan format json untuk menampilkan log secara real time, jangan menampilkan log lama, hanya menampilkan log yang baru dibuat pm2 logs --json #Manajemen aplikasi# Hentikan semua proses pm2 hentikan semua # Restart semua proses pm2 restart semua # Hentikan proses dengan ID yang ditentukan pm2 stop 0 # Proses restart dengan ID yang ditentukan pm2 restart 0 # Hapus proses pm2 dengan ID 0 hapus 0 # Hapus semua proses pm2 hapus semua
Anda dapat mencoba sendiri setiap perintah untuk melihat efeknya.
Berikut adalah demonstrasi khusus dari perintah monit
, yang dapat meluncurkan panel di terminal untuk menampilkan status aplikasi yang berjalan secara real time. Semua aplikasi yang dikelola oleh pm2 dapat dialihkan melalui panah atas dan bawah:
PM2 memiliki fungsi yang sangat kuat, jauh lebih banyak daripada perintah di atas. Dalam penerapan proyek nyata, Anda mungkin juga perlu mengonfigurasi file log, mode tontonan, variabel lingkungan, dll. Akan sangat membosankan untuk mengetikkan perintah secara manual setiap saat, jadi pm2 menyediakan file konfigurasi untuk mengelola dan menyebarkan aplikasi.
Anda dapat membuat file konfigurasi melalui perintah berikut:
[root@ecs-221238 express-demo]# pm2 init simple File /www/express-demo/ecosystem.config.js yang dihasilkan
akan menghasilkan file ecosystem.config.js
:
module.exports = { aplikasi : [{ nama: "aplikasi1", skrip : "./app.js" }] }
Anda juga dapat membuat file konfigurasi sendiri, seperti app.config.js
:
const path = require('path') modul.ekspor = { // Satu file konfigurasi dapat mengelola beberapa aplikasi node.js secara bersamaan // aplikasi adalah array, setiap item adalah konfigurasi aplikasi apps: [{ //Nama aplikasi: "demo-ekspres", // Skrip file entri aplikasi: "./server.js", // Ada dua mode untuk memulai aplikasi: cluster dan fork. exec_mode: 'kluster', // Jumlah instance aplikasi yang akan dibuat: 'maks', // Aktifkan pemantauan dan restart aplikasi secara otomatis ketika file berubah tonton: benar, //Abaikan perubahan pada beberapa file direktori. // Karena direktori log ditempatkan di jalur proyek, maka harus diabaikan, jika tidak, aplikasi akan menghasilkan log ketika dimulai. PM2 akan memulai ulang ketika memantau perubahan. Jika dimulai ulang dan menghasilkan log, maka akan masuk tak terbatas lingkaran abaikan_watch: [ "simpul_modul", "log" ], // Jalur penyimpanan log kesalahan err_file: path.resolve(__dirname, 'logs/error.log'), //Cetak jalur penyimpanan log out_file: path.resolve(__dirname, 'logs/out.log'), //Atur format tanggal di depan setiap log dalam file log log_date_format: "YYYY-MM-DD HH:mm:ss", }] }
Biarkan pm2 menggunakan file konfigurasi untuk mengelola aplikasi node:
pm2 start app.config.js
Sekarang aplikasi yang dikelola oleh pm2 akan meletakkan log di direktori proyek (defaultnya ada di direktori instalasi pm2), dan dapat memantau perubahan file. , mulai ulang layanan secara otomatis.