我想让她变得更好
发布于 2 年前 作者 zzzs 1157 次浏览 来自 分享

没错,她就是一段代码: 一个简单的资源服务器。

// 伪代码
// ...
http.createServer(function (request, response) {
    let pathname = url.parse(request.url)['pathname'];
    let requestUrl = path.join(fileDir, pathname);
    fs.readFile(requestUrl, (err, data) => {
        if (err) {
            response.writeHead(404);
            response.end(err.message);
        } else {
            response.writeHead(200, {
                'Content-Type': 'text/html'
            });
            response.end(data);
        }
    });
}).listen(port);

大家也看到了,确实很简单的实现,但显然可以更好,下面就以这个思路开始吧!🤭

我想让她更快,性 能更好

像上面这样实现,典型的io操作,会先把文件的内容读取存于内存,等全部内容都存于内存时,才会响应输出。可以想象,当大量请求并发时,对于小文件还好,但文件较大时,内存开销就会很紧张。于是,这里改用stream - 流的形式来实现,这样的好处是可以做到读取文件的时候,就开始响应输出,也就能解决刚才的问题。响应的更快,内存的压力也下来了。伪代码如下:

// 伪代码
// ...
http.createServer(function (request, response) {
    let pathname = url.parse(request.url)['pathname'];
    let requestUrl = path.join(fileDir, pathname);
    let reader = fs.createReadStream(requestUrl);
    response.writeHead(200, {
        'Content-Type': 'text/html'
    });
    reader.pipe(response, { end: false });
    reader.on('end', function() {
        response.end();
    });
}).listen(port);

ab压测效果:ab -n 100 -c 50,请求文件40+M

前者 0.png 后者 1.png 可以看到效果还是很明显的,尽管并发才50,但后者性能也已经是前者的两倍多了。

我想让她情绪更稳定

前面性能已经得到了优化,看似可以了。但我们知道,bug总是改不完的😄,就像她每次来的时候突然发脾气,程序也会在不经意间挂掉。我们要做的就是让她更稳定。这里用到nodejs的进程管理(ps:强烈推荐大家看朴灵老师的《深入浅出Node.js》里玩转进程章节,写的很清晰,受益匪浅),简单说明下机制:主进程通过进程间通信来实现监控工作进程,监听端口,把请求句柄发送给工作进程,从而达到多个进程监听同个端口的效果。当发现工作进程异常时,也可选择kill该进程,并重新生成新的进程。下面具体是用cluster-集群来实现,能达到两个目的,一来可以更高效的利用cpu,而来当某个工作进程挂掉时,也能自动生成新的进程,这样就靠谱多了。伪代码如下:

if (cluster.isMaster) {
    console.log(`主进程 ${process.pid} 正在运行`);
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }
    cluster.on('exit', (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 已退出`);
        cluster.fork();
    });
} else {
    http.createServer(function (request, response) {
        let pathname = url.parse(request.url)['pathname'];
        pathname = pathname === '/' ? 'small.txt' : pathname;
        let requestUrl = path.join(fileDir, pathname);
        let reader = fs.createReadStream(requestUrl);
        response.writeHead(200, {
            'Content-Type': 'text/html'
        });
        reader.pipe(response, { end: false });
        reader.on('end', function() {
            response.end();
        });
    }).listen(port);
    console.log(`工作进程 ${process.pid} 已启动`);
}

2.png 可以看到,每次进程退出,都能重新启动新进程。这里有个细节就是要考虑平滑的启动新进程,不细讲。还有就是如果主进程挂了,工作进程也会失去管理,这里可以通过监听工作进程数量,日志等方式来保证其稳定性。

我想让她更好部署,要做就做全套

我们程序最后是要部署到服务器上的,就要有基本start,stop,restart功能,这里就直接用现在热门的pm2来处理了,当然也可以用shell通过监听工作进程的方式来实现。

本文到此已结束了,相信她应该比之前更好了。 希望大家有更多能让她更xxx的想法提出来,欢迎pr,欢迎纠错,共同进步。

github:https://github.com/zzzs/make-it-better

回到顶部