瞭解了前面兩篇基於本地緩存的策略以後,咱們再來看看分佈式緩存groupcache。它的做者也是memcache的做者,在github中對groupcache的簡介有以下一句:node
groupcache is a caching and cache-filling library, intended as a replacement for memcached in many cases.git
直譯過來就是groupcache是一個kv緩存庫,而且致力於在某些場景下替代mc。但願讀者經過接下來的內容能對groupcache的使用場景有一些本身的想法。 github
筆者簡單總結了一下它應該具有的一些特性,若是有不全的歡迎你們補充:緩存
以上就是筆者暫時能想到的關於groupcache的特性。其中第四點,groupcache只支持get操做多是和mc差異最大的地方了,由於真正只能get的場景着實很少。。。bash
PS: 在啓動groupcache的時候須要監聽兩個端口,一個端口是用來外部訪問groupcache的,另一個端口是集羣內部peer互相通訊使用的。 服務器
接下來看下如何使用groupcache:併發
run_peer1.gocurl
const defaultHost = "127.0.0.1:9001" const group_addr = ":8081" func main() { if len(os.Args) <= 1 { fmt.Fprintf(os.Stderr, "Usage: %s peer1 [peer2...]", os.Args[0]) os.Exit(1) } //本地peer地址 self := flag.String("self", defaultHost, "self node") flag.Parse() //cache集羣全部節點 cluster := os.Args[1:] //初始化本地groupcache, 並監聽groupcache相應的端口 setUpGroup("test_cache") //本地peer peers := groupcache.NewHTTPPool(addrsToUrl(*self)[0]) peers.Set(addrsToUrl(cluster...)...) //設置集羣信息 用以本機緩存沒命中的時候,一致性哈希查找key的存儲節點, 並經過http請求訪問 selfPort := strings.Split(*self, ":")[1] http.ListenAndServe(":"+selfPort, peers) //監聽本機集羣內部通訊的端口 } //啓動groupcache func setUpGroup(name string) { //緩存池, stringGroup := groupcache.NewGroup(name, 1<<20, groupcache.GetterFunc(func(_ groupcache.Context, key string, dest groupcache.Sink) error { //當cache miss以後,用來執行的load data方法 fp, err := os.Open("groupcache.conf") if err != nil { return err } defer fp.Close() fmt.Printf("look up for %s from config_file\n", key) //按行讀取配置文件 buf := bufio.NewReader(fp) for { line, err := buf.ReadString('\n') if err != nil { if err == io.EOF { dest.SetBytes([]byte{}) return nil } else { return err } } line = strings.TrimSpace(line) parts := strings.Split(line, "=") if len(parts) > 2 { continue } else if parts[0] == key { dest.SetBytes([]byte(parts[1])) return nil } else { continue } } })) http.HandleFunc("/config", func(rw http.ResponseWriter, r *http.Request) { k := r.URL.Query().Get("key") var dest []byte fmt.Printf("look up for %s from groupcache\n", k) if err := stringGroup.Get(nil, k, groupcache.AllocatingByteSliceSink(&dest)); err != nil { rw.WriteHeader(http.StatusNotFound) rw.Write([]byte("this key doesn't exists")) } else { rw.Write([]byte(dest)) } }) //可以直接訪問cache的端口, 啓動http服務 //http://ip:group_addr/config?key=xxx go http.ListenAndServe(group_addr, nil) } //將ip:port轉換成url的格式 func addrsToUrl(node_list ...string) []string { urls := make([]string, len(node_list)) for k, addr := range node_list { urls[k] = "http://" + addr } return urls }
這裏執行 go run run_peer1.go 127.0.0.1:9001 就能啓動一個groupcache實例,其中8080是對外訪問端口,9001是集羣內部通訊端口, 固然在這個例子中,這是一個只有單節點的集羣。簡單測試一下:分佈式
執行 curl -i http://127.0.0.1:8081/config?key=name 命令兩次, 能夠看到第一次走到了GetterFunc類型的方法中去從配置文件load數據了,第二次直接從緩存中就拿到了數據。memcached
若是想要單機模擬多節點集羣,只須要將上述代碼配置稍微修改一下保存成run_peer2.go便可,修改以下:
//run_peer2.go const defaultHost = "127.0.0.1:9002" const group_addr = ":8082"
執行命令, 便可實現集羣模式:
go run run_peer1.go 127.0.0.1:9001 127.0.0.1:9002 go run run_peer2.go 127.0.0.1:9001 127.0.0.1:9002
此時能夠觀察一下整個集羣的執行流程以及一致性hash,執行 curl -i http://127.0.0.1:8082/config?key=name,看一下輸出結果:
由於訪問的8082端口,會先去訪問8082端口,而後查找name這個鍵所屬的存儲節點,如圖一;發現存儲節點並不是本節點以後,經過http請求訪問對應存儲節點,獲取數據,如圖二。據此,咱們能夠總結一下groupcache總的流程:
具體的結構圖以下所示,這個只畫出了一半的流程,但願你們能看明白。
至此,關於groupcache的總體運做流程描述完畢。可能有一些地方沒有細講,好比singleflight, 怎麼實現的一致性哈希等等。。。若是有疑問歡迎拍磚留言,我和你們一塊兒研究一塊兒學習。。