redis 连接池原理浅析

1.连接池的使用

redis_pool = redis.ConnectionPool(host='localhost', port=6379, db=0) //连接池的实例化
r = redis.StrictRedis(connection_pool=redis_pool) //从连接池池取出连接

从上面的代码我们可以看到,第一步是连接池的实例化。

2.连接池实例化
接下来,我们可以通过ConnectionPool实例化的源码看出,ConnectionPool实例化时,做了什么?

def __init__(self, connection_class=Connection, max_connections=None,**connection_kwargs):

        max_connections = max_connections or 2 ** 31
        if not isinstance(max_connections, (int, long)) or max_connections < 0:
            raise ValueError('"max_connections" must be a positive integer')

        self.connection_class = connection_class
        self.connection_kwargs = connection_kwargs
        self.max_connections = max_connections

        self.reset()

从代码中我们可以看出来,在连接池实例化的时候,并没有实际的去建立redis连接,只是设置了一些与连接池有关的参数:连接类,连接参数以及最大连接数。

3.连接实例化
我们知道,在连接池实例化的时候,并没有建立连接。这样,我们可以继续去看在连接实例化的时候,又做了些什么?

def __init__(self,...connection_pool=None...):
     if not connection_pool:
         ...
         connection_pool = ConnectionPool(**kwargs)
     self.connection_pool = connection_pool

从上面的代码我们可以看出来,如果我们在建立连接之前并没有创建连接池,StrictRedis也会帮我们创建一个连接池。但是知道这里,依然是没有真实的连接被建立。

4.连接池对连接的管理。
从上面我们可以知道,在连接池以及连接被实例化时,真实的连接一直没有被建立。下面我们不妨看看,当使用连接操作数据时,连接是怎样被建立,取出以及释放的。可以以set操作为例。
下面是set操作执行的源码。

 def set(self, name, value, ex=None, px=None, nx=False, xx=False):

        ...
        return self.execute_command('SET', *pieces)

我们可以继续去看execute_command()函数。

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)

上面的代码,可以挑重点来看。第一个是连接池的赋值操作,然后就是pool.get_connection(command_name, **options)这个是从pool里取出连接,还有一个pool.release(connection)这个是连接使用完成之后释放连接。

我们可以先看一下get_connection()函数,看看是怎么获取连接的。
get_connection()函数:

 def get_connection(self, command_name, *keys, **options):
        "Get a connection from the pool"
        self._checkpid()
        try:
            connection = self._available_connections.pop()
        except IndexError:
            connection = self.make_connection()
        self._in_use_connections.add(connection)
        return connection

通过上面的代码,我们可以看到从连接池获取连接其实就是从一个叫_available_connections的列表里弹出一个连接。弹出失败的话,就用make_connection方法创建一个连接。连接获取成功后,就将该连接放进一个名为_in_use_connections的列表里。

在获取连接时,可能需要创建一个新的连接。我们可以继续看一下创建连接make_connection()函数的源码。
make_connection()函数:

 def make_connection(self):
        "Create a new connection"
        if self._created_connections >= self.max_connections:
            raise ConnectionError("Too many connections")
        self._created_connections += 1
        return self.connection_class(**self.connection_kwargs)

在创建连接时,有一个最大连接数的控制。

连接获取完毕之后,就可以进行相应的操作了。在连接使用完毕之后,我们需要用release()函数来释放连接。
release()函数:

def release(self, connection):
        "Releases the connection back to the pool"
        self._checkpid()
        if connection.pid != self.pid:
            return
        self._in_use_connections.remove(connection)
        self._available_connections.append(connection)

这边我们可以看到,连接的释放其实就是将连接从_in_use_connections列表里移除,并添加到_available_connections列表里。

通过上面我们可以看到,ConnectionPool中有两个列表,一个是_available_connections,这里面存放着可以被取出使用的连接。还有一个是_in_use_connections,这里面存放着正在使用的连接。连接的获取以及释放总的来说就是将连接从一个list移到另一个list中去。

可以将连接的获取以及释放的过程总结为:
从_available_connections列表中获取可用连接。
如果获取失败,在小于最大连接数的情况下,可以重新创建一个连接。
将连接放进_in_use_connections正在使用的连接列表中
连接使用完毕,将连接从_in_use_connections列表中移除,添加进入_available_connections列表中。

你可能感兴趣的:(redis)