happybase 的连接池

Posted technologyDaily

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了happybase 的连接池相关的知识,希望对你有一定的参考价值。

前段时间有个项目使用 hbase 存储一些数据,使用 happybase 类库和 thrift server 交互。使用 happybase 连接池过程中遇到一些问题,就去看了下 happybase 连接池代码,找到了出错原因。


我们先来看下 happybase 的连接池是怎么使用的?

happybase 的连接池

先看下 init 方法,最开始里面声明了几个变量,_lock 保存了一个线程锁,_queue 保存了一个线程安全的先入后出队列,大小是初始化的池子大小,用来存储 hbase 连接,_thread_connection 则保存了一个线程本地变量,具体用途下面会介绍。再就是初始化了 size 大小的连接,并且全部放到 _queue 中。init 最后手动调用了下 connection 方法拿一个连接,目的是什么呢?保证当根据 host 和 port 连接错误的时候,初始化连接池过程中会直接抛错,而不是等到实际用到一个连接的时候再抛错。


下面我们看下 connection 方法:


happybase 的连接池


 我们可以看到 connection 方法使用 contextlib.contextmanage 装饰器修饰了,限制了使用时候必须使用 with 关键词。在看连接具体获得代码前,我们先看下连接的 yield 和释放相关的代码。

happybase 的连接池

可以看到拿到一个连接后,会 yield 出去,finally 里会把连接归还连接池,中间的 except 异常需要注意下,当某个连接在执行的时候出现问题时,会捕获异常,并 refresh 一个新的连接,保证最后 finally 归还给连接池的连接是可用的连接。except 捕获的异常必然是 with 代码内的,代码外的异常是无法捕获的,所以需要保证 with 代码块结束了,对连接的使用就结束了,不然就会出现多个线程占用同一个连接这种情况。类似 scan 操作,返回结果是生成器,最好转成 list 在 with 内部返回,不然直接返回生成器的话,with 代码外部遍历时候,其实还是在用这个连接,而其实 with 已结束,连接池就会认为连接已经用完了,会回收掉分配给其他的线程。

我们看个不正确的使用姿势:

happybase 的连接池

这个使用姿势就不对,因为 scan 返回结果是一个generator,with 一结束,连接池认为你这个连接用完了,就会回收这个连接到池子里,再分配给其他线程。而你外部使用地方可能遍历生成器内容,还是会用到这个连接,就会出现多个线程使用一个连接的情况。正确做法是 with 内部将 generator 结果转成 list,保证 with 结束连接使用也结束。


下面看下连接的获得:

happybase 的连接池

会首先获取 _thread_connections 线程本地变量的 current 属性,每个线程的 current 属性都是独立的。注意不同线程的 _thread_connections 都会指向同一个对象,因为这个变量在连接池初始化的时候就确定了。但是 python 的 thread_local 重写了 getattr 方法,里面会调用一个 patch 方法,保证每个线程 local 变量的设置和读取都是独立的。
下面就好理解了,如果连接为空,就去队列取一下,然后 set 到本地变量中。

对于 gevent patch 的服务器, thread local 变量会被 patch 成 gevent 的 local,保证每个协程的 local 都是独立的,所以可以和多线程一样正常使用。


为什么最后会需要加锁来设置线程本地变量,感兴趣的可以网上找下 Python issue1868 相关资料,具体原因会在下篇文章详细介绍。






以上是关于happybase 的连接池的主要内容,如果未能解决你的问题,请参考以下文章

连接池技术的连接池的建立

连接池的基本原理? 以及使用连接池的好处?

数据库连接池的Java连接池

连接池

spring可以不用连接池吗

数据库连接池是啥