更新日志: 

更新至2012.4.8的vitess代码

新的代码增加了同步用的条件变量,没有空闲资源时的排队不再使用channel来同步(使用其它编程语言的同学可以方便的移植这个代码了),

转而使用condition variable。不再使用mu.RLock,统一使用Lock,不再纠结。 整体代码清晰了许多。

为了进一步提高性能和代码复用,vitess还提供了通用的池管理,RoundRobin.go中实现了通用的资源池,方便管理池内资源总数,超时。

先上第一道菜:RoundRobin在整个vitess架构中扮演的什么角色?
个人觉得RoundRobin的重要性在vitess中怎么着也能算个丐帮的几个长老之一吧,作为其它几个pool的核心基础
如cache_pool.go就是RoundRobin + memcache client实现的,而conn_pool.go则是用RoundRobin实现的连接池。
先看看RoundRobin.go的基本原理,老规矩,还是从数据结构入手
// Factory is a function that can be used to create a resource.
type Factory func() (Resource, error)        //工厂方法,资源创建函数,如connection

// Every resource needs to suport the Resource interface.  
type Resource interface {    
    Close()
    IsClosed() bool
}

这里要注意的是使用RoundRobin资源池来管理的对象必须实现Resouce接口 。

为什么要实现这两个接口呢?,因为资源池需要知道如何Close超时的资源,

以及哪些资源是空闲的,哪些已经关闭了。

// RoundRobin allows you to use a pool of resources in a round robin fashion.
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当垃圾回收掉。

其实池也是生产者消费者模型。所以需要外部提供生产资源的方法,也就是上面的Factory接口。这里的可以简单理解为
c语言里面的函数指针。
RoundRobin的实现逻辑是这样的,如果池内有资源,则可以Get成功,如果没有,但还有容量,则用Factory创建一个资源。
如果池内已经没有空闲资源,则傻等,知道有空闲资源可用位置。为了了解资源池的运作状态,还记录了等待的客户有多少。
总共等了多少时间,有了这些就可以方便的评估RoundRobin的效果。源代码也是按照这个思路来写的。

不多说,上代码,详细内容见标注的代码注释

// Get 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 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()}
        }
}

接下来的系列将会分析其它依赖RoundRobin的几个池的实现,然后分析vitess的各种功能是如何实现的。休息,休息一会儿。

转载于:https://www.cnblogs.com/gongaut/archive/2012/04/06/2434648.html

vitess源码阅读笔记cache系列之用go实现通用资源池相关推荐

  1. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...

  2. 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 ...

  3. Transformers包tokenizer.encode()方法源码阅读笔记

    Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode

  4. 源码阅读笔记 BiLSTM+CRF做NER任务 流程图

    源码阅读笔记 BiLSTM+CRF做NER任务(二) 源码地址:https://github.com/ZhixiuYe/NER-pytorch 本篇正式进入源码的阅读,按照流程顺序,一一解剖. 一.流 ...

  5. 代码分析:NASM源码阅读笔记

    NASM源码阅读笔记 NASM(Netwide Assembler)的使用文档和代码间的注释相当齐全,这给阅读源码 提供了很大的方便.按作者的说法,这是一个模块化的,可重用的x86汇编器, 而且能够被 ...

  6. Yii源码阅读笔记 - 日志组件

    2015-03-09 一 By youngsterxyf 使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category); Yii: ...

  7. AQS源码阅读笔记(一)

    AQS源码阅读笔记 先看下这个类张非常重要的一个静态内部类Node.如下: static final class Node {//表示当前节点以共享模式等待锁static final Node SHA ...

  8. 【Flink】Flink 源码阅读笔记(20)- Flink 基于 Mailbox 的线程模型

    1.概述 转载:Flink 源码阅读笔记(20)- Flink 基于 Mailbox 的线程模型 相似文章:[Flink]Flink 基于 MailBox 实现的 StreamTask 线程模型 Fl ...

  9. 【Flink】Flink 源码阅读笔记(18)- Flink SQL 中的流和动态表

    1.概述 转载:Flink 源码阅读笔记(18)- Flink SQL 中的流和动态表

最新文章

  1. 优化调整Oracle 8i数据库
  2. linux shell for while if case break continue echo test 及算术运算符 关系运算符 布尔运算符 逻辑运算符 字符串运算符 文件测试运算符
  3. 关于castle和Could not find the dialect in the configuration错误
  4. @autowired注解注入为null_Intellij IDEA中Mybatis Mapper自动注入警告的6种解决方案
  5. 20190501-编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串...
  6. 河北体检系统诚信企业推荐_海南膜结构停车棚加工厂诚信企业推荐
  7. python float 精度_改变Float的精度并在Python中存储
  8. virtual 初探
  9. 2021-2027全球与中国下一代测序数据分析市场现状及未来发展趋势
  10. 计算机图表公式,通达信的DRAWICON49个图标公式
  11. MapGuide应用开发系列(三)----MapGuide 数据包管理及Maestro亮点功能介绍
  12. 计算机网络经过了几个阶段,计算机网络的发展经过哪几个阶段?
  13. python 录屏_《自拍教程70》Python adb一键录屏
  14. 尚德机构2020年Q4财报:净收入5.85亿元,管理费用同比大幅下降近五成
  15. 软件工程网络工程第二次训练(AC代码和详细解释)(C语言描述)
  16. 卧槽,这也真的太上头了吧
  17. 如何在别的电脑上用自己的系统------在移动硬盘上装win10
  18. 2020移动apn接入点哪个快_为什么别人的4g网总比你快? 手机这个设置没开启, 难怪网络差...
  19. 自动化测试 | Selenium自动化测试框架,实战遇到的坑都在这了,玩转自动化测试
  20. [Android]如何做一个崩溃率少于千分之三噶应用app(15)-View分发module架构

热门文章

  1. 配置Apache Basic和Digest认证
  2. Vue学习(入门实例、常用指令)-学习笔记
  3. WGAN-GP 学习笔记
  4. Java学习小程序(5)猜数字游戏
  5. 【Nutch2.2.1基础教程之3】Nutch2.2.1配置文件
  6. Java中HashMap、LinkedHashMap和TreeMap区别使用场景
  7. GitHub for Windows使用教程(一)安装配置
  8. 图解 Redis 五种数据结构底层实现
  9. Mysql 索引底层原理
  10. Tensorflow从入门到精通之:Tensorflow基本操作