node http keep-alive demo
http keep-alive 相關資料非常多,如果深挖,那可能就長篇大論了,不合適普及,這只是一篇新手入門引導,
主要講解 node 下 http 請求的坑,以及 keep-alive 的簡單使用,後續才會詳細剖析原理。
起因
我司使用 node 做中間層開發,所以 api 都是 node 代理轉發的,雖然目前 qps 不是特別高,都能滿足需求,但壓測總歸是有的。
不壓不知道,一壓,emmmmm。。。
搭建個純淨的 node 請求測試環境
先不說業務,我們來搭建個最簡單的測試環境,看看壓測工具能跑到多少 QPS。
然後我們寫個 api 代理模組看看能跑到多少。
使用 node 官方例子 https://nodejs.org/en/about/。
[JavaScript] 純文字檢視 複製程式碼const http = require('http'); const hostname = '127.0.0.1'; const port = 3000; const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Hello World\n'); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
然後我們用 wrk 來壓測,因為簡單方便。
引數 -c 併發數,就想象成同時請求後臺 api 的數量,一個頁面往往會呼叫3-5個後臺介面。
引數 -d 持續時間,這就是壓測本質啊,持續越久,效果越真實,因為很多時候後面會掛掉的。
看看 10 併發,持續 10 秒的情況。
[Shell] 純文字檢視 複製程式碼$ wrk -c 10 -d 10 http://localhost:3000 Running 10s test @ http://localhost:3000 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 523.02us 129.44us 4.04ms 92.33% Req/Sec 9.59k 550.11 10.55k 82.67% 192745 requests in 10.10s, 25.37MB read Requests/sec: 19083.81 Transfer/sec: 2.51MB
可以看出 Req/Sec 的 Avg 值,就是平均 QPS 為 9.59k,因為預設開了2執行緒,所以總 QPS 為 19k。
好了,測試環境和測試結果已經有了直觀的展現。
下面搭建 node 介面代理然後重新壓測。
node 介面代理
我們由於業務需求,沒用 http-proxy 做直接的代理轉發,而是基於 got 模組,自定義的介面模型。
我們先不考慮業務開銷,直接代理轉發看看結果。
[JavaScript] 純文字檢視 複製程式碼const http = require('http'); const got = require('got'); const hostname = '127.0.0.1'; const port = 8000; const server = http.createServer((req, res) => { got.get('http://localhost:3000/').then(({ body }) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end(body); }).catch((err) => console.log(err)); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
程式碼跟剛才一樣,只是多了一層 got 代理介面。
這裡沒用 got stream 直接 pipe 給 res,因為我們的代理介面中會做其他業務操作。
現在來壓測下這個服務的情況吧。
[Shell] 純文字檢視 複製程式碼$ wrk -c 10 -d 10 http://localhost:8000 Running 10s test @ http://localhost:8000 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 13.63ms 20.29ms 193.78ms 94.45% Req/Sec 522.58 126.90 660.00 89.23% 10237 requests in 10.03s, 1.35MB read Requests/sec: 1020.52 Transfer/sec: 137.53KB
有點慌,什麼情況,怎麼會相差這麼多。
我們在 3000 服務中加入 console.log(req.headers); 看看 headers 欄位。
[JavaScript] 純文字檢視 複製程式碼const server = http.createServer((req, res) => { console.log(req.headers); // 輸出 headers res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Hello World\n'); });
然後訪問 8000 服務,看到控制檯輸出:
[JavaScript] 純文字檢視 複製程式碼{ 'user-agent': 'got/9.3.2 (https://github.com/sindresorhus/got)', 'accept-encoding': 'gzip, deflate', host: 'localhost:3000', connection: 'close' }
其中 connection 所以每次請求都會重新建立 tcp 連線,浪費了不少效能。
接下來我們要開啟 keep-alive 提升代理效能。
開啟 keep-alive
安裝 agentkeepalive 模組,這是 fengmk2 大佬封裝的模組,我們直接使用看看效果先。
[Shell] 純文字檢視 複製程式碼$ yarn add agentkeepalive
然後在 8000 服務中開啟代理。
[JavaScript] 純文字檢視 複製程式碼const http = require('http'); const got = require('got'); const Agent = require('agentkeepalive'); const agent = new Agent(); const hostname = '127.0.0.1'; const port = 8000; const server = http.createServer((req, res) => { got.get('http://localhost:3000/', { agent }).then(({ body }) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end(body); }).catch((err) => console.log(err)); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
重新啟動,訪問 8000 服務,檢視 3000 服務控制檯。
[JavaScript] 純文字檢視 複製程式碼{ 'user-agent': 'got/9.3.2 (https://github.com/sindresorhus/got)', 'accept-encoding': 'gzip, deflate', host: 'localhost:3000', connection: 'keep-alive' }
看到已經開啟了 keep-alive,我們把 3000 服務中的 log 先關掉,否則影響結果。
[Shell] 純文字檢視 複製程式碼$ wrk -c 10 -d 10 http://localhost:8000 Running 10s test @ http://localhost:8000 2 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 5.32ms 1.84ms 19.30ms 89.87% Req/Sec 0.96k 69.30 1.06k 78.50% 19141 requests in 10.02s, 2.52MB read Requests/sec: 1910.43 Transfer/sec: 257.46KB
可以看到效能提升了 1 倍,將近 1k 了,但跟原服務 9.59k 還是相差太多。
目前我們只能開多程式來提升整體 QPS 了。
比如開 9 程式,每個程式都能吃到近 1k,差不多可以吃滿源服務。
但如果你開了 10 程式甚至更多,那瓶頸就會在源服務上,就需要給源服務加 cpu 或加伺服器。
小結
這裡還有個會影響結果的因素,而且影響會比較大。
因為是我本地測試,兩個服務都跑本地,要嚴謹的話,應該跑在伺服器上,或相同配置的虛擬機器中。
不過本文意圖是體驗 keep-alive 和怎麼用 node 吃滿介面服務。
所以就不做那麼嚴謹的測試了。
還要吐槽一點,node http 請求,效能真是低下啊,或者是我不知道怎麼正確的使用。
因為 autocannon 壓測工具,也是 node 寫的,但他能吃滿,我們直接寫請求,只能達到 1/10。
我自己寫了個基於 net 的 http 1.1 請求 demo 測試,只提升了100左右,並沒有特別大的改進。
因為水平有限,沒寫基於 net + keep-alive 的 http 1.1 測試,所以不能下結論。
後續我會慢慢研究 keep-alive 然後記錄分享的。
相關文章
- http keep-alive與tcp keep-aliveHTTPKeep-AliveTCP
- 長連線及在Node中的應用——HTTP/1.1 keep-aliveHTTPKeep-Alive
- 關於 Http 協議中的 keep-alive 與 Tcp keep-aliveHTTP協議Keep-AliveTCP
- node http模組HTTP
- 關於 HTTP 協議中的 keep-aliveHTTP協議Keep-Alive
- Node 深入 HTTP 模組HTTP
- node服務端渲染(完整demo)服務端
- Node學習記錄: nodemon
- 【node】模組解析之 httpHTTP
- Node實踐 --- http 模組HTTP
- HTTP協議頭部與Keep-Alive模式詳解HTTP協議Keep-Alive模式
- 每天學點node系列-httpHTTP
- Node.js 的 http模組Node.jsHTTP
- go socket、http網路程式設計demoGoHTTP程式設計
- node.js http.get 和http.post 資料Node.jsHTTP
- 開啟 Keep-Alive 可能會導致http 請求偶發失敗Keep-AliveHTTP
- 深入淺出 Node ( 四 ) HTTP核心模組HTTP
- node.js啟動http服務Node.jsHTTP
- vscode 除錯node之npm與nodemonVSCode除錯NPM
- TCP 的 Keepalive 和 HTTP 的 Keep-Alive 是一個東西嗎?TCPHTTPKeep-Alive
- HTTP 請求頭部欄位中 connection - keep-alive 的含義HTTPKeep-Alive
- [譯] HTTP 的進化 - 0.9、1.0、1.1、Keep-Alive、Upgrade 和 HTTPSHTTPKeep-Alive
- [譯]當 Node.js Core 遇到 HTTP/2Node.jsHTTP
- 使用 HTTP/2 加速 Node.js 應用HTTPNode.js
- TCP、HTTP和Node.js的那些事TCPHTTPNode.js
- HTTP Keep-Alive模式客戶端與伺服器如何判定傳輸完成HTTPKeep-Alive模式客戶端伺服器
- Node和http:一本通【附tcp實現http小程式碼】HTTPTCP
- Socket,TCP,UDP,HTTP基本通訊原理和OC版本DemoTCPUDPHTTP
- 走進Node.js 之 HTTP實現分析Node.jsHTTP
- Gofer是Node.js 的HTTP客戶端GoNode.jsHTTP客戶端
- vue <keep-alive>VueKeep-Alive
- vue keep-aliveVueKeep-Alive
- Node.js 系列 – 搭建 “Hello World” HTTP 伺服器Node.jsHTTP伺服器
- 基於Node.js的HTTP/2 Server實踐Node.jsHTTPServer
- [譯] Node.js 能進行 HTTP/2 推送啦!Node.jsHTTP
- Node.js實現一個HTTP伺服器Node.jsHTTP伺服器
- Node.js學習之道-http+url基礎Node.jsHTTP
- Node HTTP/2 Server Push 從瞭解到放棄HTTPServer