CloudGeek读源码系列-cache2go源码解析

一、cache2go是什么

  • 作者说:Concurrency-safe golang caching library with expiration capabilities.

什么意思呢?

  • 有心跳机制的并发安全的go语言缓存库

学习一门语言有一个很好的方法就是阅读优秀的开源项目源码,学习优秀前辈的编码方式,同时发现自己的知识盲区,不断获取新知识!cache2go很精简,代码量很少,非常适合刚接触go语言的同学作为入门级项目来读源码。

下面我会先介绍项目组成,然后讲解核心数据结构,再梳理整个实现逻辑,来帮助大家掌握整个项目。

二、项目结构

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

  1. cache.go
  2. cacheitem.go
  3. cachetable.go

三、关键数据结构

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

  1. CacheItem
  2. CacheTable

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

1、CacheItem

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

type CacheItem struct {
	sync.RWMutex

	// The item‘s key.
	key interface{}
	// The item‘s 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.
	accessedOn time.Time
	// How often the item was accessed.
	accessCount int64

	// Callback method triggered right before removing the item from the cache【被删除时触发的回调方法】
	aboutToExpire func(key interface{})
}

2、CacheTable

CacheTable描述了缓存中的一个表项,里面的items属性就是上面讲到的CacheItem类型实例。这里除了常规属性外还有若干函数类型的属性,源码如下所示:

type CacheTable struct {
	sync.RWMutex

	// The table‘s name.
	name string
	// All cached items.【一个表中的所有条目都存在这个map里,map的key是任意类型,值是CacheItem指针类型】
	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)
}

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

四、代码逻辑

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

1、cacheitem.go

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

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,
	}
}
  • 代码如上所示,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的类型是一个可以接收任意类型参数的函数类型。有点绕,需要静下心来理解一下哦~

源码如下:

// KeepAlive marks an item to be kept for another expireDuration period.【将accessedOn更新为当前时间】
func (item *CacheItem) KeepAlive() {
	item.Lock()
	defer item.Unlock()
	item.accessedOn = time.Now()
	item.accessCount++
}

// LifeSpan returns this item‘s expiration duration.
func (item *CacheItem) LifeSpan() time.Duration {
	// immutable
	return item.lifeSpan
}

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

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

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

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

// Data returns the value of this cached item.
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.【设置回调函数,当一个item被移除的时候这个函数会被调用】
func (item *CacheItem) SetAboutToExpireCallback(f func(interface{})) {
	item.Lock()
	defer item.Unlock()
	item.aboutToExpire = f
}

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

下面继续看另外2个源码文件吧

2、cachetable.go

3、cache.go

aboutToExpire 详细X

没有英汉互译结果
  请尝试网页搜索

原文地址:https://www.cnblogs.com/cloudgeek/p/9146736.html

时间: 2024-07-31 16:06:38

CloudGeek读源码系列-cache2go源码解析的相关文章

hbase源码系列(十一)Put、Delete在服务端是如何处理?

在讲完之后HFile和HLog之后,今天我想分享是Put在Region Server经历些了什么?相信前面看了<HTable探秘>的朋友都会有印象,没看过的建议回去先看看,Put是通过MultiServerCallable来提交的多个Put,好,我们就先去这个类吧,在call方法里面,我们找到了这句. responseProto = getStub().multi(controller, requestProto); 它调用了Region Server的multi方法.好,我们立即杀到HReg

hbase源码系列(十三)缓存机制MemStore与Block Cache

这一章讲hbase的缓存机制,这里面涉及的内容也是比较多,呵呵,我理解中的缓存是保存在内存中的特定的便于检索的数据结构就是缓存. 之前在讲put的时候,put是被添加到Store里面,这个Store是个接口,实现是在HStore里面,MemStore其实是它底下的小子. 那它和Region Server.Region是什么关系? Region Server下面有若干个Region,每个Region下面有若干的列族,每个列族对应着一个HStore. HStore里面有三个很重要的类,在这章的内容都

MySQL系列 - MySQL源码安装配置

二.MySQL系列 - MySQL源码安装配置(附5.7等最新版本)1.依赖环境准备2.开始安装2.1.下载MySQL2.2.解压2.3.赋权限2.4.修改配置文件2.5.启动MySQL3.MySQL 5.7源码安装不同之处 二.MySQL系列 - MySQL源码安装配置(附5.7等最新版本) 1.依赖环境准备 make安装 make编译器下载地址:http://www.gnu.org/software/make/ # tar zxvf make-3.82.tar.gz # cd make-3.

Android 源码系列之&lt;十四&gt;从源码的角度深入理解LeakCanary的内存泄露检测机制(下)

转载请注明出处:http://blog.csdn.net/llew2011/article/details/52958567 在上边文章Android 源码系列之<十三>从源码的角度深入理解LeakCanary的内存泄露检测机制(中)由于篇幅原因仅仅向小伙伴们讲述了在Android开发中如何使用LeakCanary来检测应用中出现的内存泄露,并简单的介绍了LeakCanary的相关配置信息.根据上篇文章的介绍我们知道LeakCanary为了不给APP进程造成影响所以新开启了一个进程,在新开启的

hbase源码系列(六)HMaster启动过程

这一章是server端开始的第一章,有兴趣的朋友先去看一下hbase的架构图,我专门从网上弄下来的. 按照HMaster的run方法的注释,我们可以了解到它的启动过程会去做以下的动作. * <li>阻塞直到变成ActiveMaster * <li>结束初始化操作 * <li>循环 * <li>停止服务并执行清理操作* </ol> HMaster是没有单点问题是,因为它可以同时启动多个HMaster,然后通过zk的选举算法选出一个HMaster来.

曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享

写在前面的话&&About me 网上写spring的文章多如牛毛,为什么还要写呢,因为,很简单,那是人家写的:网上都鼓励你不要造轮子,为什么你还要造呢,因为,那不是你造的. 我不是要造spring,我只是想把自己学习spring的一些感想,一些心得说出来,希望大家看到有不对的地方,请一定不吝赐教. 说说我自己,13年小本毕业,软件工程专业,校招去了最近疯传的牢厂总部里待了2年,15年越狱出来,某落魄互联网公司(PC时代风头无两)待了1年,慨叹深圳买房之艰难,遂于16年底回蓉.趁着热血未冷

hbase源码系列(二)HTable 如何访问客户端

hbase的源码终于搞一个段落了,在接下来的一个月,着重于把看过的源码提炼一下,对一些有意思的主题进行分享一下.继上一篇讲了负载均衡之后,这一篇我们从client开始讲吧,从client到master再到region server,按照这个顺序来开展,网友也可以对自己感兴趣的部分给我留言或者直接联系我的QQ. 现在我们讲一下HTable吧,为什么讲HTable,因为这是我们最常见的一个类,这是我们对hbase中数据的操作的入口. 1.Put操作 下面是一个很简单往hbase插入一条记录的例子.

hbase源码系列(七)Snapshot的过程

在看这一章之前,建议大家先去看一下snapshot的使用.可能有人会有疑问为什么要做Snapshot,hdfs不是自带了3个备份吗,这是个很大的误区,要知道hdfs的3个备份是用于防止网络传输中的失败或者别的异常情况导致数据块丢失或者不正确,它不能避免人为的删除数据导致的后果.它就想是给数据库做备份,尤其是做删除动作之前,不管是hbase还是hdfs,请经常做Snapshot,否则哪天手贱了... 直接进入主题吧,上代码. public void takeSnapshot(SnapshotDes

hbase源码系列(三)Client如何找到正确的Region Server

客户端在进行put.delete.get等操作的时候,它都需要数据到底存在哪个Region Server上面,这个定位的操作是通过HConnection.locateRegion方法来完成的. loc = hConnection.locateRegion(this.tableName, row.getRow()); 这里我们首先要讲hbase的两张元数据表-ROOT-和.META.表,它们一个保存着region的分部信息,一个保存着region的详细信息.在<hbase实战>这本书里面详细写了