0%

【cache2go源码分析】

cache2go是什么

  在github上面的介绍是:Concurrency-safe golang caching library with expiration capabilities.意思是: 具有过期功能并发安全的go语言缓存库。

注释项目地址

  注释源码见: cache2go-annotated

目录结构

1
2
3
4
5
6
7
8
9
benchmark_test.go
cache.go
cacheitem.go
cachetable.go
cache_test.go
errors.go
examples
LICENSE.txt
README.md

目录结构如上所示,主要功能实现源码文件为: cache.go, cacheitem.go, cachetable.go. 接下来一一说明。

源码注释

  • cacheitem.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    /*
    * Simple caching library with expiration capabilities
    * Copyright (c) 2013-2017, Christian Muehlhaeuser <muesli@gmail.com>
    *
    * For license see LICENSE.txt
    */

    package cache2go

    import (
    "sync"
    "time"
    )

    // CacheItem is an individual cache item
    // Parameter data contains the user-set value in the cache.
    // CacheItem是单个的缓存条目, 也就是一个key-value缓存数据
    type CacheItem struct {
    // 读写锁,保证CacheItem同步访问
    sync.RWMutex

    // The item's key.
    // key 可以是任意类型
    key interface{}
    // The item's data.
    // data 可以是任意类型
    data interface{}
    // How long will the item live in the cache when not being accessed/kept alive.
    // 不被访问后的保活时间
    lifeSpan time.Duration

    // Creation timestamp.
    // 创建的时间
    createdOn time.Time
    // Last access timestamp.
    // 最近一次访问时间,KeepAlive函数修改
    accessedOn time.Time
    // How often the item was accessed.
    // 访问的次数,KeepAlive函数修改
    accessCount int64

    // Callback method triggered right before removing the item from the cache
    // 被移除时候的回调函数
    aboutToExpire []func(key interface{})
    }

    // NewCacheItem returns a newly created CacheItem.
    // Parameter key is the item's cache-key.
    // Parameter lifeSpan determines after which time period without an access the item
    // will get removed from the cache.
    // Parameter data is the item's value.
    // 创建CacheItem
    func NewCacheItem(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
    t := time.Now()
    return &CacheItem{
    key: key,
    lifeSpan: lifeSpan,
    createdOn: t,
    accessedOn: t,
    accessCount: 0,
    aboutToExpire: nil,
    data: data,
    }
    }

    // KeepAlive marks an item to be kept for another expireDuration period.
    // 重置过期时间, 需要加锁(下面类似的不再说)
    func (item *CacheItem) KeepAlive() {
    item.Lock()
    defer item.Unlock()
    item.accessedOn = time.Now()
    item.accessCount++
    }

    // LifeSpan returns this item's expiration duration.
    // 返回lifeSpan, 不需要加锁, 因为创建后就没有情况会修改此值(下面类似的不再说)
    func (item *CacheItem) LifeSpan() time.Duration {
    // immutable
    return item.lifeSpan
    }

    // AccessedOn returns when this item was last accessed.
    // 返回accessedOn
    func (item *CacheItem) AccessedOn() time.Time {
    item.RLock()
    defer item.RUnlock()
    return item.accessedOn
    }

    // CreatedOn returns when this item was added to the cache.
    // 返回createdOn
    func (item *CacheItem) CreatedOn() time.Time {
    // immutable
    return item.createdOn
    }

    // AccessCount returns how often this item has been accessed.
    // 返回accessCount
    func (item *CacheItem) AccessCount() int64 {
    item.RLock()
    defer item.RUnlock()
    return item.accessCount
    }

    // Key returns the key of this cached item.
    // 返回key
    func (item *CacheItem) Key() interface{} {
    // immutable
    return item.key
    }

    // Data returns the value of this cached item.
    // 返回data
    func (item *CacheItem) Data() interface{} {
    // immutable
    return item.data
    }

    // SetAboutToExpireCallback configures a callback, which will be called right
    // before the item is about to be removed from the cache.
    // 设置被移除时候的回调函数
    func (item *CacheItem) SetAboutToExpireCallback(f func(interface{})) {
    if len(item.aboutToExpire) > 0 {
    item.RemoveAboutToExpireCallback()
    }
    item.Lock()
    defer item.Unlock()
    item.aboutToExpire = append(item.aboutToExpire, f)
    }

    // AddAboutToExpireCallback appends a new callback to the AboutToExpire queue
    // 添加被移除时候的回调函数
    func (item *CacheItem) AddAboutToExpireCallback(f func(interface{})) {
    item.Lock()
    defer item.Unlock()
    item.aboutToExpire = append(item.aboutToExpire, f)
    }

    // RemoveAboutToExpireCallback empties the about to expire callback queue
    // 删除被移除时候的回调函数
    func (item *CacheItem) RemoveAboutToExpireCallback() {
    item.Lock()
    defer item.Unlock()
    item.aboutToExpire = nil
    }
  • cachetable.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    /*
    * Simple caching library with expiration capabilities
    * Copyright (c) 2013-2017, Christian Muehlhaeuser <muesli@gmail.com>
    *
    * For license see LICENSE.txt
    */

    package cache2go

    import (
    "log"
    "sort"
    "sync"
    "time"
    )

    // CacheTable is a table within the cache
    // 缓存表,
    type CacheTable struct {
    // 读写锁,保证CacheItem同步访问
    sync.RWMutex

    // The table's name.
    // 缓存表名称
    name string
    // All cached items.
    // 所有缓存的条目
    items map[interface{}]*CacheItem

    // Timer responsible for triggering cleanup.
    // 负责触发清除操作的计时器
    cleanupTimer *time.Timer
    // Current timer duration.
    // 触发清理清除操作的时间间隔
    cleanupInterval time.Duration

    // The logger used for this table.
    // 日志
    logger *log.Logger

    // Callback method triggered when trying to load a non-existing key.
    // 加载一个不存在的key时触发的回调函数
    loadData func(key interface{}, args ...interface{}) *CacheItem
    // Callback method triggered when adding a new item to the cache.
    // 添加缓存条目时触发的回调函数
    addedItem []func(item *CacheItem)
    // Callback method triggered before deleting an item from the cache.
    // 删除缓存条目时触发的回调函数
    aboutToDeleteItem []func(item *CacheItem)
    }

    // Count returns how many items are currently stored in the cache.
    // 返回缓存条目的数量
    func (table *CacheTable) Count() int {
    table.RLock()
    defer table.RUnlock()
    return len(table.items)
    }

    // Foreach all items
    // 遍历缓存条目
    func (table *CacheTable) Foreach(trans func(key interface{}, item *CacheItem)) {
    table.RLock()
    defer table.RUnlock()

    for k, v := range table.items {
    trans(k, v)
    }
    }

    // SetDataLoader configures a data-loader callback, which will be called when
    // trying to access a non-existing key. The key and 0...n additional arguments
    // are passed to the callback function.
    // 设置加载一个不存在的key时触发的回调函数
    func (table *CacheTable) SetDataLoader(f func(interface{}, ...interface{}) *CacheItem) {
    table.Lock()
    defer table.Unlock()
    table.loadData = f
    }

    // SetAddedItemCallback configures a callback, which will be called every time
    // a new item is added to the cache.
    // 设置添加缓存条目时触发的回调函数
    func (table *CacheTable) SetAddedItemCallback(f func(*CacheItem)) {
    if len(table.addedItem) > 0 {
    table.RemoveAddedItemCallbacks()
    }
    table.Lock()
    defer table.Unlock()
    table.addedItem = append(table.addedItem, f)
    }

    //AddAddedItemCallback appends a new callback to the addedItem queue
    // 添加添加缓存条目时触发的回调函数
    func (table *CacheTable) AddAddedItemCallback(f func(*CacheItem)) {
    table.Lock()
    defer table.Unlock()
    table.addedItem = append(table.addedItem, f)
    }

    // RemoveAddedItemCallbacks empties the added item callback queue
    // 删除添加缓存条目时触发的回调函数
    func (table *CacheTable) RemoveAddedItemCallbacks() {
    table.Lock()
    defer table.Unlock()
    table.addedItem = nil
    }

    // SetAboutToDeleteItemCallback configures a callback, which will be called
    // every time an item is about to be removed from the cache.
    // 设置删除缓存条目时触发的回调函数
    func (table *CacheTable) SetAboutToDeleteItemCallback(f func(*CacheItem)) {
    if len(table.aboutToDeleteItem) > 0 {
    table.RemoveAboutToDeleteItemCallback()
    }
    table.Lock()
    defer table.Unlock()
    table.aboutToDeleteItem = append(table.aboutToDeleteItem, f)
    }

    // AddAboutToDeleteItemCallback appends a new callback to the AboutToDeleteItem queue
    // 添加删除缓存条目时触发的回调函数
    func (table *CacheTable) AddAboutToDeleteItemCallback(f func(*CacheItem)) {
    table.Lock()
    defer table.Unlock()
    table.aboutToDeleteItem = append(table.aboutToDeleteItem, f)
    }

    // RemoveAboutToDeleteItemCallback empties the about to delete item callback queue
    // 删除删除缓存条目时触发的回调函数
    func (table *CacheTable) RemoveAboutToDeleteItemCallback() {
    table.Lock()
    defer table.Unlock()
    table.aboutToDeleteItem = nil
    }

    // SetLogger sets the logger to be used by this cache table.
    // 设置日志
    func (table *CacheTable) SetLogger(logger *log.Logger) {
    table.Lock()
    defer table.Unlock()
    table.logger = logger
    }

    // Expiration check loop, triggered by a self-adjusting timer.
    // 过期检查, 能自动调节间隔
    func (table *CacheTable) expirationCheck() {
    table.Lock()
    // 计时器停止,后面调整间隔后启动
    if table.cleanupTimer != nil {
    table.cleanupTimer.Stop()
    }
    if table.cleanupInterval > 0 {
    table.log("Expiration check triggered after", table.cleanupInterval, "for table", table.name)
    } else {
    table.log("Expiration check installed for table", table.name)
    }

    // To be more accurate with timers, we would need to update 'now' on every
    // loop iteration. Not sure it's really efficient though.
    // 每次会更新
    now := time.Now()
    // 最小时间间隔
    smallestDuration := 0 * time.Second
    // 遍历所有的items查找最近一个将要过期的时间间隔
    for key, item := range table.items {
    // Cache values so we don't keep blocking the mutex.
    item.RLock()
    lifeSpan := item.lifeSpan
    accessedOn := item.accessedOn
    item.RUnlock()

    // 0 表示永远不过期
    if lifeSpan == 0 {
    continue
    }
    // 已经过期了,删除
    if now.Sub(accessedOn) >= lifeSpan {
    // Item has excessed its lifespan.
    table.deleteInternal(key)
    } else {
    // Find the item chronologically closest to its end-of-lifespan.
    // 更新smallestDuration, 获取最近一个将要过期的时间间隔
    if smallestDuration == 0 || lifeSpan-now.Sub(accessedOn) < smallestDuration {
    smallestDuration = lifeSpan - now.Sub(accessedOn)
    }
    }
    }

    // Setup the interval for the next cleanup run.
    // 设置cleanupInterval为最近将要过期的时间间隔
    table.cleanupInterval = smallestDuration
    if smallestDuration > 0 {
    // 重新启动下一次的过期检测
    table.cleanupTimer = time.AfterFunc(smallestDuration, func() {
    go table.expirationCheck()
    })
    }
    table.Unlock()
    }

    // 内部添加函数, 代码重用, 调用这个方法之前需要加锁
    func (table *CacheTable) addInternal(item *CacheItem) {
    // Careful: do not run this method unless the table-mutex is locked!
    // It will unlock it for the caller before running the callbacks and checks
    table.log("Adding item with key", item.key, "and lifespan of", item.lifeSpan, "to table", table.name)
    table.items[item.key] = item

    // Cache values so we don't keep blocking the mutex.
    expDur := table.cleanupInterval
    addedItem := table.addedItem
    table.Unlock()

    // Trigger callback after adding an item to cache.
    // 触发增加条数的回调函数
    if addedItem != nil {
    for _, callback := range addedItem {
    callback(item)
    }
    }

    // If we haven't set up any expiration check timer or found a more imminent item.
    // 如果当前没有过期检测函数或者当前添加的比当前最短的过期时间还早过期,则更新过期检测
    if item.lifeSpan > 0 && (expDur == 0 || item.lifeSpan < expDur) {
    table.expirationCheck()
    }
    }

    // Add adds a key/value pair to the cache.
    // Parameter key is the item's cache-key.
    // Parameter lifeSpan determines after which time period without an access the item
    // will get removed from the cache.
    // Parameter data is the item's value.
    // 添加缓存条目到缓存表中, addInternal会释放锁
    func (table *CacheTable) Add(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
    item := NewCacheItem(key, lifeSpan, data)

    // Add item to cache.
    table.Lock()
    table.addInternal(item)

    return item
    }

    // 内部删除函数, 代码重用, 调用这个方法之前需要加锁
    func (table *CacheTable) deleteInternal(key interface{}) (*CacheItem, error) {
    r, ok := table.items[key]
    if !ok {
    return nil, ErrKeyNotFound
    }

    // Cache value so we don't keep blocking the mutex.
    aboutToDeleteItem := table.aboutToDeleteItem
    table.Unlock()

    // Trigger callbacks before deleting an item from cache.
    // 触发删除条数的回调函数
    if aboutToDeleteItem != nil {
    for _, callback := range aboutToDeleteItem {
    callback(r)
    }
    }

    // 触发CacheItem过期删除的回调函数
    r.RLock()
    defer r.RUnlock()
    if r.aboutToExpire != nil {
    for _, callback := range r.aboutToExpire {
    callback(key)
    }
    }

    table.Lock()
    table.log("Deleting item with key", key, "created on", r.createdOn, "and hit", r.accessCount, "times from table", table.name)
    delete(table.items, key)

    return r, nil
    }

    // Delete an item from the cache.
    // 从缓存表中删除缓存条目, addInternal会释放锁
    func (table *CacheTable) Delete(key interface{}) (*CacheItem, error) {
    table.Lock()
    defer table.Unlock()

    return table.deleteInternal(key)
    }

    // Exists returns whether an item exists in the cache. Unlike the Value method
    // Exists neither tries to fetch data via the loadData callback nor does it
    // keep the item alive in the cache.
    // 是否存在某个key
    func (table *CacheTable) Exists(key interface{}) bool {
    table.RLock()
    defer table.RUnlock()
    _, ok := table.items[key]

    return ok
    }

    // NotFoundAdd tests whether an item not found in the cache. Unlike the Exists
    // method this also adds data if they key could not be found.
    // 不存在才添加
    func (table *CacheTable) NotFoundAdd(key interface{}, lifeSpan time.Duration, data interface{}) bool {
    table.Lock()

    if _, ok := table.items[key]; ok {
    table.Unlock()
    return false
    }

    item := NewCacheItem(key, lifeSpan, data)
    table.addInternal(item)

    return true
    }

    // Value returns an item from the cache and marks it to be kept alive. You can
    // pass additional arguments to your DataLoader callback function.
    // 获取value, 会通过KeepAlive更新访问时间和访问次数
    func (table *CacheTable) Value(key interface{}, args ...interface{}) (*CacheItem, error) {
    table.RLock()
    r, ok := table.items[key]
    loadData := table.loadData
    table.RUnlock()

    if ok {
    // Update access counter and timestamp.
    r.KeepAlive()
    return r, nil
    }

    // Item doesn't exist in cache. Try and fetch it with a data-loader.
    // 如果不存在,通过loadData获取
    if loadData != nil {
    item := loadData(key, args...)
    if item != nil {
    table.Add(key, item.lifeSpan, item.data)
    return item, nil
    }

    return nil, ErrKeyNotFoundOrLoadable
    }

    return nil, ErrKeyNotFound
    }

    // Flush deletes all items from this cache table.
    // 清除所有的缓存条目, 不会调用 缓存表的aboutToDeleteItem 和 缓存条目的aboutToExpire
    func (table *CacheTable) Flush() {
    table.Lock()
    defer table.Unlock()

    table.log("Flushing table", table.name)

    table.items = make(map[interface{}]*CacheItem)
    table.cleanupInterval = 0
    if table.cleanupTimer != nil {
    table.cleanupTimer.Stop()
    }
    }

    // CacheItemPair maps key to access counter
    // 缓存条目对
    type CacheItemPair struct {
    Key interface{}
    AccessCount int64
    }

    // CacheItemPairList is a slice of CacheIemPairs that implements sort.
    // Interface to sort by AccessCount.
    // 缓存条目对切片
    type CacheItemPairList []CacheItemPair

    // qsort需要的一些函数, 根据访问次数排序
    func (p CacheItemPairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
    func (p CacheItemPairList) Len() int { return len(p) }
    func (p CacheItemPairList) Less(i, j int) bool { return p[i].AccessCount > p[j].AccessCount }

    // MostAccessed returns the most accessed items in this cache table
    // 获取访问最多的几个CacheItem, 最多访问count个
    func (table *CacheTable) MostAccessed(count int64) []*CacheItem {
    table.RLock()
    defer table.RUnlock()

    p := make(CacheItemPairList, len(table.items))
    i := 0
    for k, v := range table.items {
    p[i] = CacheItemPair{k, v.accessCount}
    i++
    }
    sort.Sort(p)

    var r []*CacheItem
    c := int64(0)
    for _, v := range p {
    if c >= count {
    break
    }

    item, ok := table.items[v.Key]
    if ok {
    r = append(r, item)
    }
    c++
    }

    return r
    }

    // Internal logging method for convenience.
    // 内部日志打印
    func (table *CacheTable) log(v ...interface{}) {
    if table.logger == nil {
    return
    }

    table.logger.Println(v...)
    }
  • cache.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    /*
    * Simple caching library with expiration capabilities
    * Copyright (c) 2012, Radu Ioan Fericean
    * 2013-2017, Christian Muehlhaeuser <muesli@gmail.com>
    *
    * For license see LICENSE.txt
    */

    package cache2go

    import (
    "sync"
    )

    var (
    // 全局缓存表的Map, 支持多个缓存表
    cache = make(map[string]*CacheTable)
    // cache的锁
    mutex sync.RWMutex
    )

    // Cache returns the existing cache table with given name or creates a new one
    // if the table does not exist yet.
    // 创建缓存
    func Cache(table string) *CacheTable {
    mutex.RLock()
    t, ok := cache[table]
    mutex.RUnlock()

    if !ok {
    mutex.Lock()
    t, ok = cache[table]
    // Double check whether the table exists or not.
    if !ok {
    t = &CacheTable{
    name: table,
    items: make(map[interface{}]*CacheItem),
    }
    cache[table] = t
    }
    mutex.Unlock()
    }

    return t
    }
-------------本文结束感谢您的阅读-------------