groupcache 架構設計

groupcache 是一個分布式緩存 go 語言庫,支持多節點互備熱數據,有良好的穩定性和較高的并發性。

這里有個簡單的應用場景:

groupcache.png

當 GET foo 打到 groupcache-1 后:

  1. groupcache-1 先看看自己的 cache 里有沒有 foo,有的話直接返回
  2. 要是沒有,看看這個請求歸不歸自己管,若是,去 DataSever 獲取,否則問 group-2(假設 foo 歸 -2管) 要數據,成功返回后 groupcache-1 本地也緩存一份
  3. 在 2 過程中,所有后來打到 groupcache-1 的 GET foo 都會阻塞,直到第一個請求返回

問題來了,如何判斷 foo 由誰來處理?

consistenthash.png

如上圖,利用hash將所有節點平均打散到全集,然后當 foo 進來后用相同hash算法就會得到一個唯值,落在那個區間就屬于那個節點,要保證一致性。

因為 foo 和某資源一一對應,這就要求 groupcache 只有 get 沒有 update。

一個簡單的HTTP groupcache Server:

package main

import "github.com/golang/groupcache"

import "github.com/gin-gonic/gin"
import "net/http"
import "time"
import "bytes"

// 虛擬文件生成方法
func generateThumbnail(fileName string) []byte {
    return []byte("fake file")
}

func main() {
    // 本機 ip
    me := "http://10.0.0.1"
    peers := groupcache.NewHTTPPool(me)
    // 設置互備的 node
    peers.Set("http://10.0.0.1", "http://10.0.0.2", "http://10.0.0.3")
    // 創建一個 cache group,最大緩存為64M
    var thumbNails = groupcache.NewGroup("thumbnail", 64<<20, groupcache.GetterFunc(
        func(ctx groupcache.Context, key string, dest groupcache.Sink) error {
            fileName := key
            dest.SetBytes(generateThumbnail(fileName))
            return nil
        }))

    // 設置 thumbnail 的 peers
    groupcache.RegisterPeerPicker(func() groupcache.PeerPicker {
        return peers
    })

    // 起一個 HTTP server
    server := gin.Default()
    server.GET("/files/:name", gin.HandlerFunc(
        func(ctx *gin.Context) {
            var data []byte
            name := ctx.Param("name")
            // 獲取緩存
            err := thumbNails.Get(ctx, name, groupcache.AllocatingByteSliceSink(&data))
            if err != nil {
                ctx.JSON(http.StatusBadRequest, gin.H{"mesage": "file not found"})
                return
            }
            // 返回給客戶端
            http.ServeContent(ctx.Writer, ctx.Request, name, time.Now(), bytes.NewReader(data))
        }))
    server.Run("10.0.0.1:80")
}

Group

groupcache.NewGroup(addr string)
Group 代表一個 cache資源庫

type Group struct {
    name       string
    getter     Getter // cache 沒有命中,從數據庫獲取
    peersOnce  sync.Once 
    peers      PeerPicker // peer 節點調度器
    cacheBytes int64 // 最大cache字節數
    mainCache cache // 此節點緩存
    hotCache cache // 其他節點緩存
    loadGroup flightGroup // 請求并發控制器
    Stats Stats // 統計數據
}

對于一個 Group 來說,會緩存自己節點的數據和訪問比較頻繁的 peer節點 的數據,用LRU算法控制緩存。

當 cache 沒有命中的時候,首先看看這個請求歸不歸該節點管,若是就是調用 getter:

Getter

type Getter interface {
    Get(ctx Context, key string, dest Sink) error
}

對于一個 cache 來說,他不知道如何拉取需要緩存的數據,所以他說啊,你要是想緩存新的東西,就得有個 type 實現 Getter 接口,然后給我一個 Getter 對象,這樣cache沒有命中的時候我能靠這個對象拉取數據。

這個 Getter 類似于 http.Handler,抽象拉取要緩存的數據這個行為,Context(interface{}) 是操作的附帶信息,key 請求的 id,Sink 類似于 http.ResponseWriter,抽象了數據載體的行為:

Sink

type Sink interface {
    // SetString 寫入 string
    SetString(s string) error

    // SetBytes 寫入字節數組,調用者會保留 v 引用
    SetBytes(v []byte) error

    // SetProto 寫入proto.Message,調用者會保留 m 應用
    SetProto(m proto.Message) error

    // ...
}

groupcache 提供了一些常用的 Sink 如 StringSink,BytesSliceSink 和 ProtoSink,這個 proto 是github.com/golang/protobuf/proto,groupcache 規定內部 peer 節點之間數據通信格式使用 google/protobuf,為了抽象 peer 節點,定義了 ProtoGetter:

ProtoGetter

type ProtoGetter interface {
    Get(context Context, in *pb.GetRequest, out *pb.GetResponse) error
}

pb.GetRequestpb.GetResponse 定義了請求和響應 struct,這個抽象可以分離底層傳輸方式。

當然還需要對節點調度器抽象,PeerPicker:

PeerPicker

type PeerPicker interface {
    // PickPeer 根據 key 返回應該處理這個 key 的節點
    // ok 為 true 代表找到了節點
    // nil, false 代表當前節點就是 key 的處理器
    PickPeer(key string) (peer ProtoGetter, ok bool)
}

調度器主要負責根據管理 key 和節點的一致性映射。

groupcache 實現了一個 HTTP 的 PeerPicker,HTTPPool。

至此,groupcache 通過 Getter,PeerPicker,ProtoGetter 三個 interface 定義了cache,節點和調度器之間的連接方式,可以有效地控制耦合度,也提供了比較大的靈活性。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,869評論 18 139
  • 原文出處: 楊步濤的博客 一、 設計理念 1. 空間換時間1) 多級緩存,靜態化客戶端頁面緩存(http head...
    CookieziSui閱讀 2,539評論 0 48
  • 問題導讀: 1.如何構建高并發電商平臺架構 2.哈希、B樹、倒排、bitmap的作用是什么? 3.作為軟件工程師,...
    MaLiang閱讀 5,145評論 1 70
  • 高并發平臺架構 設計理念 1. 空間換時間 多級緩存,靜態化前端頁面緩存(HTTP Header中包含Expire...
    AkaTBS閱讀 3,048評論 0 13
  • 一直以來,甲班之夜都是有人來分享,然后其他小伙伴默默的聽,然后提問偶爾冒冒泡,于是今天,我換了一種方式,讓大家做主...
    雜草袁閱讀 360評論 2 0