【问题标题】:Redis Python Client opens many connectionsRedis Python 客户端打开许多连接
【发布时间】:2017-08-29 12:23:23
【问题描述】:

我正在使用以下代码连接 Redis 服务器。 我看到巨大的连接处于 TIME_WAIT 状态。有什么问题?

root@ubuntu:~$ netstat | grep :6479 | grep TIME_WAIT |wc -l
9061
root@ubuntu:~$ netstat | grep :6479 | grep ESTABLISHED |wc -l
7

我想在使用以下代码使用 Redis 服务器完成操作后关闭连接。但我收到了这个错误。

@staticmethod
def disconnectRedisConnection(r_server):
    if r_server is not None and r_server:
        r_server.connection.disconnect()

我收到以下错误,

r_server.connection.disconnect()
AttributeError: 'Redis' object has no attribute 'connection'

对巨大的 TIME_WAIT 连接/使用 Redis 完成操作后关闭连接有什么想法吗? 代码:

import threading
from time import sleep
import time, datetime
import traceback
import CACHE_CONST
import json
import os

import MySQLdb
import redis

# Static methods to interact with the Redis cache server
class CacheUtil(object):

    # Log Errors
    @staticmethod
    def log_message(msg):
        log_file = None
        log_file = open (os.path.abspath(CACHE_CONST.REDIS_LOG_FILE), "a")
        print(msg)
        if (log_file):
            message = time.strftime("%d-%m-%Y %H:%M:%S")    
            message += " :: " + str(msg)
            log_file.write(message + "\n")

    @staticmethod
    def saveToCache(hashName, hashValue):
        r_server = CacheUtil.getRedisConnection()
        r_server.hmset(hashName, hashValue)
        CacheUtil.disconnectRedisConnection(r_server)

    @staticmethod
    def getTrackerDetailsByID(trackerId):
        trackerDetail = None
        r_server = None
        hashName = "tDetails:" + str(trackerId)
        try:
            if trackerId is not None:
                print("getTrackerDetailsByID ")
                r_server = CacheUtil.getRedisConnection()
                trackerDetail = r_server.hgetall(hashName)
            else:
                CacheUtil.log_message("getDetailsByID failed with empty trackerId ")
        except:
            CacheUtil.log_message("getDetailsByID failed, ll fetch from DB " + str(traceback.format_exc()))
        finally:
            CacheUtil.disconnectRedisConnection(r_server)
        return trackerDetail

    @staticmethod
    def getRedisConnection():
        print("Get Redis Connection on Util ")
        r_server = redis.Redis(host=CACHE_CONST.REDIS_SERVER_URL, port=CACHE_CONST.REDIS_SERVER_PORT, db=0, password=CACHE_CONST.REDIS_PASS_PHRASE, socket_connect_timeout=2, socket_timeout=2)
        return r_server;

    @staticmethod
    def disconnectRedisConnection(r_server):
        if r_server is not None and r_server:
            r_server.connection.disconnect()

【问题讨论】:

    标签: python redis redis-py


    【解决方案1】:

    实际上,当您调用 redis.Redis() 时,它会为您创建具有连接池的“客户端”,而不仅仅是一个连接。

    每次发送 redis.set() 之类的命令时,它都会从其连接池中检索一个连接,并使用该连接发送该命令并等待回复。当请求完成后,它会将连接放回连接池以供重用。所以你不需要自己管理连接。查看此https://github.com/andymccurdy/redis-py 了解更多信息。

    就像这样:

    import threading
    from time import sleep
    import time, datetime
    import traceback
    import CACHE_CONST
    import json
    import os
    
    import MySQLdb
    import redis
    r_server = redis.Redis(host=CACHE_CONST.REDIS_SERVER_URL, port=CACHE_CONST.REDIS_SERVER_PORT, db=0, password=CACHE_CONST.REDIS_PASS_PHRASE, socket_connect_timeout=2, socket_timeout=2)
    
    # Static methods to interact with the Redis cache server
    class CacheUtil(object):
    
        # Log Errors
        @staticmethod
        def log_message(msg):
            log_file = None
            log_file = open (os.path.abspath(CACHE_CONST.REDIS_LOG_FILE), "a")
            print(msg)
            if (log_file):
                message = time.strftime("%d-%m-%Y %H:%M:%S")    
                message += " :: " + str(msg)
                log_file.write(message + "\n")
    
        @staticmethod
        def saveToCache(hashName, hashValue):
            r_server.hmset(hashName, hashValue)
    
        @staticmethod
        def getTrackerDetailsByID(trackerId):
            hashName = "tDetails:" + str(trackerId)
            try:
                if trackerId is not None:
                    print("getTrackerDetailsByID ")
                    trackerDetail = r_server.hgetall(hashName)
                else:
                    CacheUtil.log_message("getDetailsByID failed with empty trackerId ")
            except:
                CacheUtil.log_message("getDetailsByID failed, ll fetch from DB " + str(traceback.format_exc()))
            return trackerDetail
    

    更新

    每次使用redis实例发送命令,都会调用这个方法:

        # COMMAND EXECUTION AND PROTOCOL PARSING
    def execute_command(self, *args, **options):
        "Execute a command and return a parsed response"
        pool = self.connection_pool
        command_name = args[0]
        connection = pool.get_connection(command_name, **options)
        try:
            connection.send_command(*args)
            return self.parse_response(connection, command_name, **options)
        except (ConnectionError, TimeoutError) as e:
            connection.disconnect()
            if not connection.retry_on_timeout and isinstance(e, TimeoutError):
                raise
            connection.send_command(*args)
            return self.parse_response(connection, command_name, **options)
        finally:
            pool.release(connection)
    

    关于巨大的 TIME_WAIT 连接的任何想法/使用 Redis 完成操作后关闭连接

    这是一张关于 TCP 连接终止过程的图片。当客户端(Initiator)向服务器(Reciever)发送 ACK FIN 时,进入 Time_WAIT 状态。

    引用自 TCP/IP 图解第一卷的话:

    当 TCP 执行主动关闭并发送 最后的 ACK,该连接必须在 TIME_WAIT 状态下保持两倍的 MSL。这让 TCP 重新发送最终的 ACK,以防丢失。重新发送最终的 ACK 不是因为 TCP 重新传输 ACK (它们不消耗序号,也不被TCP重传),而是因为对方 将重新传输其 FIN(这确实消耗了一个序列号)。事实上,TCP 总是会重传 FIN 直到收到最终的 ACK。

    所以它会处于TIME_WAIT状态四分钟,之后会自动关闭连接。因为你经常打开新的tcp连接并关闭它,所以很多关闭的连接会处于TIME_WAIT状态。

    还有一篇关于the purpose about TIME_WAIT的更详细的文章

    【讨论】:

    • 感谢您的意见。还有一个查询,如果我停止使用此 CacheUtil 的 Python 代码,TIME_WAIT 连接将减少到数百个。当 Python 代码运行时,TIME_WAIT 连接数为 9061。TIME_WAIT 连接可能是什么问题?
    • 因为频繁发送命令请求,收到回复后关闭连接,Time_wait状态是Tcp byebye进程中的一个状态。它等待redis服务器发送关闭命令。
    • 时间等待连接数应与最近 4 分钟的流量相对应。参考这篇文章serverfault.com/questions/329845/…
    猜你喜欢
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-02
    • 2012-06-15
    • 1970-01-01
    • 2012-05-22
    相关资源
    最近更新 更多