摘要:在談如何做到進(jìn)程平滑離場前,我們需要一種機制,這種機制能讓我們主動通知進(jìn)程何時離場,這就涉及到進(jìn)程間通信的知識了,我們先簡單了解下。進(jìn)程間通信對或類系統(tǒng)而言,進(jìn)程間通信的方式有很多種信號是其中的一種。
本文由云+社區(qū)發(fā)表作者:草小灰
使用 Node.js 搭建 HTTP Server 已是司空見慣的事。在生產(chǎn)環(huán)境中,Node 進(jìn)程平滑重啟直接關(guān)系到服務(wù)的可靠性,它的重要性不容我們忽視。既然是平滑重啟,就涉及到新舊進(jìn)程的接替過渡:
首先,保證新進(jìn)程平滑入場
其次,保證舊進(jìn)程平滑離場
本文主要談?wù)撓拢谛屡f進(jìn)程接替過渡期間,如何保證舊進(jìn)程平滑離場。那怎樣的離場才算平滑的呢?
如何定義平滑離場以進(jìn)程離場作為時間分割點,我們可以把請求分為兩類:增量請求和存量請求。
在進(jìn)程離場前,停止接收新的(增量)請求
在進(jìn)程離場前,保證未完成的(存量)請求正常響應(yīng)
所以,達(dá)成以上兩個目標(biāo),基本上我們就認(rèn)為進(jìn)程的離場是平滑的。在談如何做到進(jìn)程平滑離場前,我們需要一種機制,這種機制能讓我們主動通知進(jìn)程何時離場,這就涉及到進(jìn)程間通信(IPC)的知識了,我們先簡單了解下。
進(jìn)程間通信對 Unix 或類 Unix 系統(tǒng)而言,進(jìn)程間通信的方式有很多種 —— 信號(Signal)是其中的一種。
信號的種類有很多,如 SIGINT、 SIGTERM 及 SIGKILL 等。這些信號視具體需要用于不同的場景,比如 SIGKILL 一般用于強殺進(jìn)程。
我們可以在命令行執(zhí)行 kill -l 查看所有的信號,如下所示(其中的數(shù)字表示 signal number):
$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE 9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGURG 17) SIGSTOP 18) SIGTSTP 19) SIGCONT 20) SIGCHLD 21) SIGTTIN 22) SIGTTOU 23) SIGIO 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGINFO 30) SIGUSR1 31) SIGUSR2
我們可以使用 kill 命令向進(jìn)程發(fā)送指定信號:
# 發(fā)送 SIGTERM 信號(默認(rèn),無須指定信號類型)給進(jìn)程 $ kill# 發(fā)送 SIGINT 信號給進(jìn)程,其中 為具體的進(jìn)程 ID $ kill -INT # 發(fā)送 SIGKILL 信號給進(jìn)程 $ kill -KILL # 或者 $ kill -9
進(jìn)程可以對接收到的信號作出回應(yīng)。對 Node 應(yīng)用而言,信號是被當(dāng)作事件發(fā)送給 Node 進(jìn)程的,進(jìn)程接收到 SIGTERM 及 SIGINT 事件有默認(rèn)回調(diào),官方文檔是這么描述的:
"SIGTERM" and "SIGINT" have default handlers on non-Windows platforms that reset the terminal mode before exiting with code 128 + signal number. If one of these signals has a listener installed, its default behavior will be removed (Node.js will no longer exit).
這句話寫的很抽象,它是什么意思呢?我們以一個簡單的 Node 應(yīng)用為例。
新建文件,鍵入如下代碼,將其保存為 server.js:
const http = require("http"); const server = http.createServer((req, res) => { setTimeout(() => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("It works"); }, 5000); }); server.listen(9420);
這里為了方便測試,對應(yīng)用接收到的每個 http 請求,等待 5 秒后再進(jìn)行響應(yīng)。
執(zhí)行 node server.js 啟動應(yīng)用。為了給應(yīng)用發(fā)送信號,我們需要獲取應(yīng)用的進(jìn)程 ID,我們可以使用 lsof 命令查看:
$ lsof -i TCP:9420 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 70826 myunlessor 13u IPv6 0xd250033eef8912eb 0t0 TCP *:9420 (LISTEN)
事實上,我們也可以在代碼里通過 console.log(process.pid) 獲取進(jìn)程 ID。這里只是順便介紹一種,在知道監(jiān)聽 TCP 端口的情況獲取進(jìn)程的方式。
隨后,我們發(fā)起一個請求,在收到響應(yīng)之前(有 5 秒等待時間),我們給應(yīng)用發(fā)送 SIGINT 信號。
$ curl http://localhost:9420 & $ kill -INT 70826 curl: (52) Empty reply from server [1]+ Exit 52 curl http://localhost:9420
可以看到,請求沒能正常收到響應(yīng)。也就是說,默認(rèn)情況下,Node 應(yīng)用在接收到 SIGINT 信號時,會馬上把進(jìn)程殺死,無視進(jìn)程還沒處理完成的請求。所幸的是,我們可以手動監(jiān)聽進(jìn)程的 SIGINT 事件,像這樣:
process.on("SIGINT", () => { // do something here });
如果我們在事件回調(diào)里什么都不做,就意味著忽略該信號,進(jìn)程該干嘛干嘛,像什么事情都沒發(fā)生一樣。
那么,如果我手動監(jiān)聽 SIGKILL 會如何呢?對不起,SIGKILL 是不能被監(jiān)聽的,官方文檔如是說:
"SIGKILL" cannot have a listener installed, it will unconditionally terminate Node.js on all platforms.
這是合情合理的,要知道 SIGKILL 是用于強殺進(jìn)程的,你無法干預(yù)它的行為。
回到上面的問題,我們可以近似地理解為 Node 應(yīng)用響應(yīng) SIGINT 事件的默認(rèn)回調(diào)是這樣子的:
process.on("SIGINT", () => { process.exit(128 + 2/* signal number */); });
我們可以打印 exit code 來驗證:
$ node server.js $ echo $? 130
有了信號,我們就能主動通知進(jìn)程何時離場了,下面談一談進(jìn)程如何平滑離場。
如何讓進(jìn)程平滑離場我們在上面示例基礎(chǔ)上,也就是在文件 server.js 中,補充如下代碼:
process.on("SIGINT", () => { server.close(err => { process.exit(err ? 1 : 0); }); });
這段代碼很簡單,我們改寫應(yīng)用接收到 SIGINT 事件的默認(rèn)行為,不再簡單粗暴直接殺死進(jìn)程,而是在 server.close 方法回調(diào)中再調(diào)用 process.exit 方法,接著繼續(xù)試驗一下。
$ lsof -i TCP:9420 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 75842 myunlessor 13u IPv6 0xd250033ec7c9362b 0t0 TCP *:9420 (LISTEN) $ curl http://localhost:9420 & [1] 75878 $ kill -2 75842 $ It works [1]+ Done curl http://localhost:9420
可以看到,應(yīng)用在退出前(即進(jìn)程離場前),成功地響應(yīng)了存量請求。
我們還可以驗證,進(jìn)程離場前,確實不再接收增量請求:
$ curl http://127.0.0.1:9420 curl: (7) Failed to connect to 127.0.0.1 port 9420: Connection refused
這正是 server.close 所做的事,進(jìn)程平滑離場就是這么簡單,官方文檔是這么描述這個 API 的:
Stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a "close" event. The optional callback will be called once the "close" event occurs. Unlike that event, it will be called with an Error as its only argument if the server was not open when it was closed.結(jié)束語
進(jìn)程平滑離場只是 Node 進(jìn)程平滑重啟的一部分。生產(chǎn)環(huán)境中,新舊進(jìn)程的接替涉及進(jìn)程負(fù)載均衡、進(jìn)程生命周期管理等方方面面的考慮。專業(yè)的工具做專業(yè)的事,PM2 就是 Node 進(jìn)程管理很好的選擇。
此文已由騰訊云+社區(qū)在各渠道發(fā)布
獲取更多新鮮技術(shù)干貨,可以關(guān)注我們騰訊云技術(shù)社區(qū)-云加社區(qū)官方號及知乎機構(gòu)號
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/101386.html
摘要:具體調(diào)用鏈路如圖函數(shù)主要是解析啟動參數(shù),并過濾選項傳給引擎。查閱文檔之后發(fā)現(xiàn),通過指定參數(shù)可以設(shè)置線程池大小。原來的字節(jié)碼編譯優(yōu)化還有都是通過多線程完成又繼續(xù)深入調(diào)查,發(fā)現(xiàn)環(huán)境變量會影響的線程池大小。執(zhí)行過程如下調(diào)用執(zhí)行。 作者:正龍 (滬江Web前端開發(fā)工程師)本文原創(chuàng),轉(zhuǎn)載請注明作者及出處。 隨著Node.js的普及,越來越多的開發(fā)者使用Node.js來搭建環(huán)境,也有很多公司開始把...
摘要:具體調(diào)用鏈路如圖函數(shù)主要是解析啟動參數(shù),并過濾選項傳給引擎。查閱文檔之后發(fā)現(xiàn),通過指定參數(shù)可以設(shè)置線程池大小。原來的字節(jié)碼編譯優(yōu)化還有都是通過多線程完成又繼續(xù)深入調(diào)查,發(fā)現(xiàn)環(huán)境變量會影響的線程池大小。執(zhí)行過程如下調(diào)用執(zhí)行。 作者:正龍 (滬江Web前端開發(fā)工程師)本文原創(chuàng),轉(zhuǎn)載請注明作者及出處。 隨著Node.js的普及,越來越多的開發(fā)者使用Node.js來搭建環(huán)境,也有很多公司開始把...
摘要:在數(shù)據(jù)緩沖區(qū)已超過或?qū)懭腙犃挟?dāng)前正忙的任何情況下,將返回。當(dāng)返回值時,背壓系統(tǒng)啟動,它會暫停傳入的流發(fā)送任何數(shù)據(jù),并等待消費者再次準(zhǔn)備就緒,清空數(shù)據(jù)緩沖區(qū)后,將發(fā)出事件并恢復(fù)傳入的數(shù)據(jù)流。 流中的背壓 在數(shù)據(jù)處理過程中會出現(xiàn)一個叫做背壓的常見問題,它描述了數(shù)據(jù)傳輸過程中緩沖區(qū)后面數(shù)據(jù)的累積,當(dāng)傳輸?shù)慕邮斩司哂袕?fù)雜的操作時,或者由于某種原因速度較慢時,來自傳入源的數(shù)據(jù)就有累積的趨勢,就像...
摘要:本文翻譯自原文地址中文標(biāo)題保持的速度創(chuàng)建高性能的工具技術(shù)和提示快速摘要是一個非常多彩的平臺,而創(chuàng)建服務(wù)就是其非常重要的能力之一。在目錄下,我們執(zhí)行譯者注現(xiàn)在的話可以使用新的形式的命令語法會在剖析完畢后,創(chuàng)建文件并自動打開瀏覽器。 pre-tips 本文翻譯自: Keeping Node.js Fast: Tools, Techniques, And Tips For Making Hi...
摘要:深入?yún)f(xié)議從功能上來講,協(xié)議已經(jīng)完全能夠解決服務(wù)器與應(yīng)用之間的數(shù)據(jù)通信問題。消息頭信息主要的消息頭信息如下用于表示協(xié)議版本號。從服務(wù)器發(fā)送到應(yīng)用,表示中止一個處理中的請求。另外我們還需要明確一點就是服務(wù)器與進(jìn)程間通信是無序的。 本文首發(fā)于 深入剖析 Web 服務(wù)器與 PHP 應(yīng)用之間的通信機制 - 掌握 CGI 和 FastCGI 協(xié)議的運行原理,轉(zhuǎn)載請注明出處! 身為一名使用 PHP ...
閱讀 1213·2021-10-11 10:59
閱讀 1996·2021-09-29 09:44
閱讀 887·2021-09-01 10:32
閱讀 1457·2019-08-30 14:21
閱讀 1899·2019-08-29 15:39
閱讀 3001·2019-08-29 13:45
閱讀 3562·2019-08-29 13:27
閱讀 2033·2019-08-29 12:27