Node.js http.エージェントが真のkeep-Aliveエージェントを実現

5180 ワード

一、Kepp-Aliveの使用状況
1、サーバーのメモリが十分な場合、KeepAlive=OnかOffかはシステムの性能にあまり影響しません.
2、サーバー上の静的Webページ(Html、ピクチャ、Css、Js)が多い場合は、KeepAliveを開くことをお勧めします.
3、サーバーがダイナミックな要求が多い場合(データベース接続のため、ファイルシステムへのアクセスが多い)、KeepAliveがオフになると、一定のメモリが節約され、節約したメモリはファイルシステムのCache(vmstatコマンドのcacheの列)として、I/O圧力を下げることができます. 
PS:KeepAlive=Onの場合、KeepAliveTimeOutの設定も実は問題で、設定が短すぎると、Apacheが頻繁に接続を確立し、Cpuに圧力をかけ、設定が長すぎると、システムに無駄なHttp接続が蓄積され、大量のメモリが消費され、具体的な設定が少なく、絶えず調整することができ、あなたのウェブサイトの閲覧とサーバーの構成によって異なります.
二、ブラウザによるKeep-Aliveの同時発生数及びドメイン名のオーバーヘッド
HTTP/1.0にとって、ブラウザのデフォルト最大同時接続数がHTTP/1.1より多いメリットを十分に利用でき、新しいドメイン名のオーバーヘッドを増やさずにより高い並列ダウンロードを実現できます.ドメイン名解釈のオーバーヘッドを削減(注:IE 6,7 HTTP/1.0では最大同時接続数が4、HTTP/1.1では最大同時接続数が2、IE 8では6、Firefox 2では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というパラメータを合理的に制御すれば、接続のオーバーヘッドを大幅に節約し、対応する速度を向上させることができます.設置がうまくいかなければ,大同時の場合は小さく,大量の接続を維持するためにサーバ資源が枯渇するが,現在国内の大部分のユーザが使用しているのはIE 6,7の場合である.
三、Nodeを使う.jsのhttp.エージェントは本物の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');
    }
};

使用方法:
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(){});