go-common-pool设计原理分析

common-pool:git

对于一些对象的频繁建立会带来很大的系统开销,而且须要对对象数量进行控制来下降资源消耗,好比数据库链接,线程等github

common-pool采用了缓存思想来解决这个问题,预先把一些对象资源建立好并统一保存起来,也就是保存到逻辑上的对象池中数据库

等到须要对象时从池中直接获取,不须要时归还到池中apache

目前对象池技术已经有不少开源优秀的库了,好比:Java实现的Apache Commons PoolGo Commons Pool缓存

go-common-pool就是参照Apache Commons Pool的思想,用go语言实现的一个通用对象池安全

 

原理分析数据结构

几个重要的数据结构介绍socket

factory:spa

包含了一个通用对象经常使用的方法集合,能够理解为一个接口,client能够根据不一样的对象特性自定义操做线程

 

ObjectPool:

主要包含了对象池的一些属性方法 

idleObjects是一个双向队列结构,保存一些可用对象,能够用FIFO、LIFO方式访问对象

allObjectsy是一个map结构,根据key-value形式保存对象,主要用来校验对象是否存在合法

也就是说一个对象在对象池中保存两份数据,对象池会启动一个协程定时维护对象

 

config:

包含对象池的一些配置

 

初始化过程

启动一个go run去定时维护对象池中的对象集合,主要分如下几步

第一步:检查空闲对象集合剔除即将失效或者已经失效对象(经过factory.Validate()判断),不会全量检查只会检查必定数量或者比例的空闲对象(数量可配置)

第二步:遍历allObjects集合中的全部对象,剔除失效对象

第三步:判断idleObjectsCount是否大于MinIdleCount,若是小于则建立对象,并往空闲对象队列里添加节点,直到两者相等

疑问:两者是同一个分支执行过程,既然有了第一部分的检查,不知道第二步存在的意义是什么。

 

获取对象:BorrowObject

首先会去idleObjects队列里面获取一个对象,若是为空说明队列里面没有现成的对象,则去建立一个新的对象

在建立对象的过程当中会先更新createCount,而后判断createCount是否大于MaxTotal

若是小于则继续建立对象,调用factory.Make()建立真正的对象,更新allObjects集合,若是建立成功返回新对象,若是失败返回nil

若是大于则说明对象池容量已经达到最大值,

这时候有两种选择,一种是返回错误,一种是先阻塞当前协程,等到其余协程归还对象后发送一个信号,唤醒当前协程最终获取到对象

第二种方式中支持两个设置:

一种是无限等待时间直到有错误产生,监听一种channel便可

一种是指定超时时间,超时后返回空对象和错误,须要用select监听两个channel,除了与其余协程通讯channel以外,还须要一个Timer.C channel时间超时channel

伪代码能够简化为:

func takeWhithTimeout(){
  for a:=next();a==nil;a=next() { if 没有剩余时间了 return nil if 能够终止了 return nil a.f()   }
  return a
} func (a Node) f() {   select {     case <-a.ch: //通知channel,获取到通知说明其队列里已经有了可用对象了,同时计算一下执行当前case后还有多少时间超时       return 剩余时间     case <-Time.After(timeout): //时间超时channel       return 超时了能够终止了   } }

  

归还对象:ReturnObject

1. 去allObjects去检查归还对象是否合法存在

2. 校验归还对象是否有效,调用factory.Validate()

3. 判断idleObjectsCount是否大于MaxIdleCount,若是小于则添加对象到idleObjects队列,不然销毁对象

 

总结:

go-common-pool对象池中还有不少细节没有体现出来,上面只有对整个对象池几个重要的操做进行了一个简单版的描述,后续会更新一些细节实现上的技巧

在这里我想总结下我的认为比较重要的几点

1. 一个对象的操做make(建立)-->active(激活)-->validate(校验)-->destory(销毁)-->passivate(钝化)

   建立、校验操做是很容易理解也是很经常使用的两个操做,通常对象定义好这两个操做就能够了。

   激活、钝化不容易理解,这两个操做是相对的,一个是初始化对象,一个是反初始化对象,消除以前对象占用的一些资源,

   具体须要结合应用场景,不一样的对象类型好比数据库链接,socket链接,线程对应不一样的操做,在翻阅了一些资料后还没找到一个很好的解释。

2. poolObject是整个对象池很核心的一个对象,对外链接factory的对象操做,对内链接allObjects、idleObjects集合的操做

    idleObjects/allObjects都是经过锁机制来保证线程安全操做的

    几个重要事件:

    一个对象加入idleObjects时须要进行(钝化)操做

    一个对象在获取时须要进行(激活-->校验)操做

    一个对象在归还时须要进行(校验-->钝化)操做

    其中任何一步失败都须要销毁对象

3. 获取对象时支持阻塞非阻塞两种方式 

相关文章
相关标签/搜索