当前位置: 首页 > news >正文

沉浸式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文件是一个实验性的代码,用于缓存分片,目前还没对外暴露。

相关文章:

沉浸式go-cache源码阅读!

大家好,我是豆小匠。 这期来阅读go-cache的源码,了解本地缓存的实现方式,同时掌握一些阅读源码的技巧~ 1. 源码获取 git clone https://github.com/patrickmn/go-cache.git用Goland打开可以看到真正实现功能的也就两个go文件,ca…...

伪协议和反序列化 [ZJCTF 2019]NiZhuanSiWei

打开题目 代码审计 第一层绕过 if(isset($text)&&(file_get_contents($text,r)"welcome to the zjctf")){ echo "<br><h1>".file_get_contents($text,r)."</h1></br>"; 要求我们get传参的text内容必须为w…...

性能优化之资源优化

性能优化之资源优化 资源优化性能关键检测流程。浅析一下基于Unity3D 美术规则约束一、模型层面二、贴图层面三、动画层面四、声音层面&#xff1a;&#xff08;音频通用设置&#xff09;五、UI层面&#xff1a; 题外点&#xff1a;诚然在优化中&#xff0c;美术占比是很重要的…...

ChatGPT免费 | 8个免费使用GPT-4的方法

这篇文章为寻找免费使用GPT-4技术的读者提供了一份实用的指南。 每个推荐的平台都包括了简要的描述和链接&#xff0c;方便读者直接访问。 以下是根据你提供的内容&#xff0c;稍作整理的文章结构&#xff1a; 1. HuggingFace 描述: 提供GPT-4等多种语言模型的平台。 如何使用:…...

解决Qt“报无法定位程序输入点xxx于动态连接库“问题

今天&#xff0c;在使用QtVS2019编译工程时&#xff0c;弹出"无法定位程序输入点xxx于动态链接库"问题&#xff0c;如图(1)所示&#xff1a; 图(1) 报"无法定位程序输入点xxx于动态链接库"问题 出现这种问题的原因有很多&#xff1a; (1) 工程Release/Deb…...

wpf-MVVM绑定时可能出现的内存泄漏问题

文章速览 引言错误示范示例1示例2 坚持记录实属不易&#xff0c;希望友善多金的码友能够随手点一个赞。 共同创建氛围更加良好的开发者社区&#xff01; 谢谢~ 引言 正确结构&#xff1a; Model <——> ViewModel <——> View 但很多时候&#xff0c;很容易出现…...

【飞凌 OK113i-C 全志T113-i开发板】一些有用的常用的命令测试

一些有用的常用的命令测试 一、系统信息查询 可以查询板子的内核信息、CPU处理器信息、环境变量等 二、CPU频率 从上面的系统信息查询到&#xff0c;这是一颗具有两个ARMv7结构A7内核的处理器&#xff0c;主频最高1.2GHz 可以通过命令查看当前支持的频率以及目前所使用主频 …...

基于iOS平台的车牌识别表情识别项目

基于iOS平台的车牌识别&&表情识别项目 简介 ​ 该项目客户端搭载于iOS平台&#xff0c;服务端搭载于阿里云服务器&#xff0c;主要功能是通过拍照或选取相册图片来进行车牌的识别以及人脸表情识别。本文便是对项目整体流程设计思路和具体实现做一个详细介绍。 整体实…...

Matlab仿真2ASK/OOK、2FSK、2PSK、QPSK、4QAM在加性高斯白噪声信道中的误码率与归一化信噪比的关系

本文为学习所用&#xff0c;严禁转载。 本文参考链接 https://zhuanlan.zhihu.com/p/667382398 QPSK代码及高斯白噪声如何产生 https://ww2.mathworks.cn/help/signal/ref/butter.html 滤波器 https://www.python100.com/html/4LEF79KQK398.html 低通滤波器 本实验使用matlab仿…...

九:爬虫-MongoDB基础

MongoDB介绍 MongoDB是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当中功能最丰富&#xff0c;最像关系数据库的。它支持的数据结构非常松散&#xff0c;因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大&#xff0c;其…...

机器学习之实验过程01

import pandas as pd import numpy as np import matplotlib.pyplot as plt data_path /home/py/Work/labs/data/SD.csv # 请确保您的数据文件路径是正确的 df pd.read_csv(data_path) df.head() # 创建散点图 # 创建散点图 plt.figure(figsize(10, 6)) plt.scatter…...

【【迭代16次的CORDIC算法-verilog实现】】

迭代16次的CORDIC算法-verilog实现 -32位迭代16次verilog代码实现 CORDIC.v module cordic32#(parameter DATA_WIDTH 8d32 , // we set data widthparameter PIPELINE 5d16 // Optimize waveform)(input …...

IntelliJ IDEA 2023.3 安装教程

引言 IntelliJ IDEA&#xff0c;通常简称为 IDEA&#xff0c;是由 JetBrains 开发的一款强大的集成开发环境&#xff0c;专为提升开发者的生产力而设计。它支持多种编程语言&#xff0c;包括 Java、Kotlin、Scala 和其他 JVM 语言&#xff0c;同时也为前端开发和移动应用开发提…...

Go 错误处理

Go 错误处理 Go 语言通过内置的错误接口提供了非常简单的错误处理机制。 error类型是一个接口类型&#xff0c;这是它的定义&#xff1a; type error interface {Error() string }我们可以在编码中通过实现 error 接口类型来生成错误信息。 函数通常在最后的返回值中返回错误…...

HarmonyOS构建第一个ArkTS应用(Stage模型)

构建第一个ArkTS应用&#xff08;Stage模型&#xff09; 创建ArkTS工程 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。 选择Application应用开发…...

故障排查利器-错误日志详解

目录 什么是错误日志 错误日志的作用 错误日志的内容 错误日志的格式 错误日志的生成方式 错误日志的解析和处理 错误日志的最佳实践 小结 错误日志是软件开发和运维中非常重要的一部分&#xff0c;记录了应用程序运行过程中发生的错误和异常信息&#xff0c;如错误类型…...

微信小程序(uniapp)api讲解

Uniapp是一个基于Vue.js的跨平台开发框架&#xff0c;可以同时开发微信小程序、H5、App等多个平台的应用。下面是Uniapp常用的API讲解&#xff1a; Vue.js的API Uniapp采用了Vue.js框架&#xff0c;因此可以直接使用Vue.js的API。例如&#xff1a;v-show、v-if、v-for、comput…...

overtureDNS使用介绍

Overture是一个定制的DNS中继服务器。 在此下在二进制版本 https://github.com/shawn1m/overture/releases默认配置文件./config.yml bindAddress: :53 debugHTTPAddress: 127.0.0.1:5555 dohEnabled: false primaryDNS:- name: DNSPodaddress: 119.29.29.29:53protocol: udp…...

平衡二叉树的构建(递归

目录 1.概念&#xff1a;2.特点&#xff1a;3.构建方法&#xff1a;4.代码&#xff1a;小结&#xff1a; 1.概念&#xff1a; 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;&#xff0c;也称为AVL树&#xff0c;是一种二叉树&#xff0c;它满足每个节点的左子树和右…...

flutter开发实战-设置bottomNavigationBar中间按钮悬浮效果

flutter开发实战-设置bottomNavigationBar中间按钮悬浮的效果 在使用tabbar时候&#xff0c;可以使用bottomNavigationBar来设置中间凸起的按钮&#xff0c;如下 一、效果图 中间按钮凸起的效果图如下 二、实现代码 我们使用BottomAppBar 一个容器&#xff0c;通常与[Sscaf…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...