• 启航


    一、概述

      我们今天开始第一部分“golang技能提升”。这一块我计划分析3个项目,一个是很流行的golang源码阅读入门项目cache2go,接着是非常流行的memcache的go语言版groupcache,最后是docker项目中分出来的核心组件之一containerd,通过这3个项目的源码全解析来达到golang能力上一个台阶的目的。

      在本系列教程的学习过程中,我希望你能够做到如下要求:如果你是一个go语言新手,在看下面的代码分析过程中你肯定会遇到一些自己陌生的编码方式、陌生的知识点,这个过程中我希望你遇到一个点掌握一个点,比如看到下面的代码用到了锁,就去找各种资料把锁相关的知识点学了。看到回调函数,就思考一下人家为什么这些地方都使用回调函数,有什么好处。这样在看完这个项目源码分析后,你就能学到一部分知识模块

    二、cache2go是什么

      这是一个在github上开源的项目,原作者这样介绍:

      Concurrency-safe golang caching library with expiration capabilities.

      看懂了吗?简单说就是有心跳机制的并发安全的go语言缓存库。ok,下面我们要分析的这个项目是一个缓存库,并且有2大特性,并发安全和心跳机制!

    三、项目结构

    项目目录结构如上图所示,可以看到功能实现相关源码文件只有3个:

    • cache.go

    • cacheitem.go

    • cachetable.go

    四、关键数据结构

       项目中只涉及到2个复杂的数据类型,分别是:

    • CacheItem

    • CacheTable

       含义和字面意义一致,一个是缓存表,一个是缓存表中的条目。下面分别看一下这2个结构是怎么定义的。

    1、CacheItem

      CacheItem类型是用来表示一个单独的缓存条目,源码如下所示,每个字段都很清晰易懂,注释稍长的属性已经中文标注。

     1// CacheItem is an individual cache item
    2// Parameter data contains the user-set value in the cache.
    3type CacheItem struct {
    4    sync.RWMutex
    5
    6    // The item's key.
    7    key interface{}
    8    // The item's data.
    9    data interface{}
    10    //【不被访问后存活时间】
    11    // How long will the item live in the cache when not being accessed/kept alive.
    12    lifeSpan time.Duration
    13
    14    // Creation timestamp.
    15    createdOn time.Time
    16    // Last access timestamp.
    17    accessedOn time.Time
    18    // How often the item was accessed.
    19    accessCount int64
    20    //【被删除时触发的回调方法】
    21    // Callback method triggered right before removing the item from the cache
    22    aboutToExpire func(key interface{})
    23}

    2、CacheTable

      CacheTable描述了缓存中的一个表项,里面的items属性就是上面讲到的CacheItem类型实例。这里除了常规属性外还有若干函数类型的属性,源码如下所示(最后几行显示样式好像抽筋了~):

     1// CacheTable is a table within the cache
    2type CacheTable struct {
    3    sync.RWMutex
    4
    5    // The table's name.
    6    name string
    7    // All cached items.
    8    //【一个表中的所有条目都存在这个map里,map的key是任意类型,值是CacheItem指针类型】
    9    items map[interface{}]*CacheItem
    10
    11    // Timer responsible for triggering cleanup.
    12    //【负责触发清除操作的计时器】
    13    cleanupTimer *time.Timer
    14    // Current timer duration.
    15    //【清除操作触发的时间间隔】
    16    cleanupInterval time.Duration
    17
    18    // The logger used for this table.
    19    logger *log.Logger
    20
    21    // Callback method triggered when trying to load a non-existing key.
    22    //【需要提取一个不存在的key时触发的回调函数】
    23    loadData func(key interface{}, args ...interface{}) *CacheItem
    24    // Callback method triggered when adding a new item to the cache.
    25    //【增加一个缓存条目时触发的回调函数】
    26    addedItem func(item *CacheItem)
    27    // Callback method triggered before deleting an item from the cache.
    28    //【删除前触发的回调函数】
    29    aboutToDeleteItem func(item *CacheItem)
    30}

      如上所示,cache2go的核心数据结构很简洁,应该比较容易理解。当然没有完全理解每个字段这样定义的原因也别急,看完下面的代码逻辑后反过来再看数据结构,肯定就明白每个字段的作用了。

    五、代码逻辑

       我们的思路是从下往上分析代码,什么意思呢?就是说先看和item相关的操作,再看包含item的table相关的代码,最后看操作table的cache级别的逻辑。所以下面我们要先看cacheitem.go的代码,接着分析cachetable.go,然后看cache.go,最后看3个文件中的源码互相间关联是什么,最后看example,也就是cache怎么玩~

    1、cacheitem.go

      如上图,这个源码文件中只包含了一个类型CacheItem和一个函数NewCacheItem()的定义。CacheItem有哪些属性前面已经看过了,下面先看NewCacheItem()函数:

     1// NewCacheItem returns a newly created CacheItem.
    2// Parameter key is the item's cache-key.
    3// Parameter lifeSpan determines after which time period without an access the item
    4// will get removed from the cache.
    5// Parameter data is the item's value.
    6func NewCacheItem(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
    7    t := time.Now()
    8    return &CacheItem{
    9        key:           key,
    10        lifeSpan:      lifeSpan,
    11        createdOn:     t,
    12        accessedOn:    t,
    13        accessCount:   0,
    14        aboutToExpire: nil,
    15        data:          data,
    16    }
    17}
    •  代码如上所示,NewCacheItem()函数接收3个参数,分别是键、值、存活时间(key、data、lifeSpan),返回一个CacheItem类型实例的指针。

    • 其中createOn和accessedOn设置成了当前时间,aboutToExpire也就是被删除时触发的回调方法暂时设置成nil,不难想到这个函数完成后还需要调用其他方法来设置这个属性。

      cacheitem.go中除了CacheItem类型的定义,NewCacheItem()函数的定义外,还有一个部分就是CacheItem的方法定义,一共8个

      源码看起来行数不少,内容其实很简单,主要是元素获取操作,这里需要留意读写操作都是加了相应的读写锁的,还记得开头提到的cache2go是一个并发安全的程序吗?并发安全就体现在这些地方。下面最复杂的是最后一个回调函数的设置,这个方法的形参是f func(interface{}),也就是说形参名为f,形参类型是func(interface{}),这是一个函数类型,这个函数类型的参数是一个interface{},也就是空接口,因为任意类型都可以被认为实现了空接口,所以这里可以接收任意类型的实参。也就是说f的类型是一个可以接收任意类型参数的函数类型。有点绕,需要静下心来理解一下哦~

      源码如下:

     1// KeepAlive marks an item to be kept for another expireDuration period.
    2//【将accessedOn更新为当前时间】
    3func (item *CacheItem) KeepAlive() {
    4    item.Lock()
    5    defer item.Unlock()
    6    item.accessedOn = time.Now()
    7    item.accessCount++
    8}
    9
    10// LifeSpan returns this item's expiration duration.
    11func (item *CacheItem) LifeSpan() time.Duration {
    12    // immutable
    13    return item.lifeSpan
    14}
    15
    16// AccessedOn returns when this item was last accessed.
    17func (item *CacheItem) AccessedOn() time.Time {
    18    item.RLock()
    19    defer item.RUnlock()
    20    return item.accessedOn
    21}
    22
    23// CreatedOn returns when this item was added to the cache.
    24func (item *CacheItem) CreatedOn() time.Time {
    25    // immutable
    26    return item.createdOn
    27}
    28
    29// AccessCount returns how often this item has been accessed.
    30func (item *CacheItem) AccessCount() int64 {
    31    item.RLock()
    32    defer item.RUnlock()
    33    return item.accessCount
    34}
    35
    36// Key returns the key of this cached item.
    37func (item *CacheItem) Key() interface{} {
    38    // immutable
    39    return item.key
    40}
    41
    42// Data returns the value of this cached item.
    43func (item *CacheItem) Data() interface{} {
    44    // immutable
    45    return item.data
    46}
    47
    48// SetAboutToExpireCallback configures a callback, which will be called right
    49// before the item is about to be removed from the cache.
    50//【设置回调函数,当一个item被移除的时候这个函数会被调用】
    51func (item *CacheItem) SetAboutToExpireCallback(f func(interface{})) {
    52    item.Lock()
    53    defer item.Unlock()
    54    item.aboutToExpire = f
    55}

      到这里就看完这个源文件了,是不是很轻松呢~

      上面的一堆方法功能都还是很直观的,今天我们先看到这里,下一讲继续分析cachetable相关代码。

     

  • 相关阅读:
    《赵渝强》《Docker+Kubernetes容器实战派》新书上市!!!
    CF1665 DV2 D. GCD Guess
    os.path.relpath和os.path.basename,返回文件路径中的文件名
    nginx中使用perl脚本来定制一些请求转发等等
    nginx中自带的一些变量参数说明
    Nginx反向代理
    从此告别手动下载selenium驱动,自动跟随浏览器(Chrom,)更新驱动
    Portable PDB简介
    Mac 安装 brew(最新教程,绝对可行,一行代码搞定,不报错)
    定时删除某目录下几天前的文件
  • 原文地址:https://www.cnblogs.com/cloudgeek/p/9197177.html
Copyright © 2020-2023  润新知