說起連接池,可能很多人直接望而卻步,覺得好複雜,還是直接調用別人現成的好了,其實都是連接池也是扮豬喫老虎。
但是之前看redis-py連接池的實現,覺得好簡單,但是卻又很巧妙,正好最近又在研究redis-py的連接池,將連接池相關的實現抽象出來,接單說明一下。
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-py連接池的關鍵表達出來了。
連接池內部維護兩個集合,一個可用的連接集合,一個在用的連接集合,當上層獲取連接的時候,從可用集合拿出一個返回,同時加到在用集合。
還有一個很巧妙的細節,就是連接在傳遞數據出錯的時候,不是銷燬連接對象,而是銷燬連接對象內部的socket。因爲連接對象一旦實例化就沒有足夠的理由去銷燬它,即使出錯也是socket連接出錯,銷燬socket就好了,避免了連接對象的頻繁生成、銷燬。