Node.js http.Agent實現真正的keep-Alive代理

1、 Kepp-Alive使用狀況

一、當你的Server內存充足時,KeepAlive =On仍是Off對系統性能影響不大。css

二、當你的Server上靜態網頁(Html、圖片、Css、Js)居多時,建議打開KeepAlive 。數據庫

三、當你的Server多爲動態請求(由於鏈接數據庫,對文件系統訪問較多),KeepAlive 關掉,會節省必定的內存,節省的內存正好能夠做爲文件系統的Cache(vmstat命令中cache一列),下降I/O壓力。 瀏覽器

PS:當KeepAlive =On時,KeepAliveTimeOut的設置其實也是一個問題,設置的太短,會致使Apache 頻繁創建鏈接,給Cpu形成壓力,設置的過長,系統中就會堆積無用的Http鏈接,消耗掉大量內存,具體設置多少,能夠進行不斷的調節,因你的網站瀏覽和服務器配置 而異。服務器


2、瀏覽器對Keep-Alive的併發數狀況及域名開銷

對於HTTP/1.0來講能夠充分利用瀏覽器默認最大併發鏈接數比HTTP/1.1多的好 處,實現不增長新域名的開銷而更高的並行下載,減小域名解釋的開銷(注:IE 6,7在HTTP/1.0中默認最大併發鏈接數爲4,在HTTP/1.1中默認最大併發鏈接數爲2,IE8都爲6,Firefox2在HTTP/1.0中 默認最大併發鏈接數爲2 在HTTP/1.1中默認最大併發鏈接數爲8,firefox 3默認都是6),根據10年7月Google索引的42億個網頁的統計報告,每張網頁裏包含29.39個圖片,7.09個外部腳本,3.22個外部CSS 樣式表,若是設置了Keep-Alive而且合理控制Keep-Alive TimeOut這個參數能夠大量的節約鏈接的開銷,提升相應速度。若是設置很差,在大併發的狀況小,因維持大量鏈接而使服務器資源耗盡,而對於目前國內大 部分的用戶使用的仍是IE6,7的狀況併發


3、使用Node.js的http.Agent開發一個真正的Keep-Alive代理

/**
 * Created by Administrator on 14-4-30.
 */
var http = require('http');
var EventEmitter = require('evnets').EventEmitter;
var net = require('net');
var util = require('util');

var Agent = function(optinos)
{
    var self = this;
    // 選項配置
    self.options  = options || {};
    // 保存請求的所有hostname
    self.requests = {};
    // 建立的socket鏈接數
    self.sockets = {};
    // 未被使用的socket
    self.unusedSockets = {};
    // socket的最大鏈接數量
    self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets;
    self.on('free',function(socket,host,port){
        var hostname = host + ':' + port;
        // 若是有正在請求的主機
        if(self.requests[hostname] && self.requests[hostname].length)
        {
            self.requests[hostname].shift().onSocket(socket);
        }
        else
        {
            // 若是沒有請求數就銷燬socket鏈接並從鏈接池移除
            if(!self.unusedSockets[hostname])
            {
                self.unusedSockets[hostname] = [];
            }
            self.unusedSockets[hostname].push(socket);
        }
    });
    self.createConnection = net.createConnection;
};
util.inherits(Agent,EventEmitter);

Agent.defaultMaxSockets = 10;

Agent.prototype.defaultPort = 80;

Agent.prototype.addRequest = function(req,host,port)
{
    var hostname = host + ':' + port;
    if(this.unusedSockets[hostname] && this.unusedSockets[hostname].length)
    {
        req.onSocket(this.unusedSockets[hostname].shift());
        return;
    }
    if(!this.sockets[hostname])
    {
        this.sockets[hostname] = [];
    }
    if(this.sockets[hostname].length < this.maxSockets)
    {
        req.onSocket(this.createSocket(hostname,host,port));
    }
    else
    {
        if(!this.requests[hostname])
        {
            this.requests[hostname] = [];
        }
        this.requests[hostname].push(req);
    }
};

Agent.prototype.createSocket = function(name, host, port) {
    var self = this;
    var s = self.createConnection(port, host, self.options);
    if (!self.sockets[name]) {
        self.sockets[name] = [];
    }
    this.sockets[name].push(s);
    var onFree = function() {
        self.emit('free', s, host, port);
    }
    s.on('free', onFree);
    var onClose = function(err) {
        // 這是惟一移除socket代理的地方,若是你想從鏈接池中移除socket,就關閉鏈接,全部的socket錯誤會致使鏈接關閉
        self.removeSocket(s, name, host, port);
    }
    s.on('close', onClose);
    var onRemove = function() {
        // We need this function for cases like HTTP "upgrade"
        // (defined by WebSockets) where we need to remove a socket from the pool
        //  because it'll be locked up indefinitely
        self.removeSocket(s, name, host, port);
        s.removeListener('close', onClose);
        s.removeListener('free', onFree);
        s.removeListener('agentRemove', onRemove);
    }
    s.on('agentRemove', onRemove);
    return s;
};
Agent.prototype.removeSocket = function(s, name, host, port) {
    if (this.sockets[name]) {
        var index = this.sockets[name].indexOf(s);
        if (index !== -1) {
            this.sockets[name].splice(index, 1);
        }
    } else if (this.sockets[name] && this.sockets[name].length === 0) {
        delete this.sockets[name];
        delete this.requests[name];
    }
    if (this.requests[name] && this.requests[name].length) {
        // If we have pending requests and a socket gets closed a new one
        // needs to be created to take over in the pool for the one that closed.
        this.createSocket(name, host, port).emit('free');
    }
};


如何使用:socket

var http = require('http');
var keepAliveAgent = require('./agent.js'); 
var agent = new keepAliveAgent({ maxSockets: 100 }); 
// Optionally define more parallel sockets 
var options = {
    agent: agent,
    hostname: 'example.com',
    path: '/path'}; 
// do get
http.get(options);
// do request
http.request(options,function(){});
相關文章
相關標籤/搜索