【问题标题】:Redis multiple connections opened with TIME_WAIT stateRedis 多个连接以 TIME_WAIT 状态打开
【发布时间】:2016-10-15 06:26:18
【问题描述】:

我正在使用通用redis来存储来自近60个系统的数据。这个通用redis用于所有机器之间的通信。起初一切似乎都运行良好,但随着我将机器从 60 增加到 80,到 redis 的连接数增加了,之后它就不再接受任何连接,这导致了一个大问题。

当我开始使用 'netstat -na | 调试问题时grep 6379' 我发现每当我们连接到 redis 时,它都会在特定端口打开一个 TCP/IP 连接并且没有释放该连接,并且即使在调用 $redis->quit() 之后,该连接仍保持在 TIME_WAIT 状态近 60 秒(使用 PHP 的 predis 库)

我试图解决这个问题的方法:

1) 我尝试减少 /proc/sys/net/ipv4/tcp_fin_timeout 中的 timewait 秒数,但这不是正确的解决方案。

2)我从 PHP 转移到 nodejs 并尝试在 nodejs 中进行连接池但没有成功。这里是示例代码

app.get('/setinredis',function(req,res){

 var poolRedis = require('pool-redis')({
    'host': 'localhost',
    'password': '',
    'maxConnections': 5
    });

   poolRedis.getClient(function(client, done) {

        client.get('somekey', function(err, value) {
            console.log('value from redis is:', value);
            done();
            res.send({message:"Done"});
        });
    });

});

有没有什么方法可以有效地解决这个问题,或者有什么替代redis的数据结构功能。

任何帮助将不胜感激。

【问题讨论】:

  • 我听说你在predis库上使用了quit(),你试过$client->disconnect()吗?我用的是phpredis,你也可以用close()

标签: php node.js redis tcp-ip netstat


【解决方案1】:

问题是每次有人点击您的/setinredis 端点时,您都​​会创建一个池。这意味着如果您点击该端点 1000 次,您将创建 1000 个池。将池创建移到路由之外:

var poolRedis = require('pool-redis')({
    'host': 'localhost',
    'password': '',
    'maxConnections': 5
});

app.get('/setinredis',function(req,res){
   poolRedis.getClient(function(client, done) {
        client.get('somekey', function(err, value) {
            console.log('value from redis is:', value);
            done();
            res.send({message:"Done"});
        });
    });
});

编辑:作为旁注,您的问题令人困惑,因为它指的是 PHP,但代码在 Node.js 中。

【讨论】:

  • 也试过了,但仍然打开了多个处于 TIME_WAIT 状态的连接。
  • 使用 redis 管道,您不应该需要打开这么多套接字。处于 TIME_WAIT 中的套接字不一定是问题,只有当您耗尽所有可用端口时。
  • 不确定流水线是否可以在这里工作,因为我需要立即跟踪每个系统的响应。
  • 抱歉 - 我的意思是 node-redis 默认使用流水线,我简要阅读了 pool-redis 文档,它表明它基于 node-redis。您是否确认在更改此设置后仍然遇到问题?
  • 是的,我尝试了你所说的。我遇到的一件奇怪的事情是,即使两个客户端在 redis 中设置了值,那么当我使用 nodejs 时,10-15 个连接也在 TIME_WAIT 状态下打开,但这不是PHP 中的问题。
【解决方案2】:

请尝试这种方式,它应该可以正常工作。

=== 控制器.js ===

const user = require('./user')()

user.getUser('12345')
  .then(user => {
    console.log(user.xxxx)
  })

=== user.js ===

const redis = require('redis'),
      Promise = require('bluebird')

function getUser(id) {
  return redis.getAsync(id)
    .then(user => JSON.parse(user))
}

module.exports = () => {
  Promise.promisifyAll(redis.RedisClient.prototype)
  Promise.promisifyAll(redis.Multi.prototype)

  return new Promise((resolve, reject) => {
    const client = redis.createClient(REDIS_SERVER)
    client.on('connect', onConnect)
    client.on('error', err => {
      throw err
    })

    function onConnect() {
      return resolve({
        getUser
      })
    }
  })
}

【讨论】:

    【解决方案3】:

    这与 TCP fin 超时有关(Linux 中默认为 60 秒)。可能您没有重复使用连接,而是每次都打开一个新连接,这很好。但是,如果您这样做的速度如此之快,以至于您冒着使用所有可用端口的风险。您应该使用这个 sysctl 参数来减少 tcp fin 超时并启用 tcp_reuse 连接。

    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_fin_timeout = 10 #value is in seconds
    

    tcp_tw_reuse TCP 重用允许 Linux 内核从处于 TIME_WAIT 状态的连接中回收连接槽,并将其重新分配给新连接。重用套接字可以非常有效地减少服务器负载。

    tcp_fin_timeout 确定 TCP/IP 可以释放关闭的连接并重用其资源之前必须经过的时间。在此 TIME_WAIT 状态期间,重新打开与客户端的连接的成本低于建立新连接的成本

    参考:http://www.speedguide.net/articles/linux-broadband-tweaks-121

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-15
      • 1970-01-01
      • 1970-01-01
      • 2018-01-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多