16.go开源groupcache项目蛤蟆笔记——部署
groupcache没有服务端与客户端之分。本身没有main函数就是一个库,可以被其他应用集成到代码中。
主要结构说明:
consistenthash一致性hash哈希算法,
lru(提供了LRU缓存算法,最终存数据的地方,里面使用了两种数据结构,map和list,map用来保存key-value数据,list按访问顺序保存value,这样实现lru,在清理数据的时候,将最久未访问的数据清除掉。
singleflight实现多次相同请求只去获取值一次,减少资源消耗。不会都将请求发到数据库或者其他的实例中,以此来避免”惊群”。
groupcachepb同一个group中的多个实例交互数据,使用pb,减少数据体积。
其他源文件:
byteview.go 可以理解为一种数据格式,在groupcache中,将所有的value都保存为byteview的格式。优先使用[]byte表示,否则使用string
peers.go同一个group里可以有多个节点,peers是用来表示这种关系的
sinks.go这个东西用来接收Get到的数据,
groupcache.go主代码核心,定义了groupcache的核心数据结构—group,以及围绕group的get等核心操作。
http.go 这里是业务和groupcache交互的接口
Get代码流程
描述一次Get请求在groupcache里经过的历程。代码在groupcache.go中。
1. 首先Group会去初始化它的peers节点,但是又不能每次get的时候都去调用,所以这里采用了sync.once用来保证只执行一次。
2. 将GETS状态+1,跟我们在memcache中看到的各种统计信息一样,用来统计Cache Get,Cache Miss
3. 从当前节点获取缓存内容,从mainCache中获取,如果没有从hotCache中获取。
4. 如果当前节点没有获取到结果,则会从peers的节点中获取内容,如果没有,则会从用户定义的getter方法中获取信息(此时会使用singleflight,来保证只有1个请求会真正发起请求,也就是,如果对当前节点有5个对key=test的请求,则只有1个会真正向peers发起请求,而其他四个请求会block,直到请求返回。而且先从peers请求,再从getter中获取也是有讲究的,因为,如果在3台业务机上同时对key=test发起请求,那么会保证只会调用一次getter,从而避免memcache等缓存的业务冷启动问题)
5. 将获取的结果返回
部署
部署框架如下图1:
注意点
(摘自网络)
§ 需要监听两个地方,一个是监听节点,一个是监听请求
§ 在批量设置节点地址的时候需要在地址前面加上http://,如果没有加上去,所以缓存信息就不能再节点之间交互
§ 启动的节点地址要与设置的节点地址一致:数量和地址值。少一个就无法在节点间交互。
测试代码
packagemain
import(
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
"github.com/golang/groupcache"
)
var(
//peers_addrs=[]string{"127.0.0.1:8001","127.0.0.1:8002","127.0.0.1:8003"}
//rpc_addrs=[]string{"127.0.0.1:9001","127.0.0.1:9002","127.0.0.1:9003"}
index=flag.Int("index",0,"peerindex")
)
funcmain(){
flag.Parse()
peers_addrs:=make([]string,3)
rpc_addrs:=make([]string,3)
iflen(os.Args)>0{
fori:=1;i<4;i++{
peers_addrs[i-1]=os.Args[i]
rpcaddr:=strings.Split(os.Args[i],":")[1]
port,_:=strconv.Atoi(rpcaddr)
rpc_addrs[i-1]=":"+strconv.Itoa(port+1000)
}
}
if*index<0||*index>=len(peers_addrs){
fmt.Printf("peer_index%dnotinvalid\n",*index)
os.Exit(1)
}
peers:=groupcache.NewHTTPPool(addrToURL(peers_addrs[*index]))
varstringcache=groupcache.NewGroup("SlowDBCache",64<<20,groupcache.GetterFunc(
func(ctxgroupcache.Context,keystring,destgroupcache.Sink)error{
result,err:=ioutil.ReadFile(key)
iferr!=nil{
log.Fatal(err)
returnerr
}
fmt.Printf("askingfor%sfromdbserver\n",key)
dest.SetBytes([]byte(result))
returnnil
}))
peers.Set(addrsToURLs(peers_addrs)...)
http.HandleFunc("/zk",func(rwhttp.ResponseWriter,r*http.Request){
log.Println(r.URL.Query().Get("key"))
vardata[]byte
k:=r.URL.Query().Get("key")
fmt.Printf("cliaskedfor%sfromgroupcache\n",k)
stringcache.Get(nil,k,groupcache.AllocatingByteSliceSink(&data))
rw.Write([]byte(data))
})
gohttp.ListenAndServe(rpc_addrs[*index],nil)
rpcaddr:=strings.Split(os.Args[1],":")[1]
log.Fatal(http.ListenAndServe(":"+rpcaddr,peers))
}
funcaddrToURL(addrstring)string{
return"http://"+addr
}
funcaddrsToURLs(addrs[]string)[]string{
result:=make([]string,0)
for_,addr:=rangeaddrs{
result=append(result,addrToURL(addr))
}
returnresult
}
执行方式:./test127.0.0.1:8001 127.0.0.1:8002 127.0.0.1:8003
启动了一个节点,设置节点地址个数为3个,所以在启动其它节点的时候变换下地址的顺序,使第一个地址三次都不一样就好了。这样同样方法启动三个节点。
打开浏览器访问127.0.0.1:9001?key=1.txt,这里1.txt是需要获取数据的之际地方,类似于实际中的数据库,我这里直接用一个文件代替了。
访问使用9000是因为在测试代码中将端口加上了1000.
友情链接
http://capotej.com/blog/2013/07/28/playing-with-groupcache/
有疑问加站长微信联系(非本文作者)