伸缩扩展Node.JS应用

伸缩扩展Node.js应用是一个具有挑战性的任务,Javascript的单线程特性会阻止你的Node使用现代多核应用,比如下面一个基本Http服务器,这个代码无论是在单核还是多核上都是运行在一个线程中。



var http = require("http");
var port = parseInt(process.argv[2]);

http.createServer(function(request, response) {
console.log(
"Request for: " + request.url);
response.writeHead(200);
response.end(
"hello world\n");
}).listen(port);

跨多核扩展
只要稍微做些修改,这段代码就能充分利用多核特点,下面是使用cluster模块进行优化,Cluster允许你方便地创建一个分享端口的进程网络,在这个例子中,每个核对应单独进程,是由numCPU定义,每个子进程然后实现Http服务器,通过监听分享的端口。


var cluster = require("cluster");
var http = require(
"http");
var numCPUs = require(
"os").cpus().length;
var port = parseInt(process.argv[2]);

if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}

cluster.on(
"exit", function(worker, code, signal) {
cluster.fork();
});
} else {
http.createServer(function(request, response) {
console.log(
"Request for: " + request.url);
response.writeHead(200);
response.end(
"hello world\n");
}).listen(port);
}

跨多台机器扩展
上面实现了一个机器内跨多核的扩展,下面看看如何跨服务器横向扩展。
扩展多个服务器是通过使用一个反向代理服务器,对进来的请求实现负载平衡。

Nodejitsu 已经开发了node-http-proxy,这是一个NodeJS的开源代理服务器,这个模块通过如下命令安装:


npm install http-proxy

下面是使用案例代码,在这个例子中哦哦那个,负载是在本地服务器两个端口之间进行平衡,在测试反向代理之前,确保原来的Http服务器运行在端口8080和8081之间,下一步,加载反向代理,然后就可以用浏览器连接它,如果正常,你会看到请求在两台服务器之间分发。



var proxyServer = require('http-proxy');
var port = parseInt(process.argv[2]);
var servers = [
{
host: "localhost",
port: 8081
},
{
host:
"localhost",
port: 8080
}
];

proxyServer.createServer(function (req, res, proxy) {
var target = servers.shift();

proxy.proxyRequest(req, res, target);
servers.push(target);
}).listen(port);

当然,这个案例只是使用了一台服务器,如果你有多台服务器,让你的反向代理服务器运行在一台服务器上,而使用另外一台或几台运行Http服务器。

使用Nginx扩展
使用反向代理当然好,因为它让你的整个软件堆栈都是相同的技术。然而,在生产系统中,更普遍的是使用nginx作为处理负载平衡。 nginx的是一个开放源码的HTTP和反向代理服务器是,它服务于静态文件如CSS和HTML都非常好。因此,nginx可以用来缓存和服务于您的网站的静态内容,同时转发动态内容到Node服务器的请求。

下面是Nginx作为反向代理的配置:


user nobody;
worker_processes 1;

error_log logs/error.log;
error_log logs/error.log notice;
error_log logs/error.log info;

pid logs/nginx.pid;


events {
worker_connections 1024;
}


http {
include mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent
"$http_referer" '
# '
"$http_user_agent" "$http_x_forwarded_for"';

access_log logs/access.log main;

sendfile on;
tcp_nopush on;

keepalive_timeout 0;
keepalive_timeout 65;

gzip on;

upstream node_app {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}

server {
listen 80;
server_name localhost;

charset koi8-r;

access_log logs/host.access.log main;

location / {
root html;
index index.html index.htm;
}

location /foo {
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Connection
"";
proxy_http_version 1.1;
proxy_pass http:
//node_app;
}

error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

}

其中配置了upstream 服务器名称为node_app,在两个IP之间平衡:


upstream node_app {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}

然后定义一下路由,上面配置中/foo就是一个将请求分发到 node_app。具体配置可见有关Nginx反向代理配置。