golang中cache组件的使用及groupcache源码解析

 更新时间:2021年06月09日 11:41:27   作者:随风奔跑尿飞扬  
本篇主要解析groupcache源码中的关键部分, lru的定义以及如何做到同一个key只加载一次。缓存填充以及加载抑制的实现方法,本文重点给大家介绍golang中cache组件的使用及groupcache源码解析,感兴趣的朋友一起看看吧

groupcache 简介

在软件系统中使用缓存,可以降低系统响应时间,提高用户体验,降低某些系统模块的压力.
groupcache是一款开源的缓存组件.与memcache与redis不同的时,groupcache不需要单独的部署,可以作为你程序的一个库来使用. 这样方便我们开发的程序部署.

本篇主要解析groupcache源码中的关键部分, lru的定义以及如何做到同一个key只加载一次。

缓存填充以及加载抑制的实现

上篇有提到load函数的实现, 缓存填充的逻辑也体现在这里。
groupcache尽量避免从源中获取数据,当本地数据缺失时会先从peer中获取,peer中命中则直接填充到本地,未命中才会从源中加载,这正是缓存填充的实现逻辑。
而加载抑制,避免重复加载的功能是依靠 singleflight包实现的。
这个包中主要有两个结构体:

call用来存放获取结果(val)和错误(err), 每个key对应一个call实例。wg用来控制请求的等待。

type call struct {
	wg  sync.WaitGroup
	val interface{}
	err error
}

Group用来存放所有的call,记录所有的请求。

type Group struct {
	mu sync.Mutex       // protects m
	m  map[string]*call // lazily initialized
}

Group.Do是功能的实现。
当接到一个请求时, 会首先加锁, 并初始化用来记录请求的mapmap的键为请求的key, 值为call

g.mu.Lock()
if g.m == nil {
	g.m = make(map[string]*call)
}

如果当前的key已经在请求加载的过程中,那么解除上一步定义的冲突锁,并等待已经存在的加载请求结束后返回。

if c, ok := g.m[key]; ok {
	g.mu.Unlock()
	c.wg.Wait()
	return c.val, c.err
}

如果当前的key没有已经存在的加载过程,那么创建一个call实例, 加入到map记录中,并向call.wg中加入一个记录,以阻塞其他请求,解除上一步定义的冲突锁。

c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()

调用传入的函数(作者并没有将这个功能局限于数据获取,通过传入的func可以实现不同功能的控制),将结果赋值给call,获取完成后wg.done结束阻塞。

c.val, c.err = fn()
c.wg.Done()

然后删除map记录

g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()

这个功能的实现主要是依靠sync.WaitGroup的阻塞实现, 这里也是对初学者最难理解的地方。
可以想象一个场景:
大学寝室中,你和你的室友都要到食堂买午饭,你对室友说:“你自己去就行,给我带一份”。然后你就在宿舍中等待舍友回来。
在这个场景中,你和室友就是请求,你在等待就是阻塞

cache(lru)

上篇提到的主缓存和热缓存均是依靠cache实现。
cache的实现依靠双向链表。
MaxEntries 最大的存储量
OnEvicted当发生驱逐时(即到达MaxEntries)执行的操作
ll双向链表本体
cache key对应链表中的元素

type Cache struct {
	// MaxEntries is the maximum number of cache entries before
	// an item is evicted. Zero means no limit.
	MaxEntries int

	// OnEvicted optionally specifies a callback function to be
	// executed when an entry is purged from the cache.
	OnEvicted func(key Key, value interface{})

	ll    *list.List
	cache map[interface{}]*list.Element
}

添加时会先进行初始化map,如果key已存在,那么会将keyindex提到首位(这里的链表不存在index,仅为方便理解),并更新其value。
如果不存在则直接插入到首位。
如果插入后的长度超过限制, 会执行清理操作

func (c *Cache) Add(key Key, value interface{}) {
	if c.cache == nil {
		c.cache = make(map[interface{}]*list.Element)
		c.ll = list.New()
	}
	if ee, ok := c.cache[key]; ok {
		c.ll.MoveToFront(ee)
		ee.Value.(*entry).value = value
		return
	}
	ele := c.ll.PushFront(&entry{key, value})
	c.cache[key] = ele
	if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
		c.RemoveOldest()
	}
}

清理时会删除尾部元素, 这里就解释了为什么每次操作时会把元素提到首位。

func (c *Cache) RemoveOldest() {
	if c.cache == nil {
		return
	}
	ele := c.ll.Back()
	if ele != nil {
		c.removeElement(ele)
	}
}

以上就是golang中cache组件的使用之groupcache的详细内容,更多关于go groupcache用法的资料请关注脚本之家其它相关文章!

相关文章

  • 浅析Golang中rune类型的使用

    浅析Golang中rune类型的使用

    从golang源码中看出,rune关键字是int32的别名(-231~231-1),对比byte(-128~127),可表示的字符更多,本文就来简单聊聊它的使用方法吧,希望对大家有所帮助
    2023-05-05
  • Golang 日期/时间包的使用详解

    Golang 日期/时间包的使用详解

    这篇文章主要介绍了Golang 日期/时间包的使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • golang实现分页算法实例代码

    golang实现分页算法实例代码

    这篇文章主要给大家介绍了关于golang实现分页算法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-09-09
  • Go通用的 MapReduce 工具函数详解

    Go通用的 MapReduce 工具函数详解

    本文介绍了使用Go语言实现的MapReduce框架,特别是在AWSS3 SDK的MultiPartUpload功能中的应用,包括并发上传和错误处理策略,详细解释了如何通过并发goroutines提高上传效率,并通过MapReduce模型优化代码结构和处理流程,感兴趣的朋友跟随小编一起看看吧
    2024-09-09
  • go-zero使用goctl生成mongodb的操作使用方法

    go-zero使用goctl生成mongodb的操作使用方法

    mongodb是一种高性能、开源、文档型的nosql数据库,被广泛应用于web应用、大数据以及云计算领域,goctl model 为 goctl 提供的数据库模型代码生成指令,目前支持 MySQL、PostgreSQL、Mongo 的代码生成,本文给大家介绍了go-zero使用goctl生成mongodb的操作使用方法
    2024-06-06
  • 使用Go+GoQuery库实现头条新闻采集

    使用Go+GoQuery库实现头条新闻采集

    在本文中,我们将介绍如何使用Go语言和GoQuery库实现一个简单的爬虫程序,用于抓取头条新闻的网页内容,我们还将使用爬虫代理服务,提高爬虫程序的性能和安全性,我们将使用多线程技术,提高采集效率,最后,我们将展示爬虫程序的运行结果和代码,需要的朋友可以参考下
    2023-10-10
  • Golang 编写Tcp服务器的解决方案

    Golang 编写Tcp服务器的解决方案

    Golang 作为广泛用于服务端和云计算领域的编程语言,tcp socket 是其中至关重要的功能,这篇文章给大家介绍Golang 开发 Tcp 服务器及拆包粘包、优雅关闭的解决方案,感兴趣的朋友一起看看吧
    2022-10-10
  • GO语言 复合类型专题

    GO语言 复合类型专题

    这篇文章主要介绍了GO语言 复合类型的的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • golang中之strconv包的具体使用方法

    golang中之strconv包的具体使用方法

    这篇文章主要介绍了golang中之strconv包的具体使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • go实现整型的二进制转化的方法

    go实现整型的二进制转化的方法

    这篇文章主要介绍了go实现整型的二进制转化的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2019-07-07

最新评论