python redis之鏈接池的原理

python redis之鏈接池的原理

 

轉載地址python

什麼是鏈接池

一般狀況下, 當咱們須要作redis操做時, 會建立一個鏈接, 並基於這個鏈接進行redis操做, 操做完成後, 釋放鏈接,redis

通常狀況下, 這是沒問題的, 但當併發量比較高的時候, 頻繁的鏈接建立和釋放對性能會有較高的影響併發

因而, 鏈接池就發揮做用了app

鏈接池的原理是, 經過預先建立多個鏈接, 當進行redis操做時, 直接獲取已經建立的鏈接進行操做, 並且操做完成後, 不會釋放, 用於後續的其餘redis操做性能

這樣就達到了避免頻繁的redis鏈接建立和釋放的目的, 從而提升性能了spa

 

原理

那麼, 在redis-py中, 他是怎麼進行鏈接池管理的呢code

鏈接池使用

首先看下如何進行鏈接池操做的對象

rdp = redis.ConnectionPool(host='127.0.0.1', port=6379, password='xxxxx') rdc = redis.StrictRedis(connection_pool=rdp) rdc.set('name', 'Yi_Zhi_Yu') rdc.get('name')

原理解析

當redis.ConnectionPool 實例化的時候, 作了什麼blog

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

這個鏈接池的實例化其實未作任何真實的redis鏈接, 僅僅是設置最大鏈接數, 鏈接參數和鏈接類rem

StrictRedis 實例化的時候, 又作了什麼

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

以上僅保留了關鍵部分代碼

能夠看出, 使用StrictRedis 即便不建立鏈接池, 他也會本身建立

到這裏, 咱們尚未看到什麼redis鏈接真實發生

繼續

下一步就是set 操做了, 很明顯, 這個時候必定會發生redis鏈接(要否則怎麼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)

終於, 在這咱們看到到了鏈接建立

connection = pool.get_connection(command_name, **options)

這裏調用的是ConnectionPool的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

若是有可用的鏈接, 獲取可用的連接, 若是沒有, 建立一個

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)

終於, 咱們看到了, 在這裏建立了鏈接

在ConnectionPool的實例中, 有兩個list, 依次是_available_connections_in_use_connections,

分別表示可用的鏈接集合正在使用的鏈接集合, 在上面的get_connection中, 咱們能夠看到獲取鏈接的過程是

  1. 從可用鏈接集合嘗試獲取鏈接,
  2. 若是獲取不到, 從新建立鏈接
  3. 將獲取到的鏈接添加到正在使用的鏈接集合

上面是往_in_use_connections裏添加鏈接的, 這種鏈接表示正在使用中, 那是何時將正在使用的鏈接放回到可用鏈接列表中的呢

這個仍是在execute_command裏, 咱們能夠看到在執行redis操做時, 在finally部分, 會執行一下

pool.release(connection)

鏈接池對象調用release方法, 將鏈接從_in_use_connections 放回 _available_connections, 這樣後續的鏈接獲取就能再次使用這個鏈接了

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)

總結

至此, 咱們把鏈接池的管理流程走了一遍, ConnectionPool經過管理可用鏈接列表(_available_connections) 和 正在使用的鏈接列表從而實現鏈接池管理

相關文章
相關標籤/搜索