Nodejs不支持设置超时参数?怎样增加超时处理呢?为什么要处理这些呢?
Stream是干什么的?V8有1G堆栈内存限制,那么Nodejs该如何读写大文件?
什么?异步调用还有串行和并行?Callback、Callback ... and Callback
Nodejs中请求一个HTTP URL非常方便:http.request,
    封装url get方法:
    
var http = require('http')
  , parse = require('url').parse;
function urlget(url, callback) {
    var info = parse(url)
      , path = info.pathname + (info.search || '')
      , options = { host: info.hostname, 
            port: info.port || 80, 
            path: path, 
            method: 'GET' };
    var req = http.request(options, function(res) {
        var req = http.request(options, function(res) {
        var chunks = [], length = 0;
        res.on('data', function(chunk) {
            length += chunk.length;
            chunks.push(chunk);
        }).on('end', function() {
            var data = new Buffer(length), pos = 0
              , l = chunks.length;
            for(var i = 0; i < l; i++) {
                chunks[i].copy(data, pos);
                pos += chunks[i].length;
            }
            res.body = data;
            callback(null, res);
        }).on('error', function(err) {
            callback(err, res);
        });
    }).on('error', function(err) {
        callback(err);
    });
    req.end();
};一般来说,urlget都能很好地按我们想象的方式正常工作:
var good_url = 'http://www.google.com/';
urlget(good_url, function(err, res) {
    console.log('\nGET', good_url);
    if(err) {
        console.log('error:', err, '\nHeaders:\n', 
            res ? res.headers : null);
    } else {
        console.log('Headers:\n', res.headers, 
            '\nBody:\n', res.body.toString());
    }
});
    例如一些被墙的服务,一些耗时不稳定的服务。即使是你自己的服务,也会有不稳定的时候。
var timeout_url = 'http://t.co/';
urlget(timeout_url, function(err, res) {
    console.log('\nGET', timeout_url);
    if(err) {
        console.log('error:', err, '\nResponse:\n', 
            res ? res.headers : null);
    } else {
        console.log('Headers:\n', res.headers, 
            '\nBody:\n', res.body.toString());
    }
});等不及了,请求连接超时(Request timeout)异常触发,这是我本机之前的测试结果:
GET http://t.co/
75584
error: { stack: [Getter/Setter],
  arguments: undefined,
  type: undefined,
  message: 'ETIMEDOUT, Operation timed out',
  errno: 60,
  code: 'ETIMEDOUT',
  syscall: 'connect' } 
Response:
 null
    我能自己设置超时时间吗?很抱歉,在Nodejs文档搜索得到的结果是:
不会吧,这会白白浪费生命啊,自己掌控不了很不自在啊。。。
我的命,我自己操盘!
 
http.request 即使没有提供参数让我们设置超时,但是人类已经无法阻止setTimeout的出现。
没错,就是setTimeout, 它一直都在:
var req = null, request_timeout = null;
request_timeout = setTimeout(function() {
    request_timeout = null;
    // 终止请求
    req.abort();
    // 回调返回超时异常
    callback(new Error('Request timeout'));
}, 5000);
    function urlget(url, callback) {
    // ... 此处忽略相同部分的代码 ...
    var req = null, request_timeout = null;
    request_timeout = setTimeout(function() {
        request_timeout = null; 
        req.abort();
        callback(new Error('Request timeout'));
    }, 5000);
    req = http.request(options, function(res) {
        clearTimeout(request_timeout);
        var chunks = [], length = 0;
        // ... 此处忽略相同部分的代码 ...
    }).on('error', function(err) {
        // node0.5.x及以上,
        // req.abort()会触发一次“socket hang up”
        // 所以需要判断是否超时
        if(request_timeout) {
            clearTimeout(request_timeout);
            callback(err);
        }
    });
    req.end();
};  处理请求超时,还有的情况是响应返回时间过长的问题,先看看模拟响应时间过长的服务器端代码:
var http = require('http')
  , parse = require('url').parse;
http.createServer(function(req, res) {
    var info = parse(req.url, true);
    var s = +info.query.s;
    // 响应第一批数据
    res.write('Please waitting for ' 
        + s + ' seconds...');
    setTimeout(function() {
        // 模拟响应处理时间
        res.end(s + ' seconds, url: ' + req.url);
    }, s * 1000);
}).listen(1984);
console.log('Server http://localhost:1984/');从代码中可以看到,一个请求过来,会马上响应第一批数据, 接着在参数指定的时间后响应剩余的数据。对于这种情况,我们仅仅有请求超时还是不够的:
var url = 'http://localhost:1984/foo?s=10';
urlget(url, function(err, res) {
    if(err) {
        console.log('error:', err, 
            '\nHeaders:\n', res ? res.headers);
    } else {
        console.log('Headers:\n', res.headers, 
            '\nBody:\n', res.body.toString());
    }
}); function urlget(url, callback) {
    // ... 此处忽略相同部分的代码 ...
    req = http.request(options, function(res) {
        clearTimeout(request_timeout);
        var chunks = [], length = 0, response_timeout = null;
        response_timeout = setTimeout(function() {
            response_timeout = null;
            req.abort();
            callback(new Error('Response timeout'));
        }, 5000);
        res.on('data', function(chunk) {
            length += chunk.length;
            chunks.push(chunk);
        }).on('end', function() {
            if(response_timeout) {
                // node0.5.x及以上:req.abort()会触发res的end事件
                clearTimeout(response_timeout);
                var data = new Buffer(length);
                // ... 此处忽略相同部分的代码 ...
            }
        }).on('error', function(err) {
            // ... 此处忽略相同部分的代码 ...
        }).on('aborted', function() {
            if(response_timeout) {
                // node0.5.x及以上:当res有效的时候,
                // req.abort()会触发res的aborted事件
                callback(new Error('Response aborted'), res);
            }
        });
    }).on('error', function(err) {
        // ... 此处忽略相同部分的代码 ...HTTP客户端发起请求到接受到HTTP服务器端返回响应头的这段时间, 如果超出设定时间,则表示请求超时。
示例代码:http-request-with-timeout-demo.js
HTTP服务器端开始发送响应数据到HTTP客户端接收全部数据的这段时间, 如果超出设定时间,则表示响应超时。
示例代码:http-request-with-timeout-all-demo.js
随便请求一个URL确实很容易,但是你想让你的代码健壮性足够好,你必须处理每一个细节。
一个挂起的socket可能你无法注意到,但是瞬间100w个socket挂起了,你的服务器就无法控制了。
为了解决这个疑惑,就得深入源代码了,查看lib/timers_legacy.js。 从代码中会看到这段注释:
// IDLE TIMEOUTS
//
// Because often many sockets will have the same idle timeout we will not
// use one timeout watcher per item. It is too much overhead.  Instead
// we'll use a single watcher for all sockets with the same timeout value
// and a linked list. This technique is described in the libev manual:
// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeoutsNodejs已经知道会出现大量定时器的情景,于是利用单定时器和双向链表实现了一个非常高效的方案。
像我示例中,超时时间都是固定的5秒和10秒,就算同时发起100000个请求,也只是使用一个Timer进行处理。 因为使用双向链表,从链接remove的时间复杂度是O(1),所以clearTimeout也是非常高效的。
var readstream = fs.createReadStream(uploadfile);readstream.on('data', function(chunk) {
    console.log('write', chunk.length);
    // 向服务器发送数据
    req.write(chunk);
});readstream.on('end', function() {
    req.end();
});嫌既监听data又要监听end事件很麻烦?那就试试pipe吧,简直像安装水管那么简单。
readstream.pipe(req);通过readStream读取大文件并发送到网络中去:upload_file.js
var writestream = fs.createWriteStream(savefile);res.pipe(writestream);PS:非pipe方式请发挥你的水电工想象力。
writestream.on('close', function() {
    callback(null, res);
});通过WriteStream接收网络中得到的数据:download_file.js
 
db.users.findOne({name: 'foo'}, function(err, user) {
    if(err) {
        return next(err);
    }
    user.visit_count += 1;
    var updates = {visit_count: user.visit_count};
    db.users.update({_id: user._id}, {$set: updates}, 
            function(err) {
        if(err) {
            return next(err);
        }
        res.render('profile', {user: user});
    });
});var counter = 2, post = null, comments = null;
function handle() {
    if(--counter === 0) {
        callback(post, comments);
    }
};
db.posts.findOne({_id: post_id}, function(err, p) {
    post = p;
    handle();
});
db.comments.find({pid: post_id}).toArray(function(err, cms) {
    comments = cms;
    handle();
});
foo(bar, function(err, data) {
    // ... callback again again and again
    // ...
                            })
                    // ...
                })
            })
        })
    })
});var ep = new EventProxy();
ep.assgin('user', 'post', 'comments', function(user, post, comments) {
    callback(user, post, comments);
});
ep.on('user', function(user) {
    db.users.update(...);
});
db.users.findOne({name: 'foo'}, function(err, user) {
    ep.emit('user', user);
});
db.posts.findOne({_id: post_id}, function(err, post) {
    ep.emit('post', post);
});
db.comments.find({pid: post_id})
        .toArray(function(err, comments) {
    ep.emit('comments', comments);
});/
#