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

相关文章
相关标签/搜索