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