Redis-py连接池的实现

Posted 魔笛手CTO

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis-py连接池的实现相关的知识,希望对你有一定的参考价值。


说起连接池,可能很多人直接望而却步,觉得好复杂,还是直接调用别人现成的好了,其实都是连接池也是扮猪吃老虎。

但是之前看redis-py连接池的实现,觉得好简单,但是却又很巧妙,正好最近又在研究redis-py的连接池,将连接池相关的实现抽象出来,简单说明一下。


通过类图可以看到,主要有三个类,Redis对外提供服务,内部维护一个ConnectionPool类,最底层的Connection负责与redis服务的通信。

理解了层级关系,现在我们看代码。

class Connection:    def __init(self, **db_info):        """初始化需要一些redis服务器的连接信息,此处以db_info代替"""        self._sock = None        pass                    def connect(self):        """供上层调用"""        if not self._sock:            self._connect()                    pass            def _connect(self):        """同redis服务器建立socket"""        self._sock = socket(**db_info)        def disconnect(self):        """关闭socket连接,此处很巧妙"""        """连接对象不会销毁,内部的socket会被销毁"""        self.socket = None            def send_command(self, **args):        if not self._socket:            self.connect()        self._sock.sendall(**args)            def read_response(self):        return self._sock.readall()        class ConnectionPool:    def __init__(self, **pool_config, **db_info):        """实例化需要连接池配置(如连接池大小),数据库连接信息"""        self.reset()            def reset(self):        """初始化内部维护的连接"""        self._available_connections = []        self._in_use_connections = set()        def make_connection(self):        """创建新的连接对象"""        conn = Connection(**db_info)            def get_connection(self):        """获取一条连接,供上层调用"""        if self._available_connections:            conn = self._available_connections        else:            conn = self.make_connection()                self._in_use_connections.add(conn)        return conn            def release(self, connection):        """释放单个连接对象到连接池"""        self._in_use_connections.remove(connection)        self._available_connections.append(connection)        def disconnect(self):        """将关闭所有在用的,可用的连接对象"""        all_conns = chain(self._available_connections,                          self._in_use_connections)        for connection in all_conns:            connection.disconnect()        class Redis:    """redis对象,实例化之后内部同时实例化一个连接池对象"""    def __init__(self, **pool_config, **db_info):        self.pool = ConnectionPool(**pool_config, **db_info)            def execute_command(data):        """从连接池取出一条连接,发送信息,释放给连接池"""        connection = self.pool.get_connection()        try:            connection.send_command(data)        except:            connection.disconnect()        pool.release(connection)

当然关于上面的代码遗漏了redis的通信协议和很多的防御代码 ,比如检测父子进程是否共用一个相同的描述符等,但是我觉的把redis-py连接池的关键表达出来了。


连接池内部维护两个集合,一个可用的连接集合,一个在用的连接集合,当上层获取连接的时候,从可用集合拿出一个返回,同时加到在用集合。


还有一个很巧妙的细节,就是连接在传递数据出错的时候,不是销毁连接对象,而是销毁连接对象内部的socket。因为连接对象一旦实例化就没有足够的理由去销毁它,即使出错也是socket连接出错,销毁socket就好了,避免了连接对象的频繁生成、销毁。


以上是关于Redis-py连接池的实现的主要内容,如果未能解决你的问题,请参考以下文章

安装redis-py并连接Redis服务器设置和获取redis的二进制数据

使用Python客户端(redis-py)连接Redis--华为云DCS for Redis使用经验

代理池的维护

Druid连接池的工具类以及简单代码实现

Swoole MySQL 连接池的实现

数据库连接池的选择 | 实现