在網(wǎng)上查了挺多groupcache的相關(guān)信息,但是搜出來大部分都是copy,實際的例子也沒有,所以就看了下源碼,也在golang-nuts上問了,github上搜索了一些groupcache的使用例子,在此作個總結(jié),目前對這個緩存庫還僅處于了解狀態(tài)
大概介紹
其實關(guān)于groupcache的介紹網(wǎng)上非常的多,搜索出來清一色都是說的介紹,當然也有配圖如何部署,但是文字與配圖完全不在一個時空,圖也是copy國外的一篇博客。它不像其它的一些緩存數(shù)據(jù)庫有個服務(wù)端,需要客戶端去連接,文檔中明確說明了它就是一個程序庫,所以沒有服務(wù)端與客戶端這么一說,換句話說,它本沒有服務(wù)端,又或者是人人都是服務(wù)端,食神的感覺油然而生。
主要代碼結(jié)構(gòu)
它的代碼結(jié)構(gòu)也比較清晰,代碼量也不是很大,很適合大家去閱讀學習。主要分為了consistenthash (提供一致性哈希算法的支持),lru (提供了LRU方式清楚緩存的算法),singleflight (保證了多次相同請求只去獲取值一次,減少了資源消耗),還有一些源文件:byteview.go 提供類似于一個數(shù)據(jù)的容器,http.go 提供不同地址間的緩存的溝通的實現(xiàn),peers.go 節(jié)點的定義,sinks.go 感覺就是一個開辟空間給容器,并和容器交互的一個中間人,groupcache.go 整個源碼里的大當家,其它人都是為它服務(wù)的。
一個測試例子
在使用這個緩存的時候比較重要的幾方面也是我之前犯錯的幾個地方
- 需要監(jiān)聽兩個地方,一個是監(jiān)聽節(jié)點,一個是監(jiān)聽請求
- 在批量設(shè)置節(jié)點地址的時候需要在地址前面加上
http:// ,因為一開始我沒有加上去,所以緩存信息一直不能再節(jié)點之間交互
- 啟動的節(jié)點地址要與設(shè)置的節(jié)點地址一致:數(shù)量和地址值。因為我每次少一個就無法在節(jié)點間交互。
以上的一些信息可能也有不對的地方,只是我個人的一個測試后的結(jié)果。
代碼部分
package main
import (
"flag"
"fmt"
"github.com/golang/groupcache"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
)
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, "peer index")
)
func main() {
flag.Parse()
peers_addrs := make([]string, 3)
rpc_addrs := make([]string, 3)
if len(os.Args) > 0 {
for i := 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 %d not invalid\n", *index)
os.Exit(1)
}
peers := groupcache.NewHTTPPool(addrToURL(peers_addrs[*index]))
var stringcache = groupcache.NewGroup("SlowDBCache", 64<<20, groupcache.GetterFunc(
func(ctx groupcache.Context, key string, dest groupcache.Sink) error {
result, err := ioutil.ReadFile(key)
if err != nil {
log.Fatal(err)
return err
}
fmt.Printf("asking for %s from dbserver\n", key)
dest.SetBytes([]byte(result))
return nil
}))
peers.Set(addrsToURLs(peers_addrs)...)
http.HandleFunc("/zk", func(rw http.ResponseWriter, r *http.Request) {
log.Println(r.URL.Query().Get("key"))
var data []byte
k := r.URL.Query().Get("key")
fmt.Printf("cli asked for %s from groupcache\n", k)
stringcache.Get(nil, k, groupcache.AllocatingByteSliceSink(&data))
rw.Write([]byte(data))
})
go http.ListenAndServe(rpc_addrs[*index], nil)
rpcaddr := strings.Split(os.Args[1], ":")[1]
log.Fatal(http.ListenAndServe(":"+rpcaddr, peers))
}
func addrToURL(addr string) string {
return "http://" + addr
}
func addrsToURLs(addrs []string) []string {
result := make([]string, 0)
for _, addr := range addrs {
result = append(result, addrToURL(addr))
}
return result
}
執(zhí)行方式:./demo 127.0.0.1:8001 127.0.0.1:8002 127.0.0.1:8003
在上面的命令中我們就啟動了一個節(jié)點,并且設(shè)置節(jié)點地址個數(shù)為3個,這里由于我是默認的index為0,所以在啟動其它節(jié)點的時候變換下地址的順序,使第一個地址三次都不一樣就好了。(抱歉,這里實現(xiàn)的不是很好),這樣同樣方法啟動三個節(jié)點。
打開瀏覽器訪問127.0.0.1:9001?key=1.txt ,這里1.txt是需要獲取數(shù)據(jù)的之際地方,類似于實際中的數(shù)據(jù)庫,我這里直接用一個文件代替了。
運行結(jié)果:
在上面圖中,我們像:8001 這個節(jié)點請求數(shù)據(jù),它沒有緩存數(shù)據(jù),然后會去其它節(jié)點中尋找這個key 的數(shù)據(jù),當都沒有的時候就會去數(shù)據(jù)庫中獲取(這里我們是一個文件),所以會出現(xiàn)在:8003 這個節(jié)點中獲取數(shù)據(jù)庫數(shù)據(jù)的操作。
根據(jù)上圖看到,第一個地址為:8002 這個節(jié)點直接從緩存里面取值,但是在請求之前這個節(jié)點并沒有緩存數(shù)據(jù),這個也同樣是節(jié)點間的交互完成的。
我在本地開啟了兩個虛擬機,在同一個局域網(wǎng)中,測試也能得出相同的結(jié)果。這里結(jié)果就不再貼上了,當然運行的時候節(jié)點地址要做相應的變動,根據(jù)每個機子的局域網(wǎng)中的地址。
這篇博客關(guān)于groupcache的介紹和源代碼的說明部分比較少,主要就是貼出了一個測試的例子,因為我看到網(wǎng)上很少,并且壓根就沒有給出如何運行或者運行結(jié)果(不包括剛才提到的老外的一個博客,他寫的還是很好的,我就是看了他的博客才著手自己編寫的)。
|