介绍一下groupcache实现和使用

[size=x-large]一.groupcache介绍[/size]
memcached作者Brad Fitzpatrick用Go开发了前者的替代版,现已在Google多个生产环境(如dl.google.com)中投入使用。本文粗略介绍一下groupcache的实现方式。
memcached的业务架构如下图,memcache的分布式不是服务器端实现,而是通过客户端实现;是客户端根据key自己计算决定memcached实例 。

[img]http://dl2.iteye.com/upload/attachment/0113/1242/28db1eed-1a4e-3472-8a03-f143b9749655.jpg[/img]

groupcahe的业务结构如下图,key的存储采用分布式方式,key通过一致性哈希分散在各个实例中,通过任意一个实例皆可得到数据。

[img]http://dl2.iteye.com/upload/attachment/0113/1238/65de3662-d533-300f-b30c-8acc0b50ada1.jpg[/img]

[size=x-large]二.groupcahe数据结构[/size]

[b]1.byteview[/b]
byteview对字符串数组和string进行封装,存储何种形式的字符串对用户透明,定义如下

type struct {
b []byte
s string
}


具体的操作函数的实现方式如下
func at(i){
if b!=nil
return b[i]
return s[i]
}



[b]2.singleflight[/b]

singleflight保证“同时”多个同参数的get请求只执行一次操作功能,整个流程如下


[img]http://dl2.iteye.com/upload/attachment/0113/1244/b1e9c90b-153e-3737-b79f-0ed9eb899ac8.jpg[/img]

当Thread3在时间00:01执行Get("太阳")时候,在系统中以key为“太阳”标识正在翻译“太阳”,整个翻译过程需要8秒钟。
当Thread1和Thread2分别与00:02,00:03提交执行Get("太阳")时候,发现系统中存在以key为“太阳”标识的翻译执行,进程阻塞等待Thread3的翻译结果。
在时间00:09,Thread3得到“太阳”的翻译结果,返回给客户,此时Thread1和Thread2执行读取翻译结果返回给客户。
整个过程中,不仅减少Thread1和Thread2的得到结果时间,也减少读取数据库的次数,节约系统资源。

[b]3. consistenthash[/b]
consistenthash提供一致性hash功能,将key按照一致性hash分布在各个实例中,主要作用是实例加入离开时不会导致映射关系的重大变化。
consistenthash 由数组和一个存储实例标识和标识key的hashmap实现,结构如下,

[img]http://dl2.iteye.com/upload/attachment/0113/1234/1b30af06-9cd8-3c1c-b39a-e4aa75c3af89.jpg[/img]

consistenthash会将实例标识复制replicas(可以设置)份放到hashmap结构中,选择数据key的时候,选择大于实例key的hash值最近的一个。
eg:如果 hash("太阳")的值为24,选择整数数组中的30(大于24最近的key)作为存储实例的key,从hashmap找到30的实例为192.168.0.1,判断192.168.0.1作为“太阳”的存储实例。
[b]
4.lru[/b]
lru提供缓存的清除算法,groupcache实现方式无特别之处,再次略过不说。

[b]5.group[/b]
group是key-value的容器,每个group都有一个名字,相当于一批key-value的家族标识。
当用户访问某个key时,group现在本地内存中维持的hashmap中查找,如果没有则从远端的peer或者本地的文件系统等地方加载。

[img]http://dl2.iteye.com/upload/attachment/0113/1236/0e46132b-6eb2-3bb4-98f0-16a0722b63e2.jpg[/img]

Getter定了如何从本地读取数据,这部分需要使用使用groupcache的开发者自己提供实现,group 函数getLocally将会调用。
PickPeer会按照一致性hash选择peer,发起http请求拉取数据,这便是group函数getFromPeer实现。
在getLocally和getFromPeer调用的过程中,用singleflight提供多次同参get请求只执行一次操作。
maincahe 是本地读取的数据的存储容器;hotcahe是从远端peer读取的热数据的存储,现在的热数据完全凭运气,从远端读取的数据有10%的概率会称为热数据。
maincahe和hotcahe的内存用量总和高于此group的cacheBytes限制时,便启用lru进行数据清理。
stats是指标统计,如Gets便是用户请求本实例的计次,cacheHits是命中本地内存maincahe和hotcahe的计次。
group通过key对应的value的伪代码如下:

func get(key){
/*从本地内存中读取*/
if maincache.has(key)
return maincache[key]
if hotcahe.has(key)
return hotcahe [key]
/*从远端peer读取*/
value := PeerPicker.PickPeer(key).get(key)
if value !=nil
if(rand()%10 == 1)
hotcahe[key] = value
return value
/*从本地文件等系统读取*/
value:= getLocally(key)
if value !=nil
maincache[key] = value
return value
}



[b]6.httppool[/b]
httppool便是各个peer通讯的封装,开启通讯http,group的getFromPeer便是调用相对应peer的httpool提供的服务。
httppool同时保存了所有的远端peer实例的请求地址,实现pickPeer安装一致性hash取得某key对应的远端peer实例的地址。
[size=x-large]三. groupcahe使用[/size]
groupcahe是个库,不是一个程序,如果需要使用,需要自己写部分逻辑,在此提供一个简单的例子
package main
import (
"fmt"
"github.com/groupcache"
"io/ioutil"
"log"
"net/http"
"os"
)
var (
peers_addrs = []string{"http://127.0.0.1:8001", "http://127.0.0.1:8002", "http://127.0.0.1:8003"}
)
func main() {
if len(os.Args) != 3 {
fmt.Println("\r\n Usage local_addr \t\n local_addr must in(127.0.0.1:8001,localhost:8002,127.0.0.1:8003)\r\n")
os.Exit(1)
}
local_addr := os.Args[1]
peers := groupcache.NewHTTPPool("http://" + local_addr)
peers.Set(peers_addrs...)
var image_cache = groupcache.NewGroup("image", 8<<30, groupcache.GetterFunc(
func(ctx groupcache.Context, key string, dest groupcache.Sink) error {
result, err := ioutil.ReadFile(key)
if err != nil {
fmt.Printf("read file error %s.\n", err.Error())
return nil
}
fmt.Printf("asking for %s from local file system\n", key)
dest.SetBytes([]byte(result))
return nil
}))
http.HandleFunc("/image", func(rw http.ResponseWriter, r *http.Request) {
var data []byte
k := r.URL.Query().Get("id")
fmt.Printf("user get %s from groupcache\n", k)
image_cache.Get(nil, k, groupcache.AllocatingByteSliceSink(&data))
rw.Write([]byte(data))
})
log.Fatal(http.ListenAndServe(local_addr, nil))
}


使用的时候
src.exe 127.0.0.1:8001 src.exe 127.0.0.1:8002 src.exe 127.0.0.1:8003创建3个peer实例即可
客户端访问http://127.0.0.1:8001/image?id=configure.ini即可得到数据

[img]http://dl2.iteye.com/upload/attachment/0113/1246/cf77c078-f510-30c6-b382-5d3ebd2eb573.png[/img]

对源码进行修改打印出来的日志,各位看到的并不是这样,并没有这些日志。
[size=x-large]四.最后说几点[/size]
groupcahe不提供过期、删除等操作,所有groupcahe只适合与静态不变的数据,不能用于取代memcahe。
一些地方实现比较粗略,如定义热点数据等,现在采用10%随机并不合理;peer节点的删除增加也没有处理。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值