cache2go源码分析

1.cache2go的特点

cache2go是我在u网络上看到许多推荐的golang学习的经典项目,它代码量小,工程结构、代码思路都非常简洁,非常的适合新手去阅读并学习。

总体来讲cache2go是一个并发安全、自带过期检查、的内存k-v数据库。从本项目可以学习到:读写锁的运用、回调函数的运用,并且有着非常友好的封装和兼容性。

2.总体设计结构

三个重要元素:CacheItemCacheTableCache ,CacheItem 表示的是一个基本的键值对,也即我们要存在缓存里的数据,CacheTable是一个表,它负责管理所有存在它名下的CacheItem(由一个map结构,item作为map的值类型),最终我们直接使用的是Cache,Cache是以map的初始化进入自己的生命周期,map的值类型为CacheTable。

2.1 CacheItem

CacheItem的结构定义就不多赘述,可以看看item的回调函数的设置:

这里可以看到对对象CacheItem的生命周期结束时,删除该item前会调用每一个回调函数(例如打印该item的基本信息之类的)。可以看到在删除回调函数里: item.aboutToExpire = nil,直接将函数数组字段赋为nil,然后gc会自动的去收回前面的回调函数占用的内存。

2.2 CacheTable

Cachetable中最出彩的代码莫属于:expirationCheck()函数的实现:下面是一部分片段

time.Timer在初始化时会设置一个间隔duration,和一个caller,当duration的间隔过去后,自动执行后面的caller。在这里,这个caller其实就是`expirationCheck() 自身,所以进入`expirationCheck()时,先检查table的cleanupTimer是不是nil,如果是的话就要调用它的stop方法,停止timer的计数,免得 expirationCheck()在自己还没退出之前又唤起了另一个 expirationCheck()。对item一一检查之后,得到一个最先会过期的item的生命剩余周期:smallestDuration。也就是说:下一次 执行expirationCheck()的时间就是这个间隔之后,所以会重新设置这个timer,caller就是expirationCheck()的一个goroutine。

3 并发安全

源码中涉及到许多的读写锁:sync.RWMutex 的运用。 RWMutex 是单写多读锁,也就是说:该锁可以加多个读锁或者一个写锁,读锁占用的情况下会阻止写,不会阻止读,多个 goroutine 可以同时获取读锁;写锁会阻止其他 goroutine(无论读和写)进来,整个锁由该 goroutine 独占;适用于读多写少的场景

锁Lock()之后,都会跟上defer .UnLock()来安全的释放锁,这也是利用了golang的特性:关键字defer,让新手也能安全的用锁