沉浸式go-cache源码阅读!

大家好,我是豆小匠。

这期来阅读go-cache的源码,了解本地缓存的实现方式,同时掌握一些阅读源码的技巧~


1. 源码获取

git clone https://github.com/patrickmn/go-cache.git

用Goland打开可以看到真正实现功能的也就两个go文件,cache.go 1162行,sharded.go 193行,共1355行,用来作为源码阅读的练手素材是非常合适的。

img

通过README.md文件,可以了解这个包的使用方法:

import ("fmt""github.com/patrickmn/go-cache""time"
)func main() {// 创建一个缓存对象,默认过期时间5分钟,每10分钟清理一次缓存c := cache.New(5*time.Minute, 10*time.Minute)// 设置缓存key:foo,value:bar,过期时间是包里定义的一个常量,一会看看具体定义了啥c.Set("foo", "bar", cache.DefaultExpiration)// 获取key为foo的缓存,通过类型断言获取原始的数据foo, found := c.Get("foo")if found {MyFunction(foo.(string))}
}

2. 源码阅读

上面我们看到,创建一个缓存实例,需要传入缓存清理的间隔,也就是说缓存的删除不是根据缓存过期时间实时删除的,那怎么处理才能让已过期的缓存在逻辑上失效呢?

带着疑问,开始阅读cache.go文件。

2.1. Cache定义

type Cache struct {*cache // 为何套娃,先按下不表
}type cache struct {defaultExpiration time.Duration	// 默认过期时间items             map[string]Item // 所有缓存key value,用一个map保存,key是string,value是一个结构体Itemmu                sync.RWMutex	// 读写锁,可以知道go-cache大概率是并发安全的onEvicted         func(string, interface{}) // 这啥,先不管janitor           *janitor // 这啥,先不管
}type Item struct {Object     interface{}	// 真正存储的缓存数据Expiration int64	// 这个数据的过期时间
}

看完Cache结构体的定义,先有个整体印象,再看它的方法实现~

2.2. Cache初始化

在README.go,我们已经知道,初始化的函数是New(defaultExpiration, cleanupInterval time.Duration),双击shift,输入New,就能找到这个函数。

img

type janitor struct {Interval time.Duration	// 清理过期缓存的间隔stop     chan bool // 接受停止协程的信号
}func New(defaultExpiration, cleanupInterval time.Duration) *Cache {items := make(map[string]Item)	// 定义缓存容器,会存到cache对象的itemsreturn newCacheWithJanitor(defaultExpiration, cleanupInterval, items) // 创建一个带有清理协程的Cache对象
}func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache {c := newCache(de, m) // 生成小写那个cache对象(私有)C := &Cache{c}if ci > 0 {	// 传入定时删除缓存时间大于0,启动看清理协程runJanitor(c, ci)	// 启动清理协程,定时删除过期的cache keyruntime.SetFinalizer(C, stopJanitor) // 设置C被回收时,执行函数停止清理协程}return C
}

runtime.SetFinalizer:对象可以关联一个SetFinalizer函数, 当gc检测到unreachable对象有关联的SetFinalizer函数时,会执行关联的SetFinalizer函数, 同时取消关联。 这样当下一次gc的时候,对象重新处于unreachable状态并且没有SetFinalizer关联, 就会被回收。

通过上面源码的阅读,我们可以知道:

  1. 清理过期缓存通过一个清理协程定期清理。
  2. 当Cache不可达时,GC会触发停止janitor协程的函数,下一次GC,Cache和cache(内部cache对象)都会被回收。(如果janitor协程和Cache绑定,Cache对象不会被回收,有内存泄露的风险)
c := cache.New(5*time.Minute, 10*time.Minute)
c = nil	// 这里cache已经不使用了,第一次GC会执行SetFinalizer函数,停掉清理协程,第二次GC则会把Cache和cache对象都回收掉

如果清理协程绑定在Cache对象,因为协程一直在运行,即使在使用者看来c已经设置为nil,cache不再使用,GC也无法回收Cache。

2.3. 缓存失效判断

Cache上是不挂方法的,方法都挂在内部对象cache上。

img

我们先看Get方法:

func (c *cache) Get(k string) (interface{}, bool) {c.mu.RLock()	// 加读锁item, found := c.items[k]if !found {c.mu.RUnlock()return nil, false}// 下面这里会判断item里的过期时间,过期时间小于当前时间,则在逻辑上失效,返回nil, falseif item.Expiration > 0 {	// 如果expiration为0,说明设置的是永不过期if time.Now().UnixNano() > item.Expiration {c.mu.RUnlock()return nil, false}}c.mu.RUnlock()return item.Object, true
}

看源码可以很清晰的看到,缓存过期不是通过是否存在key来判断的,而是通过item里存的expiration时间来判断,因此定时清理缓存是为了清理空间。

2.4. 总体梳理

其他方法都非常明确,我们可以挑几个常用的看看实现,最后整理下cache这个类的成员变量和方法,画个图,完事!

img

前面埋的坑:onEvicted 是删除key的回调函数。

另外sharded.go文件是一个实验性的代码,用于缓存分片,目前还没对外暴露。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371272.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CoreAnimation 变换

CoreAnimation 变换 CoreAnimation 目录 博客园MakeDown支持不佳,如有需要请进GitHub 本片博客主要内容: 仿射变换 - CGAffineTransform3D变换 - CATransform3D仿射变换 - CGAffineTransform CGAffineTransform 是用于二维空间的旋转,缩放和平移的属性.首先展示一个简单的样例,…

20170907wdVBA_GetCellsContentToExcel

WORD 加载项 代码模板 Dim cmdBar As CommandBar, cmdBtn As CommandBarControl Const cmdBtnCap As String "批量提取操作步骤"Sub AutoExec()Call DelCmdBtnCall AddCmdBtnEnd Sub Sub AutoExit()Call DelCmdBtn End SubSub AddCmdBtn()Set cmdBar Application.C…

mysql 5.7 mirror_Centos7 Docker离线部署Mysql5.7

1 环境信息查看系统内核[rootlocalhost /]# cat /etc/redhat-releaseCentOS Linux release 7.5.1804 (Core)2 虚拟机拉取镜像此处资源获取在虚拟机中进行,完成后上传到服务器安装2.1 拉取mysql5.7镜像[rootlocalhost /]# docker pull mysql:5.72.2 导出镜像[rootloc…

Java中的简单REST客户端

如今,大多数用于与某些服务器通信的移动应用程序都使用REST服务。 这些服务也是与JavaScript或jQuery一起使用的常见做法。 现在,我知道在Java中为REST服务创建客户端的2种方法,在本文中,我将尝试演示这两种方法,希望它…

3.20 下午

阅读《艺术学概论》 戏剧冲突是戏剧的灵魂 冲突包括:人物性格的冲突、行为的冲突、 思想感情的冲突乃至心理状态的冲突等等 转载于:https://www.cnblogs.com/bgd140206110/p/6590005.html

华为root工具_华为Mate9解锁后无法ROOT 需要手动刷入Recovery怎么办【解决方法】...

很多朋友手机到手之后,都希望能够ROOT使用更多的系统功能。近日有网友向小编询问,为何华为Mate9解锁后无法ROOT,明明已经通过官方的解锁教程解锁的,但是之后使用“大师”等第三方刷机工具,无法ROOT。其实ROOT的关键就在…

JAX-WS入门

JAX-WS代表XML Web Services的Java API。 它是一种Java编程语言API,用于创建Web服务和使用XML进行通信的客户端。 这篇文章是JAX-WS的快速入门。 先决条件 GlassFish与Eclipse集成在一起 。 创建JAX-WS Web服务 1.在Eclipse中创建一个名为“ com.eviac.blog.jax…

canvas 图片反色

代码实例&#xff1a; <!DOCTYPE HTML> <html> <head><meta charset"utf-8"><title>图片反色</title><style type"text/css">body{ background:black;}#c1{ background:white;}</style><script type&q…

python中的文件父路径怎么表达_python中的文件父路径怎么表达_如何在Python中访问父目录...

所以我有一个朋友给我的Python脚本&#xff0c;但是我没有Python的经验。代码如下&#xff1a;from os import path, chdir, listdir, mkdir, getcwdfrom sys import argvfrom zipfile import ZipFilefrom time import sleep#Defines what extensions to look for within the f…

Maven的中央仓库地址

www.mvnrepository.com转载于:https://www.cnblogs.com/j-liu3323/p/6590435.html

Spring–添加AOP支持

我听到了一个有关一位高级&#xff08;且酬劳颇丰&#xff09;软件工程师的故事。 他的任务是记录他正在研究的项目中每个控制器中的每个方法。 工程师重写了所有控制器方法&#xff0c;因此使用如下代码&#xff1a; RequestMapping(method RequestMethod.GET)public String …

vscode python第三方库检测_VSCode中使用Pylint检查python代码

为什么使用lint在日常开发中&#xff0c;不同开发人员会写下不同风格的代码&#xff0c;导致代码可维护性变差&#xff0c;为了解决风格不一致问题&#xff0c;我们可以制定代码规范&#xff0c;让开发人员都遵守同样的规范编写代码。在开发过程中&#xff0c;部分代码存在质量…

Spring MVC-集成(Integration)-集成LOG4J示例(转载实践)

以下内容翻译自&#xff1a;https://www.tutorialspoint.com/springmvc/springmvc_log4j.htm 说明&#xff1a;示例基于Spring MVC 4.1.6。 以下示例说明如何使用Spring Web MVC框架来触发LOG4J。首先&#xff0c;让我们使用Eclipse IDE&#xff0c;并按照以下步骤使用Spring W…

NUMA架构和Java

是时候部署您的应用程序了&#xff0c;期待着采购最适合负载要求的硬件。 如今&#xff0c;具有40核或80核的包装盒非常普遍。 总体概念是更多的内核&#xff0c;更多的处理能力&#xff0c;更多的吞吐量。 但是我看到了一些相反的结果&#xff0c;表明小型的CPU密集型测试运行…

存储过程常用技巧

我们在进行pl/sql编程时打交道最多的就是存储过程了。存储过程的结构是非常的简单的&#xff0c;我们在这里除了学习存储过程的基本结构外&#xff0c;还会学习编写存储过程时相关的一些实用的知识。如&#xff1a;游标的处理&#xff0c;异常的处理&#xff0c;集合的选择等等…

vue是用a标签打开新页面_vue 在新窗口打开页面并设置不同的背景

开发一个新系统&#xff0c;前端用的vue&#xff0c;vue是单体应用&#xff0c;所有页面都在一个窗口里实现&#xff0c;但项目要求在点button链接后要新打开一个浏览器页面&#xff0c;解决方法如下&#xff1a;1. 给此button设置新事件 click"createdefect"提交缺陷…

卡尔曼滤波的推导

卡尔曼滤波的推导1 最小二乘法在一个线性系统中&#xff0c;若\(x\)为常量&#xff0c;是我们要估计的量&#xff0c;关于\(x\)的观测方程如下&#xff1a; \[ y Hx v \tag{1.1}\] \(H\)是观测矩阵&#xff08;或者说算符&#xff09;&#xff0c;\(v\)是噪音&#xff0c;\(y…

Java注释-保留

考虑一下Java批注&#xff1a; public interface AnAnnotaton {}带有此注释的类&#xff1a; AnAnnotaton class AnAnnotatedClass{}还有一个测试&#xff0c;检查类中是否存在此批注&#xff1a; import static org.hamcrest.MatcherAssert.assertThat; import static org.h…

MYSQL查询选修三门以上课程_SQL高级查询的练习题

Student(S#,Sname,Sage,Ssex) 学生表Course(C#,Cname,T#) 课程表SC(S#,C#,score) 成绩表Teacher(T#,Tname) 教师表问题&#xff1a;1、查询“001”课程比“002”课程成绩高的所有学生的学号&#xff1b;select a.S# from (select s#,score from SC where C#001) a,(select s#,s…

Determing client's IP

AuthorDeterming clients IPАнатоли&23.04.2009 18:39:46Registered userHow to determine clients IP address in THTTPServer.OnClientConnected, THTTPServer.OnClientDisonnected and TRtcFunction.OnExecute events?Danijel Tkalcec [RTC]23.04.2009 19:45:05…