伸缩扩展Node.JS应用

14-04-15 banq

伸缩扩展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);
<p>

跨多核扩展

只要稍微做些修改,这段代码就能充分利用多核特点,下面是使用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);
}
<p>

跨多台机器扩展

上面实现了一个机器内跨多核的扩展,下面看看如何跨服务器横向扩展。

扩展多个服务器是通过使用一个反向代理服务器,对进来的请求实现负载平衡。

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

npm install http-proxy
<p>

下面是使用案例代码,在这个例子中哦哦那个,负载是在本地服务器两个端口之间进行平衡,在测试反向代理之前,确保原来的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);
<p>

当然,这个案例只是使用了一台服务器,如果你有多台服务器,让你的反向代理服务器运行在一台服务器上,而使用另外一台或几台运行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;
        }    
    }

}
<p>

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

upstream node_app {
  server 127.0.0.1:8080;
  server 127.0.0.1:8081;
}
<p>

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