greentor MySQL鏈接池實現
經過greentor實現了pymysql在Tornado上異步調用的過程後發現,每次創建數據庫鏈接都會通過socket 3次握手,而每一次socket讀寫都會伴隨着greenlet的切換,以及ioloop的callback過程,雖然是異步了,可是IO性能並無提高,因此在研究了TorMySQL鏈接池的實現後,實現了greentor本身的鏈接池。mysql
https://github.com/zhu327/greentor/blob/master/greentor/green.pygit
class Pool(object): def __init__(self, max_size=32, wait_timeout=8, params={}): self._maxsize = max_size # 鏈接池最大鏈接數 self._conn_params = params # 鏈接參數 self._pool = deque(maxlen=self._maxsize) # 存儲鏈接的雙端隊列 self._wait = deque() # 等待獲取鏈接的callback self._wait_timeout = wait_timeout # 等待超時時間 self._count = 0 # 已建立的鏈接數 self._started = False # 鏈接池是否可用 self._ioloop = IOLoop.current() self._event = Event() # 鏈接池關閉時間,set該時間後,鏈接池全部的鏈接關閉 self._ioloop.add_future(spawn(self.start), lambda future: future) # 在greenlet中啓動鏈接池 def create_raw_conn(self): pass # 經過self._conn_params參數建立新鏈接,用於重寫 def init_pool(self): # 建立新的鏈接,並加入到鏈接池中 self._count += 1 conn = self.create_raw_conn() self._pool.append(conn) @property def size(self): # 可用的鏈接數 return len(self._pool) def get_conn(self): while 1: if self._pool: # 若是有可用鏈接,直接返回 return self._pool.popleft() elif self._count < self._maxsize: # 若是沒有可用鏈接,且建立的鏈接尚未達到最大鏈接數,則新建鏈接 self.init_pool() else: self.wait_conn() # 若是沒有可用鏈接,且以建立了最大鏈接數,則等待鏈接釋放 def wait_conn(self): timer = None child_gr = greenlet.getcurrent() main = child_gr.parent try: if self._wait_timeout: # 建立計時器,若是等待了超時則拋出異常 timer = Timeout(self._wait_timeout) timer.start() self._wait.append(child_gr.switch) main.switch() # 切換到父greenlet上,直到child_gr.switch被調用 except TimeoutException, e: raise Exception("timeout wait connections, connections size %s", self.size) finally: if timer: timer.cancel() def release(self, conn): self._pool.append(conn) # 釋放鏈接,從新加入鏈接池中 if self._wait: # 若是有等待的greenlet callback = self._wait.popleft() self._ioloop.add_callback(callback) # 在下次ioloop過程當中切換到等待獲取鏈接的greenlet def quit(self): # 關閉鏈接池 self._started = False self._event.set() def _close_all(self): for conn in tuple(self._pool): conn.close() self._pool = None def start(self): # self.init_pool() self._started = True self._event.wait() self._close_all()
這是一個通用鏈接池,經過繼承Pool
類,並重寫create_raw_conn
方法就可用實現一個簡單的鏈接池,好比mysql,memcache等github
https://github.com/zhu327/greentor/blob/master/greentor/mysql.pysql
from pymysql.connections import DEBUG, Connection class ConnectionPool(Pool): def __init__(self, max_size=32, keep_alive=7200, mysql_params={}): super(ConnectionPool, self).__init__(max_size=max_size, params=mysql_params) self._keep_alive = keep_alive # 爲避免鏈接自動斷開,配置鏈接ping週期 def create_raw_conn(self): conn = Connection(**self._conn_params) if self._keep_alive: self._ioloop.add_timeout(time.time()+self._keep_alive, self._ping, conn) return conn @green def _ping(self, conn): if conn in self._pool: self._pool.remove(conn) conn.ping() self.release(conn) self._ioloop.add_timeout(time.time()+self._keep_alive, self._ping, conn)
這裏實現了一個MySQL的鏈接池,MySQL默認會有一個8小時空閒,鏈接自動斷開的機制,爲了不鏈接池中出現無效鏈接,在設置了keep_alive
時間後會週期性的調用鏈接的ping
方法來保證鏈接的活躍,同時若是鏈接不在鏈接池中,說明鏈接正在被使用,就不須要再ping
了。數據庫
事隔三個月,在一位新認識的朋友提醒下,又撿起了greentor的開發,把以前準備實現的鏈接池寫了出來。app