egg-cluster: sticky用connection.remoteAddress,如果前面有nginx怎么办

Created on 28 Feb 2018  ·  10Comments  ·  Source: eggjs/egg

egg-cluster: sticky用connection.remoteAddress,如果前面有nginx怎么办

need-more-information egg-socket.io

All 10 comments

@rachardking https://eggjs.org/zh-cn/tutorials/socketio.html#%E9%83%A8%E7%BD%B2 文档有说明 nginx 配置。

X-Forwarded-For header 可以拿到转发的 ip

@fengmk2
https://github.com/eggjs/egg-cluster/blob/master/lib/master.js#L151

require('net').createServer({ pauseOnConnect: true }, connection => {
      // We received a connection and need to pass it to the appropriate
      // worker. Get the worker for this connection's source IP and pass
      // it the connection.

      /* istanbul ignore next */
      if (!connection.remoteAddress) {
        connection.close();
      } else {
        const worker = this.stickyWorker(connection.remoteAddress);
        worker.send('sticky-session:connection', connection);
      }
    }).listen(this[REALPORT], cb);

因为启用的是net server, tcp 拿不到请求头的

配置的话参考文档,如果只是拿ip,可以看socket.io 文档

我也遇到这个问题,主要问题如下:
1.connection.remoteAddress拿到的是反向代理的ip,如果反向代理层只有一台,那么这个粘性会话基本只会发送到node的一个固定worker进程里,这样其实只有一个固定的worker在处理socket.io的请求。
2.如果用LVS+2台haproxy做反向代理集群,请求会被随机发送给2台haproxy中的1台进行转发,这样的话,同一个用户发起的请求,用connection.remoteAddress拿到的ip是不固定的,会导致无法建立粘性会话。
个人觉得问题在于connection.remoteAddress拿到不是用户的真实ip。请问这是我的配置问题吗?加了代理层,connection.remoteAddress能获取到用户的真实ip吗?

@fengmk2 试过了,拿到的IP都是127.0.0.1,根本拿不到真实IP,导致websocket的所有请求都负载到一个worker进程了。

@rachardking @marxy @zhuziyu

可以考虑试修改下egg-clusteregg-scripts 模块来获取用户ip。以下是修改的内容,代码不多。

修改文件一:egg-cluster/lib/master.js

  startMasterSocketServer(cb) {

    require('http').createServer((req, res) => {
      // 前提:通过nginx proxy_set_header 设置 x-forwarded-for
      let remoteAddress = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
      if (!remoteAddress) {
        req.socket.close();
      } else {
        const worker = this.stickyWorker(remoteAddress);
        worker.send('sticky-session:connection', req, res);
      }
    }).listen(this[REALPORT], cb);
  }

修改文件二:egg-cluster/lib/app_worker.js

    process.on('message', (message, req, res) => {
      if (message !== 'sticky-session:connection') {
        return;
      }
      // 这里
      server.emit('request', req, res);
    });

hack下 egg-scripts/index.js

可以考虑创建两个新包,比如 egg-scripts-x, egg-cluster-x,然后 修改 egg-script-x/index.js (对应原来的 egg-scripts/index.js)

// 加上这段代码,这样不用去改 egg 本身
let egg = require('egg');
egg.startCluster = require('egg-cluster-x').startCluster;

其他

刚接触egg,还没有来得及深入了解。大致看了下启动脚本和sticky模式的代码,只考虑HTTP服务的情况,上面代码应该可行。其他情况还没验证。是否有毒副作用还未仔细验证,如有请指出。

@chyingp 这个改法无法工作的。 socket.io 的 websocket 模式不是 http 协议,在这个地方处理是在 tcp 级别,而不是 http 级别。tcp 级别没有 HEADER 这个概念。所以是行不通的。

解决办法其实很简单,nginx 配置一下即可:https://github.com/eggjs/egg/blob/e904e48f250b09f0fc880782a9d400d9d43210d5/docs/source/zh-cn/tutorials/socketio.md#%E9%83%A8%E7%BD%B2

proxy_bind       $remote_addr transparent;

@ngot 是的,所以标注了“只考虑HTTP服务的情况”哈哈。

socket.io 连接阶段,不管transport 是 ajax-polling + ws,还是直接ws,上层都是HTTP协议,理论上应该是可行的的(未经验证)。

prxoy_bind 对权限要求有点高,非不得已不考虑。是否考虑支持支持下proxy_protocol?

proxy_bind $remote_addr transparent;

貌似这个配置也不行,用了之后,客户端会超时,查看nginx的日志,能看到websocket的连接的status code是499,应该是客户端等待连接,一直没反应,超时了后断了连接。

如果用文档上的,是comment掉了上面这个配置的,用这个配置:

location / {
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $host;
  proxy_pass   http://127.0.0.1:7001;

  # http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_bind
  # proxy_bind       $remote_addr transparent;
}

connection.remoteAddress取到的会是127.0.0.1,而不是源ip

我是在这行打印了connection.remoteAddress来检查这个值的取值的

是我配置的有问题么?还是这个有别的配置方式?

我们在生产环境下用docker打包运行eggjs + socket.io,在nginx的reverse proxy后面。因为这个remoteAddress取值的问题,导致几个进程中只有一个进程在承接流量,很快就服务不可用了。因为上面的问题还没找到解决方案,可能临时打算将worker设置成1,然后开多个container来跑,流量在nginx进行负载均衡,这样应该能解决一些现在的负载问题,但是并不是best practice,也有点hack,希望能有人指明一下egg-socket.io应该怎样配置在nginx后面

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kkys4bfgp75be9p picture kkys4bfgp75be9p  ·  36Comments

shaoshuai0102 picture shaoshuai0102  ·  52Comments

Yao-JSON picture Yao-JSON  ·  34Comments

musicode picture musicode  ·  55Comments

zrxisme picture zrxisme  ·  44Comments