1. 程式人生 > >greentor MySQL連接池實現

greentor MySQL連接池實現

ack urn 如果 als 新建 mysql 會有 emc main

greentor MySQL連接池實現

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

通過greentor實現了pymysql在Tornado上異步調用的過程後發現,每次建立數據庫連接都會經過socket 3次握手,而每一次socket讀寫都會伴隨著greenlet的切換,以及ioloop的callback過程,雖然是異步了,但是IO性能並沒有提升,所以在研究了TorMySQL連接池的實現後,實現了greentor自己的連接池。

https://github.com/zhu327/greentor/blob/master/greentor/green.py

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等

https://github.com/zhu327/greentor/blob/master/greentor/mysql.py

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的開發,把之前準備實現的連接池寫了出來。

greentor MySQL連接池實現