在网络库nodejs中获取请求的客户端ip [英] get client ip of the request in net library nodejs

查看:1155
本文介绍了在网络库nodejs中获取请求的客户端ip的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在nginx后面的nodejs中使用粘性会话. 粘性会话通过检查连接的remoteAddress来进行负载平衡. 现在的问题是,它总是要使用nginx服务器的ip

 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;

解决方案

正如mef所指出的那样,sticky-session目前无法在反向代理后面工作,因为remoteAddress始终是相同的. 上述问题中的拉请求,以及早期的拉请求确实可以解决问题,尽管我尚未进行自我测试.

但是,这些修补程序依赖于部分解析数据包,进行低级路由,同时窥探更高级别的标头...正如对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配置和Node服务器的情况下,这会引入错误的可能性,并使动态扩展到例如环境中的内核数.

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


关于代理后面的nginx服务器的说明

在您提供的nginx配置中,您似乎已经使用了ngx_http_realip_module.虽然您没有在问题中明确提及此问题,但请注意,在nginx本身位于某种代理(例如代理)后面的情况下,这实际上可能是必要的. ELB.

然后需要real_ip_header指令以确保它是真实的客户端IP(例如X-Forwarded-For),而不是其他代理的IP,通过哈希运算可以选择要访问的端口.

在这种情况下,nginx实际上起到了与请求粘性会话的请求类似的目的:使用标头做出负载平衡决策,特别是确保始终使用相同的真实客户端IP定向到相同的过程.

当然,主要的区别在于,nginx作为专用的Web服务器,负载平衡器和反向代理,旨在完全执行这些类型的操作.解析和操作协议栈的不同层是它的基础.更重要的是,虽然目前尚不清楚有多少人实际使用过这些请求请求,但nginx稳定,维护良好并且几乎在任何地方都可以使用.

I am using sticky session in nodejs which is behind nginx. Sticky session does the load balancing by checking the remoteAddress of the connection. Now the problem is it always take ip of nginx server

 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);
});

Can we get the client ip using net library?

Nginx Configuration:

 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;

解决方案

As mef points out, sticky-session doesn't, at present, work behind a reverse proxy, where remoteAddress is always the same. The pull request in the aforementioned issue, as well as an earlier pull request, might indeed solve the problem, though I haven't tested myself.

However, those fixes rely on partially parsing packets, doing low-level routing while peeking into headers at a higher level... As the comments on the pull requests indicate, they're unstable, depend on undocumented behavior, suffer from compatibility issues, might degrade performance, etc.

If you don't want to rely on experimental implementations like that, one alternative would be leaving load balancing entirely up to nginx, which can see the client's real IP and so keep sessions sticky. All you need is nginx's built-in ip_hash load balancing.

Your nginx configuration might then look something like this:

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;
    }
}

Now, to get this to work, your server code would also need to be modified somewhat:

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);
    });
}

The difference is that instead of using cluster to have all worker processes share a single port (load balanced by cluster itself), each worker gets its own port, and nginx can distribute load between the different ports to get to the different workers.

Since nginx chooses which port to go to based on the IP it gets from the client (or the X-Forwarded-For header in your case), all requests in the same session will always end up at the same process.

One major disadvantage of this method, of course, is that the number of workers becomes far less dynamic. If the ports are "hard-coded" in the nginx configuration, the Node server has to be sure to always listen to exactly those ports, no less and no more. In the absence of a good system for syncing the nginx config and the Node server, this introduces the possibility of error, and makes it somewhat more difficult to dynamically scale to e.g. the number of cores in an environment.

Then again, I imagine one could overcome this issue by either programmatically generating/updating the nginx configuration, so it always reflects the desired number of processes, or possibly by configuring a very high number of ports for nginx and then making Node workers each listen to multiple ports as needed (so you could still have exactly as many workers as there are cores). I have not, however, personally verified or tried implementing either of these methods so far.


Note regarding an nginx server behind a proxy

In the nginx configuration you provided, you seem to have made use of ngx_http_realip_module. While you made no explicit mention of this in the question, please note that this may in fact be necessary, in cases where nginx itself sits behind some kind of proxy, e.g. ELB.

The real_ip_header directive is then needed to ensure that it's the real client IP (in e.g. X-Forwarded-For), and not the other proxy's, that's hashed to choose which port to go to.

In such a case, nginx is actually serving a fairly similar purpose to what the pull requests for sticky-session attempted to accomplish: using headers to make the load balancing decisions, and specifically to make sure the same real client IP is always directed to the same process.

The key difference, of course, is that nginx, as a dedicated web server, load balancer and reverse proxy, is designed to do exactly these kinds of operations. Parsing and manipulating the different layers of the protocol stack is its bread and butter. Even more importantly, while it's not clear how many people have actually used these pull requests, nginx is stable, well-maintained and used virtually everywhere.

这篇关于在网络库nodejs中获取请求的客户端ip的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆