vitess源码阅读笔记cache系列之用go实现通用资源池
更新至2012.4.8的vitess代码
新的代码增加了同步用的条件变量,没有空闲资源时的排队不再使用channel来同步(使用其它编程语言的同学可以方便的移植这个代码了),
转而使用condition variable。不再使用mu.RLock,统一使用Lock,不再纠结。 整体代码清晰了许多。
为了进一步提高性能和代码复用,vitess还提供了通用的池管理,RoundRobin.go中实现了通用的资源池,方便管理池内资源总数,超时。
type Factory func() (Resource, error) //工厂方法,资源创建函数,如connection
// Every resource needs to suport the Resource interface.
type Resource interface {
Close()
IsClosed() bool
}
这里要注意的是使用RoundRobin资源池来管理的对象必须实现Resouce接口 。
为什么要实现这两个接口呢?,因为资源池需要知道如何Close超时的资源,
以及哪些资源是空闲的,哪些已经关闭了。
type RoundRobin struct {
// mu controls resources & factory
// Use Lock to modify, RLock otherwise
mu sync.RWMutex
resources chan fifoWrapper //用chan来模拟一个FIFO的队列,先来先服务
factory Factory
// Use sync/atomic to access the following vars
size int64 //LQ: 池的总大小
waitCount int64 //LQ: 还有多少人在等池内出现空闲资源
waitTime int64 //LQ: 等待空闲资源总共花了多少时间
idleTimeout int64 //LQ: 最多允许资源空闲多长时间,超过则close空闲资源
}
type fifoWrapper struct {
resource Resource
timeUsed time.timeUsed //LQ: 用于控制超时,初始值为资源上次进入池内的时间(见Put函数)
}
抱歉都快到电视剧的第N集了才看到golang里面的重量级组件channel,简单的理解可以认为channel是个
支持生产者消费者模型的同步队列,而且是个不需要销毁的队列,golang会自动将不再使用的channel当垃圾回收掉。
不多说,上代码,详细内容见标注的代码注释
// has not been reached, it will create a new one using the factory. Otherwise,
// it will indefinitely wait till the next resource becomes available.
func (self *RoundRobin) Get() (resource Resource, err error) {
return self.get(true)
}
// TryGet will return the next available resource. If none is available, and capacity
// has not been reached, it will create a new one using the factory. Otherwise,
// it will return nil with no error.
func (self *RoundRobin) TryGet() (resource Resource, err error) {
return self.get(false)
}
func (self *RoundRobin) get(wait bool) (resource Resource, err error) {
self.mu.Lock()
defer self.mu.Unlock()
// Any waits in this loop will release the lock, and it will be
// reacquired before the waits return.
for {
select {
case fw := <-self.resources:
// Found a free resource in the channel
if self.idleTimeout > 0 && fw.timeUsed.Add(self.idleTimeout).Sub(time.Now()) < 0 {
// resource has been idle for too long. Discard & go for next.
go fw.resource.Close()
self.size--
continue
}
return fw.resource, nil
default:
// resource channel is empty
if self.size >= int64(cap(self.resources)) {
// The pool is full
if wait {
start := time.Now()
self.available.Wait() //没有空闲资源了,等着吧,不如上一版本的代码自然啊
self.recordWait(start)
continue
}
return nil, nil
}
// Pool is not full. Create a resource.
if resource, err = self.waitForCreate(); err == nil {
// Creation successful. Account for this by incrementing size.
self.size++
}
return resource, err
}
}
panic("unreachable")
}
func (self *RoundRobin) recordWait(start time.Time) {
self.waitCount++
self.waitTime += time.Now().Sub(start)
}
//LQ: 这里的increment和decrement应该是多余的,没看明白作者是什么目的,和惊群有啥关系
//为了避免self.factory()比较耗时,执行self.factory时unlock还是有必要的
func (self *RoundRobin) waitForCreate() (resource Resource, err error) {
// Prevent thundering herd: increment size before creating resource, and decrement after.
self.size++
self.mu.Unlock()
defer func() {
self.mu.Lock()
self.size--
}()
return self.factory()
}
在代码注释中可以看到,为了避免惊群效应,这里采用的方式是先increment,本人也不太明白,为什么这样能避免惊群效应。
看完了Get发现排队等待是那么的自然,一行代码的事情。再来看Put函数,我们会发现唤醒也是那么的简洁。
// Put will return a resource to the pool. You MUST return every resource to the pool,
// even if it's closed. If a resource is closed, Put will discard it. Thread synchronization
// between Close() and IsClosed() is the caller's responsibility.
func (self *RoundRobin) Put(resource Resource) {
self.mu.Lock()
defer self.mu.Unlock()
defer self.available.Signal() //LQ: 排队的兄弟该醒醒了
if self.size > int64(cap(self.resources)) {
go resource.Close()
self.size--
} else if resource.IsClosed() {
self.size--
} else {
self.resources <- fifoWrapper{resource, time.Now()}
}
}
转载于:https://www.cnblogs.com/gongaut/archive/2012/04/06/2434648.html
vitess源码阅读笔记cache系列之用go实现通用资源池相关推荐
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...
- syzkaller 源码阅读笔记1(syz-extract syz-sysgen)
文章目录 1. syz-extract 1-0 总结 1-1. `main()` 1-2 `archList()` - `1-1 (3)` 获取架构 name list 1-3 `createArch ...
- Transformers包tokenizer.encode()方法源码阅读笔记
Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode
- 源码阅读笔记 BiLSTM+CRF做NER任务 流程图
源码阅读笔记 BiLSTM+CRF做NER任务(二) 源码地址:https://github.com/ZhixiuYe/NER-pytorch 本篇正式进入源码的阅读,按照流程顺序,一一解剖. 一.流 ...
- 代码分析:NASM源码阅读笔记
NASM源码阅读笔记 NASM(Netwide Assembler)的使用文档和代码间的注释相当齐全,这给阅读源码 提供了很大的方便.按作者的说法,这是一个模块化的,可重用的x86汇编器, 而且能够被 ...
- Yii源码阅读笔记 - 日志组件
2015-03-09 一 By youngsterxyf 使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category); Yii: ...
- AQS源码阅读笔记(一)
AQS源码阅读笔记 先看下这个类张非常重要的一个静态内部类Node.如下: static final class Node {//表示当前节点以共享模式等待锁static final Node SHA ...
- 【Flink】Flink 源码阅读笔记(20)- Flink 基于 Mailbox 的线程模型
1.概述 转载:Flink 源码阅读笔记(20)- Flink 基于 Mailbox 的线程模型 相似文章:[Flink]Flink 基于 MailBox 实现的 StreamTask 线程模型 Fl ...
- 【Flink】Flink 源码阅读笔记(18)- Flink SQL 中的流和动态表
1.概述 转载:Flink 源码阅读笔记(18)- Flink SQL 中的流和动态表
最新文章
- 优化调整Oracle 8i数据库
- linux shell for while if case break continue echo test 及算术运算符 关系运算符 布尔运算符 逻辑运算符 字符串运算符 文件测试运算符
- 关于castle和Could not find the dialect in the configuration错误
- @autowired注解注入为null_Intellij IDEA中Mybatis Mapper自动注入警告的6种解决方案
- 20190501-编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串...
- 河北体检系统诚信企业推荐_海南膜结构停车棚加工厂诚信企业推荐
- python float 精度_改变Float的精度并在Python中存储
- virtual 初探
- 2021-2027全球与中国下一代测序数据分析市场现状及未来发展趋势
- 计算机图表公式,通达信的DRAWICON49个图标公式
- MapGuide应用开发系列(三)----MapGuide 数据包管理及Maestro亮点功能介绍
- 计算机网络经过了几个阶段,计算机网络的发展经过哪几个阶段?
- python 录屏_《自拍教程70》Python adb一键录屏
- 尚德机构2020年Q4财报:净收入5.85亿元,管理费用同比大幅下降近五成
- 软件工程网络工程第二次训练(AC代码和详细解释)(C语言描述)
- 卧槽,这也真的太上头了吧
- 如何在别的电脑上用自己的系统------在移动硬盘上装win10
- 2020移动apn接入点哪个快_为什么别人的4g网总比你快? 手机这个设置没开启, 难怪网络差...
- 自动化测试 | Selenium自动化测试框架,实战遇到的坑都在这了,玩转自动化测试
- [Android]如何做一个崩溃率少于千分之三噶应用app(15)-View分发module架构