python socketpool:通用连接池
简介
在软件开发中经常要管理各种“连接”资源,通常我们会使用对应的连接池来管理,比如mysql数据库连接可以用sqlalchemy中的池来管理,thrift连接可以通过thriftpool管理,redis-py中的StrictRedis实现本身就是基于连接池的,等等。 而今天介绍的socketpool是一个通用的python连接池库,通过它可以实现任意类型连接的管理,虽然不是很完美,但在一些找不到合适连接池实现、而又不想自己造轮子的时候使用起来会节省很多精力。
内部实现要点
- 这个类库的代码其实并不是特别的漂亮,但结构设计的不错,关键留下了对拓展开放的钩子,能让使用者根据自己的需要定制自己的连接池
- 内部主要的组件有ConnectionPool,Connector和backend_mod三个
- ConnectionPool实现了一个连接池的通用逻辑,用一个优先级队列管理所有连接,另外支持connection的生命周期定制,有一个reap机制(可选),基本思想是每个conn有一个最大生命周期,比如600秒,过了这个时间,就必须回收掉,reap线程(也有可能是greenlet或eventlet)定期检查过期的conn并进行回收
- Connector是一个接口,它可以看做是一个制造conn的工厂,ConnectionPool在需要新建conn的时候,会通过这个工厂来生成conn。所以我们只要实现Connector的接口方法就可以定制一个自己的连接工厂
- backend_mod是为了支持不同的线程模型(比如python原生线程,gevent或者eventlet)抽象出来的后端模块,它统一封装了Socket, PriorityQueue, Semaphore等和并发模型相关的组件,在创造ConnectionPool对象时可以通过参数控制选用哪种backend
部分代码阅读
ConnectionPool的初始化函数
def __init__(self, factory,
retry_max=3, retry_delay=.1,
timeout=-1, max_lifetime=600.,
max_size=10, options=None,
reap_connections=True, reap_delay=1,
backend="thread"): if isinstance(backend, str):
self.backend_mod = load_backend(backend)
self.backend = backend
else:
self.backend_mod = backend
self.backend = str(getattr(backend, '__name__', backend))
self.max_size = max_size
self.pool = getattr(self.backend_mod, 'PriorityQueue')()
self._free_conns = 0
self.factory = factory
self.retry_max = retry_max
self.retry_delay = retry_delay
self.timeout = timeout
self.max_lifetime = max_lifetime
if options is None:
self.options = {"backend_mod": self.backend_mod,
"pool": self}
else:
self.options = options
self.options["backend_mod"] = self.backend_mod
self.options["pool"] = self # bounded semaphore to make self._alive 'safe'
self._sem = self.backend_mod.Semaphore(1) self._reaper = None
if reap_connections:
self.reap_delay = reap_delay
self.start_reaper()
这里几个参数的意义:
- factory是类对象,需要实现Connector接口,用来生成conn,options是调用factory时传入的参数
- retry_max是获取conn时如果出错最多重试几次
- max_lifetime是规定每个conn最大生命时间,见上面说的reap机制
- max_size是这个pool的大小上限
- backend是线程模型
- reap_connections控制是否启用reap机制
被启动的reap就是一个单独的线程,定时调用下面的方法把过期的conn回收掉:
def murder_connections(self):
current_pool_size = self.pool.qsize()
if current_pool_size > 0:
for priority, candidate in self.pool:
current_pool_size -= 1
if not self.too_old(candidate):
self.pool.put((priority, candidate))
else:
self._reap_connection(candidate)
if current_pool_size <= 0:
break
_reap_connection最终会回调conn对象的invalidate方法(Connector的接口)进行销毁。每次使用完conn后会调用release_connection, 它的逻辑是
def release_connection(self, conn):
if self._reaper is not None:
self._reaper.ensure_started() with self._sem:
if self.pool.qsize() < self.max_size:
connected = conn.is_connected()
if connected and not self.too_old(conn):
self.pool.put((conn.get_lifetime(), conn))
else:
self._reap_connection(conn)
else:
self._reap_connection(conn)
如果连接还没过期或断开,就会被重新放入优先级队列中,用户可以通过实现Connector接口的get_lifetime来控制这里放回的conn的优先级,priority最小的conn下次会被优先取出
Connector定义了哪些接口呢?
class Connector(object):
def matches(self, **match_options):
raise NotImplementedError() def is_connected(self):
raise NotImplementedError() def handle_exception(self, exception):
raise NotImplementedError() def get_lifetime(self):
raise NotImplementedError() def invalidate(self):
raise NotImplementedError()
matches方法主要用在pool取出一个conn时,除了优先选择priority最小的conn,还需要这个conn和get(**options)传入的参数match,这个match就是回调conn的matches方法。其他几个接口前面都涉及到了。
TcpConnector实现
来看一下socketpool自带的TcpConnector的实现,实现tcp socket的工厂
class TcpConnector(Connector): def __init__(self, host, port, backend_mod, pool=None):
self._s = backend_mod.Socket(socket.AF_INET, socket.SOCK_STREAM)
self._s.connect((host, port))
self.host = host
self.port = port
self.backend_mod = backend_mod
self._connected = True
# use a 'jiggle' value to make sure there is some
# randomization to expiry, to avoid many conns expiring very
# closely together.
self._life = time.time() - random.randint(0, 10)
self._pool = pool def __del__(self):
self.release() def matches(self, **match_options):
target_host = match_options.get('host')
target_port = match_options.get('port')
return target_host == self.host and target_port == self.port def is_connected(self):
if self._connected:
return util.is_connected(self._s)
return False def handle_exception(self, exception):
print('got an exception')
print(str(exception)) def get_lifetime(self):
return self._life def invalidate(self):
self._s.close()
self._connected = False
self._life = -1 def release(self):
if self._pool is not None:
if self._connected:
self._pool.release_connection(self)
else:
self._pool = None def send(self, data):
return self._s.send(data) def recv(self, size=1024):
return self._s.recv(size)
不需要太多额外解释。
拓展实现HiveConnector
根据自身项目需要,我用pyhs2实现了一个hive连接池
class HiveConnector(Connector): def __init__(self, host, port, backend_mod, pool=None, authMechanism='NOSASL',
**options):
self.host = host
self.port = port
self.backend_mod = backend_mod
self._pool = pool
self._connected = False
self._conn = pyhs2.connect(host=host,
port=port,
authMechanism=authMechanism
)
self._connected = True
# use a 'jiggle' value to make sure there is some
# randomization to expiry, to avoid many conns expiring very
# closely together.
self._life = time.time() - random.randint(0, 10) def __del__(self):
self.release() def matches(self, **match_options):
target_host = match_options.get('host')
target_port = match_options.get('port')
return target_host == self.host and target_port == self.port def is_connected(self):
return self._connected def handle_exception(self, exception):
logger.exception("error: %s" % str(exception)) def get_lifetime(self):
return self._life def invalidate(self):
try:
self._conn.close()
except:
pass
finally:
self._connected = False
self._life = -1 def release(self):
if self._pool is not None:
if self._connected:
self._pool.release_connection(self)
else:
self._pool = None def cursor(self):
return self._conn.cursor() def execute(self, hql):
with self.curosr() as cur:
return cur.execute(hql) hive_pool = ConnectionPool(factory=HiveConnector, **HIVE_CONNECTOR_CONFIG)
使用这个hive_pool去执行hql语句非常容易:
with hive_pool.connection() as conn:
with conn.cursor() as cur:
print cur.getDatabases()
总结
简绍了socketpool的内部实现,以及如何使用它构造自己的连接池。
python socketpool:通用连接池的更多相关文章
- python redis之连接池的原理
python redis之连接池的原理 转载地址 什么是连接池 通常情况下, 当我们需要做redis操作时, 会创建一个连接, 并基于这个连接进行redis操作, 操作完成后, 释放连接, 一般情况下 ...
- python socketpool:通用连接池(转)
简介 在软件开发中经常要管理各种“连接”资源,通常我们会使用对应的连接池来管理,比如mysql数据库连接可以用sqlalchemy中的池来管理,thrift连接可以通过thriftpool管理,red ...
- Golang 通用连接池库 Golang-Pool
Golang 实现的连接池 功能: * 连接池中连接类型为interface{},使得更加通用 * 链接的最大空闲时间,超时的链接将关闭丢弃,可避免空闲时链接自动失效问题 * 使用channel处理池 ...
- python 链接mysql 连接池
# python 链接mysqlimport mysql.connector.poolingconfig = { "host":"localhost", &qu ...
- 用python自定义实现db2的连接池
想要模仿zabbix的oracle插件orabix来实现对db2的监控,但是Java能力有限,就用python来实现了.但是python常用的连接池PooledDB似乎并不支持db2,一直报这样的错误 ...
- Swoole 实战:MySQL 查询器的实现(协程连接池版)
目录 需求分析 使用示例 模块设计 UML 类图 入口 事务 连接池 连接 查询器的组装 总结 需求分析 本篇我们将通过 Swoole 实现一个自带连接池的 MySQL 查询器: 支持通过链式调用构造 ...
- python全栈开发day113-DBUtils(pymysql数据连接池)、Request管理上下文分析
1.DBUtils(pymysql数据连接池) import pymysql from DBUtils.PooledDB import PooledDB POOL = PooledDB( creato ...
- 利用python list 完成最简单的DB连接池
先来看查看效果: 在代码连接数据库后,并且执行三条sql后,将mysql直接重启掉,故我们的连接池连接均是不ok的,所以,它会全部删除再抓新的连接下来,重启mysql命令: 关于python代码: # ...
- python操作Redis安装、支持存储类型、普通连接、连接池
一.python操作redis安装和支持存储类型 安装redis模块 pip3 install redis 二.Python操作Redis之普通连接 redis-py提供两个类Redis和Strict ...
随机推荐
- 布隆过滤算法体会(BlooomFilter)
在一个m位的位数组里,一个字符串经过k次hash随机分布到k个位置. http://www.cnblogs.com/aspnethot/articles/3442813.html 布隆filter数据 ...
- 服务发现之consul理论整理_结合Docker+nginx+Tomcat简单部署案例
目录 一.理论概述 服务发现的概念简述 consul简述 二.部署docker+consul+Nginx案例 环境 部署 三.测试 四.总结 一.理论概述 服务发现的概念简述 在以前使用的是,N台机器 ...
- VSCode Git 没有活动的源代码控制提供程序
主要原因:我以前安装的Git只允许在Git-Bash中运行,需要重新安装Git,选择允许三方软件的那个选项,然后安装Git插件,修改git.path即可. 以下是解决过程中的尝试,记录如下,实际上只需 ...
- CentOS7.x安装nginx
1.安装先决条件 yum install yum-utils 2.设置yum存储库和创建/etc/yum.repos.d/nginx.repo 使用以下内容命名的文件 :稳定版 [nginx-stab ...
- IO五种模型和select与epoll工作原理(引入nginx)
用户速度体验的1-3-10原则 性能影响 有很多研究都表明,性能对用户的行为有很大的影响: 79%的用户表示不太可能再次打开一个缓慢的网站 47%的用户期望网页能在2秒钟以内加载 40%的用户 ...
- KVM虚拟机快照链创建,合并,删除及回滚研究
1 QEMU,KVM,libvirt关系 QEMU QEMU提供了一个开源的服务器全虚拟化解决方案,它可以使你在特定平台的物理机上模拟出其它平台的处理器,比如在X86 CPU上虚拟出Power的CPU ...
- WMware Workstation——时间和时区问题
对于学习Linux的人来说,最常见的做法就是在WMware Workstation下安装Linux系统.在WMware Workstation可能会有几处导致时间.时区问题. UTC时间导致时间不一致 ...
- 剑指Offer(三十一):整数中1出现的次数(从1到n整数中1出现的次数)
剑指Offer(三十一):整数中1出现的次数(从1到n整数中1出现的次数) 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https:// ...
- mysql 新手入门 官方文档+官方中文文档附地址
点评: 官方文档地址 官方中文文档地址 sql语句扩展
- 集成百度编辑器 ueditor 后端配置项没有正常加载,上传插件不能正常使用!
项目要用到编辑器,于是集成了ueditor,集成ok,但一直显示 ‘’后端配置项没有正常加载,上传插件不能正常使用!‘’ 各种查: 网上说的无非就是那么集中情况 1. 因为百度官方的问题,php/co ...