最新消息:欢迎加入小松的QQ群一起讨论一起学习,搜索页面修改成lua+ElasticSearch,使用php这个页面速度相对慢一点,优化之后速度杠杠的,如有问题请加群联系我

go cache2go源码分析

golang 2浏览 0评论

今天看了cache2go源码,cache2go是一个用Go实现的并发安全的缓存库,主要是代码量少,学习一下

cache2go github地址: https://github.com/muesli/cache2go

从开始使用的代码开始分析
github上有examples目录,写的是使用的demo,examples/callbacks/callbacks.go,我开始从这个文件开始分析

代码清单 1-1 examples/callbacks/callbacks.go 11行

cache := cache2go.Cache("myCache")

使用了cache2go库中的Cache,返回一个CacheTable的地址

代码清单 2-1 cache.go 22-39行

func Cache(table string) *CacheTable {
    mutex.RLock()
    t, ok := cache[table]
    mutex.RUnlock()

    if !ok {
        t = &CacheTable{
            name:  table,
            items: make(map[interface{}]*CacheItem),
        }

        mutex.Lock()
        cache[table] = t
        mutex.Unlock()
    }

    return t
}

cache map类型读取的时候加了一个读锁,读完之后解锁,ok判断这个cache的map是否存在,如果不存在,就创建一个,如果存在就直接返回,返回的是一个CacheTable的地址

代码清单 1-2 examples/callbacks/callbacks.go 15-22行

   cache.SetAddedItemCallback(func(entry *cache2go.CacheItem) {
        fmt.Println("Added:", entry.Key(), entry.Data(), entry.CreatedOn())
    })
    // This callback will be triggered every time an item
    // is about to be removed from the cache.
    cache.SetAboutToDeleteItemCallback(func(entry *cache2go.CacheItem) {
        fmt.Println("Deleting:", entry.Key(), entry.Data(), entry.CreatedOn())
    })

调用了CacheTable的SetAddedItemCallback方法,把一个匿名函数传入到这个方法里,SetAboutToDeleteItemCallback也是一样的情况,在后台代码会调用

代码清单 1-3 examples/callbacks/callbacks.go 15-22行

cache.Add("someKey", 0, "This is a test!")

添加一个someKey的缓存,0表示没有过期时间,值为This is a test!
下面看看Add方法执行了什么

代码清单 3-1 cachetable.go 170-178行

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
}

代码清单 4-1 cachetiem.go 43-54行

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,
    }
}

代码清单 3-1中item 调用了代码清单 4-1的方法,得到的是CacheItem的结构地址,添加之前先加个锁,然后之后执行添加操作 addInternal

代码清单 3-2 cachetable.go 143-163行

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 {
        addedItem(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()
    }
}

代码table.items[item.key] = item 这个时候要缓存的内容就保存到map里,缓存的结构为
要缓存的内容都保存到一个二维映射里map[“myCache”][“someKey”],代码清单 3-1 写缓存之后不是加锁了写完之后要解锁,然后addedItem判断有没有要执行匿名函数,代码清单 1-2 SetAddedItemCallback中定义了要回调的匿名函数,这时候就会执行,下面开始自动删除过期的缓存,item.lifeSpan缓存时间必须大于0 并且expDur == 0也就是第一次保存缓存的时候或者当前写的缓存的过期时间比下次执行缓存检查的时间还要小,就执行缓存检查

代码清单 3-2 cachetable.go 111-140行

  for key, item := range items {
        // Cache values so we don't keep blocking the mutex.
        item.RLock()
        lifeSpan := item.lifeSpan
        accessedOn := item.accessedOn
        item.RUnlock()

        if lifeSpan == 0 {
            continue
        }
        if now.Sub(accessedOn) >= lifeSpan {
            // Item has excessed its lifespan.
            table.Delete(key)
        } else {
            // Find the item chronologically closest to its end-of-lifespan.
            if smallestDuration == 0 || lifeSpan-now.Sub(accessedOn) < smallestDuration {
                smallestDuration = lifeSpan - now.Sub(accessedOn)
            }
        }
    }

    // Setup the interval for the next cleanup run.
    table.Lock()
    table.cleanupInterval = smallestDuration
    if smallestDuration > 0 {
        table.cleanupTimer = time.AfterFunc(smallestDuration, func() {
            go table.expirationCheck()
        })
    }
    table.Unlock()

遍历所有的缓存,找出哪个缓存最先过期,把过期时间赋值给smallestDuration,使用 time.AfterFuncAfterFunc另起一个go程等待smallestDuration时间,去执行缓存过期检查
这个部分应该是这个库的核心了,其他的代码应该都能看懂了

QQ交流群:136351212

如无特别说明,本站文章皆为原创,若要转载,务必请注明以下原文信息:
转载保留版权:小松博客» go cache2go源码分析
本文链接地址:https://www.phpsong.com/3260.html

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
木有头像就木有JJ!点这里按步骤申请Gravatar头像吧!