happybase 的连接池
Posted technologyDaily
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了happybase 的连接池相关的知识,希望对你有一定的参考价值。
前段时间有个项目使用 hbase 存储一些数据,使用 happybase 类库和 thrift server 交互。使用 happybase 连接池过程中遇到一些问题,就去看了下 happybase 连接池代码,找到了出错原因。
我们先来看下 happybase 的连接池是怎么使用的?
先看下 init 方法,最开始里面声明了几个变量,_lock 保存了一个线程锁,_queue 保存了一个线程安全的先入后出队列,大小是初始化的池子大小,用来存储 hbase 连接,_thread_connection 则保存了一个线程本地变量,具体用途下面会介绍。再就是初始化了 size 大小的连接,并且全部放到 _queue 中。init 最后手动调用了下 connection 方法拿一个连接,目的是什么呢?保证当根据 host 和 port 连接错误的时候,初始化连接池过程中会直接抛错,而不是等到实际用到一个连接的时候再抛错。
下面我们看下 connection 方法:
我们可以看到 connection 方法使用 contextlib.contextmanage 装饰器修饰了,限制了使用时候必须使用 with 关键词。在看连接具体获得代码前,我们先看下连接的 yield 和释放相关的代码。
可以看到拿到一个连接后,会 yield 出去,finally 里会把连接归还连接池,中间的 except 异常需要注意下,当某个连接在执行的时候出现问题时,会捕获异常,并 refresh 一个新的连接,保证最后 finally 归还给连接池的连接是可用的连接。except 捕获的异常必然是 with 代码内的,代码外的异常是无法捕获的,所以需要保证 with 代码块结束了,对连接的使用就结束了,不然就会出现多个线程占用同一个连接这种情况。类似 scan 操作,返回结果是生成器,最好转成 list 在 with 内部返回,不然直接返回生成器的话,with 代码外部遍历时候,其实还是在用这个连接,而其实 with 已结束,连接池就会认为连接已经用完了,会回收掉分配给其他的线程。
我们看个不正确的使用姿势:
这个使用姿势就不对,因为 scan 返回结果是一个generator,with 一结束,连接池认为你这个连接用完了,就会回收这个连接到池子里,再分配给其他线程。而你外部使用地方可能遍历生成器内容,还是会用到这个连接,就会出现多个线程使用一个连接的情况。正确做法是 with 内部将 generator 结果转成 list,保证 with 结束连接使用也结束。
下面看下连接的获得:
会首先获取 _thread_connections 线程本地变量的 current 属性,每个线程的 current 属性都是独立的。注意不同线程的 _thread_connections 都会指向同一个对象,因为这个变量在连接池初始化的时候就确定了。但是 python 的 thread_local 重写了 getattr 方法,里面会调用一个 patch 方法,保证每个线程 local 变量的设置和读取都是独立的。
下面就好理解了,如果连接为空,就去队列取一下,然后 set 到本地变量中。
对于 gevent patch 的服务器, thread local 变量会被 patch 成 gevent 的 local,保证每个协程的 local 都是独立的,所以可以和多线程一样正常使用。
为什么最后会需要加锁来设置线程本地变量,感兴趣的可以网上找下 Python issue1868 相关资料,具体原因会在下篇文章详细介绍。
以上是关于happybase 的连接池的主要内容,如果未能解决你的问题,请参考以下文章