github.com/golang/groupcache 存储的是kv结构,同是memcache作者出品.在放弃update/delete 的特性后,换来的是:Cluster 的能力,处理热点的能力。
因为groupcache只能get,不能update和delete,也不能设置过期时间,只能通过lru淘汰最近最少访问的数据;有些数据如果长时间不更改,那么可以用groupcache作为缓存;groupcache已经在dl.Google.com、Blogger、Google Code、Google Fiber、Google生产监视系统等项目中投入使用。groupcache既是服务器,也是客户端,当在本地groupcache缓存中没有查找的数据时,通过一致性哈希,查找到该key所对应的peer服务器,在通过http协议,从该peer服务器上获取所需要的数据;还有一点就是当多个客户端同时访问memcache中不存在的键时,会导致多个客户端从mysql获取数据并同时插入memcache中,而在相同情况下,groupcache只会有一个客户端从mysql获取数据,其他客户端阻塞,直到第一个客户端获取到数据之后,再返回给多个客户端。
下面我们用一个例子看下,如何使用它。
代码语言:javascript复制package main
import (
"context"
"errors"
"log"
"net/http"
"strconv"
"github.com/golang/groupcache"
)
const (
// 启动的http端口
ServicePort = 9000
// groupcache内部通信端口 同时修改CachePort 和ServicePort 端口,模拟启动运行多个节点
CachePort = 8000
// 组名称
GroupName = "user"
)
// 模拟数据库
var UserDb = map[string]string{
"1001": "张三",
"1002": "李四",
"1003": "王五",
}
// 数据不在缓存中时,加载数据
// 使用单飞,防止缓存惊群效应
func getterFunc(ctx context.Context, key string, dest groupcache.Sink) (err error) {
log.Println("从数据库获取用户姓名,uid=" key)
name, ok := UserDb[key]
if !ok {
return errors.New("uid miss")
}
dest.SetString(name)
return nil
}
func startHTTP() {
group := groupcache.NewGroup(GroupName, 1<<20, groupcache.GetterFunc(getterFunc))
http.HandleFunc("/getName", func(writer http.ResponseWriter, request *http.Request) {
// 数据查询
uid := request.URL.Query().Get("uid")
log.Println("http uid=" uid)
var name []byte
err := group.Get(context.Background(), uid, groupcache.AllocatingByteSliceSink(&name))
if err != nil {
writer.Write([]byte("404"))
return
}
writer.Write([]byte("name=" string(name)))
})
go http.ListenAndServe(":" strconv.Itoa(ServicePort), nil)
}
func main() {
startHTTP()
// 启动groupcache
localUrl := "http://127.0.0.1:" strconv.Itoa(CachePort)
peers := groupcache.NewHTTPPool(localUrl)
peers.Set("http://127.0.0.1:8000", "http://127.0.0.1:8001")
http.ListenAndServe(":" strconv.Itoa(CachePort), peers)
}
测试下
代码语言:javascript复制% curl 'http://127.0.0.1:9000/getName?uid=1002'
name=李四%
% curl 'http://127.0.0.1:9000/getName?uid=1001'
name=张三%
% curl 'http://127.0.0.1:9000/getName?uid=1003'
name=王五%
代码语言:javascript复制2023/05/14 16:58:17 http uid=1002
2023/05/14 16:58:17 从数据库获取用户姓名,uid=1002
2023/05/14 16:58:20 http uid=1001
2023/05/14 16:58:23 http uid=1003
2023/05/14 16:58:23 从数据库获取用户姓名,uid=1003
整个流程如下:
1,初始化groupCache,并指定回源函数。
2,注册peer节点
3,启动cache服务供peer查询
查询过程如下:
1,先从本地的lru cache里查询
2,如果本地查不到,到peer的本地缓存查询,peer的选取采用一致性hash算法
3,如果peer中也没有取到结果则回源查询
4,将回源查询的结果存入本地缓存
5,上述查询过程为了防止击穿,采用了singleflight组件。
总的来说,它是为了保证可用性,放弃了一致性,所以不支持主动设置缓存、缓存过期、和缓存内容的修改。
适合不修改的内容的缓存。