在net library nodejs中获取请求的客户端ip

时间:2015-06-08 10:12:41

标签: node.js nginx socket.io

我在nginx后面的nodejs中使用粘性会话。 Sticky会话通过检查连接的remoteAddress来执行负载平衡。 现在的问题是它始终采取ip的nginx服务器

 server = net.createServer({ pauseOnConnect: true },function(c) {

  // Get int31 hash of ip
  var worker,
      ipHash = hash((c.remoteAddress || '').split(/\./g), seed);

  // Pass connection to worker
  worker = workers[ipHash % workers.length];
  worker.send('sticky-session:connection', c);
});

我们可以使用网络库获取客户端IP吗?

Nginx配置:

 server {
    listen       80 default_server;
    server_name  localhost;
    root         /usr/share/nginx/html;
    #auth_basic "Restricted";
#auth_basic_user_file /etc/nginx/.htpasswd;
    #charset koi8-r;

    #access_log  /var/log/nginx/host.access.log  main;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    location / {
   set_real_ip_from 0.0.0.0/0;
   real_ip_header X-Forwarded-For;
  real_ip_recursive on;
    proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://socket_nodes;
    proxy_read_timeout 3000;

2 个答案:

答案 0 :(得分:2)

正如mef指出的那样,粘性会话目前不在反向代理之后工作,其中remoteAddress始终是相同的。 上述问题中的pull request以及之前的pull request可能确实可以解决问题,但我没有对自己进行过测试。

但是,这些修复程序依赖于部分解析数据包,在更高级别查看标题时执行低级别路由...正如对pull请求的注释所示,它们不稳定,依赖于未记录的行为,遭受兼容性问题,可能会降低性能等。

如果你不想依赖这样的实验性实现,一种替代方案是将负载平衡完全保留到nginx,这可以看到客户端的真实IP,因此保持会话的粘性。您只需要nginx内置的ip_hash负载均衡。

您的nginx配置可能如下所示:

upstream socket_nodes {
    ip_hash;
    server 127.0.0.1:8000;
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
    server 127.0.0.1:8003;
    server 127.0.0.1:8004;
    server 127.0.0.1:8005;
    server 127.0.0.1:8006;
    server 127.0.0.1:8007;
}

server {
    listen       80 default_server;
    server_name  localhost;
    root         /usr/share/nginx/html;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    location / {
        # Note: Trusting all addresses like this means anyone
        # can pretend to have any address they want.
        # Only do this if you're absolutely certain only trusted
        # sources can reach nginx with requests to begin with.
        set_real_ip_from 0.0.0.0/0;

        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_pass http://socket_nodes;
        proxy_read_timeout 3000;
    }
}

现在,要使其正常工作,您的服务器代码也需要进行一些修改:

if (cluster.isMaster) {
    var STARTING_PORT = 8000;
    var NUMBER_OF_WORKERS = 8;

    for (var i = 0; i < NUMBER_OF_WORKERS; i++) {
        // Passing each worker its port number as an environment variable.
        cluster.fork({ port: STARTING_PORT + i });
    }

    cluster.on('exit', function(worker, code, signal) {
        // Create a new worker, log, or do whatever else you want. 
    });
}
else {
    server = http.createServer(app);

    // Socket.io initialization would go here.

    // process.env.port is the port passed to this worker by the master.
    server.listen(process.env.port, function(err) {
        if (err) { /* Error handling. */ }
        console.log("Server started on port", process.env.port);
    });
}

不同之处在于,不是使用集群让所有工作进程共享一个端口(由集群本身负载平衡),每个工作者都有自己的端口,nginx可以在不同端口之间分配负载以获取不同的工作人员

由于nginx根据从客户端获取的IP(或您的案例中的X-Forwarded-For标头)选择要转到哪个端口,因此同一会话中的所有请求将始终在同一进程中结束。 / p>

这种方法的一个主要缺点当然是工人的数量变得不那么动态了。如果端口在nginx配置中是“硬编码”的,那么节点服务器必须确保始终正确地监听这些端口,不能再少,也不能更多。在没有用于同步nginx配置和节点服务器的良好系统的情况下,这引入了错误的可能性,并且使得动态扩展到例如更加困难。环境中的核心数量。

然后再次,我想通过以编程方式生成/更新nginx配置可以克服这个问题,因此它总是反映所需的进程数,或者可能通过为nginx配置非常多的端口然后使Node工作者每个端口都根据需要监听多个端口(因此,您仍然可以拥有与核心一样多的工作人员)。但是,到目前为止,我还没有亲自验证或试图实施这两种方法。

关于代理服务器后面的nginx服务器的注意事项

在您提供的nginx配置中,您似乎已经使用了ngx_http_realip_module。虽然你在这个问题中没有明确提到这一点,但请注意,在nginx本身位于某种代理的情况下,例如,这可能是必要的。 ELB。

然后需要real_ip_header指令来确保它是真正的客户端IP(例如在X-Forwarded-For中),而不是其他代理,它可以选择要去哪个端口。

在这种情况下,nginx实际上提供了与粘性会话的pull请求尝试完成的目的非常相似的目的:使用头来做出负载平衡决策,特别是确保相同的真实客户端IP始终针对同一过程。

当然,关键的区别在于nginx作为专用的Web服务器,负载均衡器和反向代理,旨在完成这些类型的操作。解析和操纵协议栈的不同层是它的面包和黄油。更重要的是,虽然目前尚不清楚有多少人实际使用过这些拉动请求,但nginx稳定,维护良好且几乎无处不在。

答案 1 :(得分:0)

您正在使用的模块似乎还不支持反向代理源。

看看this Github issue,一些拉取请求似乎可以解决您的问题,因此您可以通过使用模块的分支来解决问题(您可以point at it on github from your package.json file。)