greentor MySQL鏈接池實現

greentor MySQL鏈接池實現

https://en.wikipedia.org/wiki/Connection_poolpython

經過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

相關文章
相關標籤/搜索