http.Agent理解

http.Agent官方文檔

附圖,個(gè)人對(duì)http.Agent的理解:

image.png

一個(gè) Agent是在client端用來管理鏈接的持久性和重用速种。對(duì)于一個(gè)host+port維持著一個(gè)請(qǐng)求隊(duì)列齿穗,這些請(qǐng)求重復(fù)使用一個(gè)socket控硼,直到這個(gè)隊(duì)列空自点,這時(shí)韩脏,這個(gè)socket會(huì)被destroy或者放到pool里,在pool里時(shí)這個(gè)socket將會(huì)被再次重用(這兩個(gè)行為取決于keepAlive的配置)

keepAlive
keepAliveMsecs
maxSockets
maxFreeSockets

在pool中的鏈接已經(jīng)開啟了tcp的Keep-Alive窒升,然而在server端會(huì)有如下行為影響pool中的鏈接:

測(cè)試代碼準(zhǔn)備:

client.js


const http = require('http');

const agent = new http.Agent({
    keepAlive: true,
    keepAliveMsecs: 1000,
    maxSockets: 4,
    maxFreeSockets: 2
});

const test = () => {
    return new Promise((resolve, reject) => {
        const option = {
            protocol: 'http:',
            host: 'localhost',
            port: 9990,
            path: `/`,
            agent: agent,
            // agent: agent,
            headers: {"Connection": "keep-alive"},
            method: 'GET'
        };


        const req = http.request(option, function(res) {
            res.setEncoding('utf8');
            let body = '';
            res.on('data', (chunk) => {
                body += chunk;
            });
            res.on('end', () => {
                resolve(body)
            });
        });

        req.on('error', (e) => {
            console.error(`problem with request: ${e.message}`);
            console.log(e.stack)
        });


        req.end();
    })
};




const sendReq = (count) => {
    let arr = [];
    for (let i=0;i<count;i++) arr.push(test())
    Promise.all(arr).then(function(){
        console.log('======end======')
    })
}


server.js

const http = require('http');

let server = http.createServer(function(req, res) {

    console.log(req.connection.remotePort);
    res.end('200');

}).listen(9990);

server.keepAliveTimeout = 5000; // 這個(gè)值默認(rèn)就是5s,可以直接賦值修改

  • server端主動(dòng)關(guān)閉空閑鏈接:client收到通知后缀遍,當(dāng)前socket會(huì)從pool中移除,下一次請(qǐng)求時(shí)會(huì)創(chuàng)建一個(gè)新的socket

Pooled connections have TCP Keep-Alive enabled for them, but servers may still close idle connections, in which case they will be removed from the pool and a new connection will be made when a new HTTP request is made for that host and port.

client.js補(bǔ)充

sendReq(1);  // 先發(fā)送一個(gè)req

setTimeout(() => {sendReq(1)}, 10 * 1000); //隔10s后再次發(fā)送一次req

server.js輸出如下:

 // console.log(req.connection.remotePort);

 53957 // 發(fā)送第一個(gè)請(qǐng)求的socket port
 54011 // 隔10s后發(fā)送第二個(gè)請(qǐng)求的socket port饱须。port不同域醇,說明第一個(gè)socket已經(jīng)被關(guān)閉

wireshark抓包如下:

image.png

可以看到每隔1s發(fā)送向server端發(fā)送了一次TCP Keep-Alive探測(cè)。由于server端設(shè)置的keepAliveTimeout為5s(默認(rèn)就是5s)蓉媳,所以在5s后關(guān)閉了這個(gè)tcp鏈接譬挚,相應(yīng)的,client端收到關(guān)閉的信號(hào)就會(huì)close到當(dāng)前的socket酪呻,并從pool中移除這個(gè)socket

_http_agent.js

Agent.prototype.removeSocket = function removeSocket(s, options) {
  var name = this.getName(options);
  debug('removeSocket', name, 'writable:', s.writable);
  var sets = [this.sockets];

  // If the socket was destroyed, remove it from the free buffers too.
  if (!s.writable)
    sets.push(this.freeSockets);

  for (var sk = 0; sk < sets.length; sk++) {
    var sockets = sets[sk];

    if (sockets[name]) {
      var index = sockets[name].indexOf(s);
      if (index !== -1) {
        sockets[name].splice(index, 1);
        // Don't leak
        if (sockets[name].length === 0)
          delete sockets[name];
      }
    }
  }

  // 省略其他代碼
};
  • server端拒絕多個(gè)請(qǐng)求共用一個(gè)tcp鏈接减宣,在這種情況下,在每次請(qǐng)求時(shí)鏈接都會(huì)建立并且不能被pool玩荠。agent仍然會(huì)處理請(qǐng)求的發(fā)送漆腌,只是每個(gè)請(qǐng)求都會(huì)建立在一個(gè)新的tcp鏈接上

Servers may also refuse to allow multiple requests over the same connection, in which case the connection will have to be remade for every request and cannot be pooled. The Agent will still make the requests to that server, but each one will occur over a new connection.

client.js不變

server.js添加如下代碼

    res.shouldKeepAlive = false; // 禁用shouldkeepAlive
    res.end('200');

wireshark抓包如下:


image.png

可以看到,請(qǐng)求結(jié)束后阶冈,server就會(huì)關(guān)閉socket

When a connection is closed by the client or the server, it is removed from the pool. Any unused sockets in the pool will be unrefed so as not to keep the Node.js process running when there are no outstanding requests. (see socket.unref()).

當(dāng)想要保持一個(gè)http請(qǐng)求很長(zhǎng)時(shí)間并不在pool中闷尿,可以調(diào)用“agentRemove”(這個(gè)時(shí)間取決于server端socket close的時(shí)間)

Sockets are removed from an agent when the socket emits either a 'close' event or an 'agentRemove' event. When intending to keep one HTTP request open for a long time without keeping it in the agent, something like the following may be done:

client.js

      // new
        req.on('socket', (socket) => {
            socket.emit('agentRemove');
        });

server.js

server.keepAliveTimeout = 20000; // 為了清楚,服務(wù)端設(shè)置20s后再關(guān)閉

wireshark抓包如下:


image.png

可以看到女坑,觸發(fā)“agentRemove”后填具,當(dāng)前socket并沒有發(fā)送探測(cè)包,并且知道server端通知關(guān)閉才關(guān)閉堂飞。

當(dāng)agent參數(shù)設(shè)置為false時(shí)灌旧,client將會(huì)為每一個(gè)http請(qǐng)求都創(chuàng)建一個(gè)鏈接。


node keep-alive還是很有必要開啟的绰筛,尤其時(shí)作為中間層代理枢泰,當(dāng)有如下模式時(shí):高并發(fā)下優(yōu)勢(shì)更明顯

browser瀏覽器 -> nginx -> node -> nginx -> java

當(dāng)nginx和node都開啟keep-alive時(shí),性能測(cè)試如下:


image.png

參考資料mark:
http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html

https://stackoverflow.com/questions/30365250/what-will-happen-if-i-use-socket-setkeepalive-in-node-js-server

https://stackoverflow.com/questions/19043355/how-to-use-request-js-node-js-module-pools

https://github.com/nodejs/node/issues/10774

NodeJS的底層通信

這篇文章很詳細(xì)铝噩,贊一個(gè)
https://www.zhuxiaodong.net/2018/tcp-http-keepalive/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末衡蚂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌毛甲,老刑警劉巖年叮,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異玻募,居然都是意外死亡只损,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門七咧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跃惫,“玉大人,你說我怎么就攤上這事艾栋”妫” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蝗砾,是天一觀的道長(zhǎng)先较。 經(jīng)常有香客問我,道長(zhǎng)悼粮,這世上最難降的妖魔是什么闲勺? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮矮锈,結(jié)果婚禮上霉翔,老公的妹妹穿的比我還像新娘。我一直安慰自己苞笨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布子眶。 她就那樣靜靜地躺著瀑凝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪臭杰。 梳的紋絲不亂的頭發(fā)上粤咪,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音渴杆,去河邊找鬼寥枝。 笑死,一個(gè)胖子當(dāng)著我的面吹牛磁奖,可吹牛的內(nèi)容都是我干的囊拜。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼比搭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼冠跷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤蜜托,失蹤者是張志新(化名)和其女友劉穎抄囚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體橄务,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幔托,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜂挪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柑司。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锅劝,靈堂內(nèi)的尸體忽然破棺而出攒驰,到底是詐尸還是另有隱情,我是刑警寧澤故爵,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布玻粪,位于F島的核電站,受9級(jí)特大地震影響诬垂,放射性物質(zhì)發(fā)生泄漏劲室。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一结窘、第九天 我趴在偏房一處隱蔽的房頂上張望很洋。 院中可真熱鬧,春花似錦隧枫、人聲如沸喉磁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)协怒。三九已至,卻和暖如春卑笨,著一層夾襖步出監(jiān)牢的瞬間孕暇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工赤兴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妖滔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓桶良,卻偏偏與公主長(zhǎng)得像座舍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子艺普,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理簸州,服務(wù)發(fā)現(xiàn)鉴竭,斷路器,智...
    卡卡羅2017閱讀 134,637評(píng)論 18 139
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測(cè)試 ...
    KeKeMars閱讀 6,313評(píng)論 0 6
  • 聚散不由人 也沒有誰(shuí)愿意聽你講大道理 不斷適應(yīng)不斷改變不斷接受。
    小雨碎碎念閱讀 189評(píng)論 0 0
  • 到牛仔手袋服裝店去送貨矢洲,老板娘是特別知性的女子璧眠,短發(fā),大眼读虏,笑容可掬责静。倆人坐在她的小店里閑聊,對(duì)于生命的理解和詮釋...
    yanzikuaile燕子閱讀 396評(píng)論 2 1
  • 網(wǎng)頁(yè)的緩存是由http消息頭中的“Cache-control”來控制的盖桥,常見的取值有private灾螃、no-cach...
    鐵木真丫丫丫閱讀 2,187評(píng)論 0 3