[Golang] 使用 Group Cache 來寫一個 Image Server 範例

測試方式

go run groupcache.go -http=:8081 -groupcache-peers=:8082,:8083
go run groupcache.go -http=:8082 -groupcache-peers=:8081,:8083
go run groupcache.go -http=:8083 -groupcache-peers=:8081,:8082

開啟你的本地瀏覽器 http://localhost:8081/medium.jpg?width=100

這邊是可以看到你目前的狀態 http://localhost:8081/stats

你可以看到以下的資訊

{
  "Group": {
    "Gets": 2,
    "CacheHits": 1,
    "PeerLoads": 0,
    "PeerErrors": 1,
    "Loads": 1,
    "LoadsDeduped": 1,
    "LocalLoads": 1,
    "LocalLoadErrs": 0,
    "ServerRequests": 0
  },
  "Caches": {
    "Main": {
      "Bytes": 2762,
      "Items": 1,
      "Gets": 3,
      "Hits": 1,
      "Evictions": 0
    },
    "Hot": {
      "Bytes": 0,
      "Items": 0,
      "Gets": 2,
      "Hits": 0,
      "Evictions": 0
    }
  }
}

 

需要用到的套件:

  • imageserver  https://github.com/pierrre/imageserver/blob/master/README.md

以下為 go 的程式碼

 

// Package advanced provides a groupcache example.
//
// Run:
//  go run groupcache.go -http=:8081 -groupcache-peers=:8082,:8083
//  go run groupcache.go -http=:8082 -groupcache-peers=:8081,:8083
//  go run groupcache.go -http=:8083 -groupcache-peers=:8081,:8082
//
// Open http://localhost:8081/medium.jpg?width=100
//
// Stats are available on http://localhost:8081/stats
package main

import (
  "crypto/sha256"
  "encoding/json"
  "flag"
  "fmt"
  "net/http"
  "net/url"
  "strings"

  "github.com/disintegration/gift"
  "github.com/golang/groupcache"
  "github.com/pierrre/imageserver"
  imageserver_cache "github.com/pierrre/imageserver/cache"
  imageserver_cache_groupcache "github.com/pierrre/imageserver/cache/groupcache"
  imageserver_http "github.com/pierrre/imageserver/http"
  imageserver_http_gift "github.com/pierrre/imageserver/http/gift"
  imageserver_http_image "github.com/pierrre/imageserver/http/image"
  imageserver_image "github.com/pierrre/imageserver/image"
  _ "github.com/pierrre/imageserver/image/gif"
  imageserver_image_gift "github.com/pierrre/imageserver/image/gift"
  _ "github.com/pierrre/imageserver/image/jpeg"
  _ "github.com/pierrre/imageserver/image/png"
  imageserver_testdata "github.com/pierrre/imageserver/testdata"
)

const (
  groupcacheName = "imageserver"
)

var (
  flagHTTP            = ":8080"
  flagGroupcache      = int64(128 * (1 << 20))
  flagGroupcachePeers string
)

func main() {
  parseFlags()
  startHTTPServer()
}

func parseFlags() {
  flag.StringVar(&flagHTTP, "http", flagHTTP, "HTTP")
  flag.Int64Var(&flagGroupcache, "groupcache", flagGroupcache, "Groupcache")
  flag.StringVar(&flagGroupcachePeers, "groupcache-peers", flagGroupcachePeers, "Groupcache peers")
  flag.Parse()
}

func startHTTPServer() {
  http.Handle("/", http.StripPrefix("/", newImageHTTPHandler()))
  http.Handle("/favicon.ico", http.NotFoundHandler())
  initGroupcacheHTTPPool() // it automatically registers itself to "/_groupcache"
  http.HandleFunc("/stats", groupcacheStatsHTTPHandler)
  err := http.ListenAndServe(flagHTTP, nil)
  if err != nil {
    panic(err)
  }
}

func newImageHTTPHandler() http.Handler {
  return &imageserver_http.Handler{
    Parser: imageserver_http.ListParser([]imageserver_http.Parser{
      &imageserver_http.SourcePathParser{},
      &imageserver_http_gift.ResizeParser{},
      &imageserver_http_image.FormatParser{},
      &imageserver_http_image.QualityParser{},
    }),
    Server: newServer(),
  }
}

func newServer() imageserver.Server {
  srv := imageserver_testdata.Server
  srv = newServerImage(srv)
  srv = newServerGroupcache(srv)
  return srv
}

func newServerImage(srv imageserver.Server) imageserver.Server {
  return &imageserver.HandlerServer{
    Server: srv,
    Handler: &imageserver_image.Handler{
      Processor: &imageserver_image_gift.ResizeProcessor{
        DefaultResampling: gift.LanczosResampling,
      },
    },
  }
}

func newServerGroupcache(srv imageserver.Server) imageserver.Server {
  if flagGroupcache <= 0 {
    return srv
  }
  return imageserver_cache_groupcache.NewServer(
    srv,
    imageserver_cache.NewParamsHashKeyGenerator(sha256.New),
    groupcacheName,
    flagGroupcache,
  )
}

func initGroupcacheHTTPPool() {
  self := (&url.URL{Scheme: "http", Host: flagHTTP}).String()
  var peers []string
  peers = append(peers, self)
  for _, p := range strings.Split(flagGroupcachePeers, ",") {
    if p == "" {
      continue
    }
    peer := (&url.URL{Scheme: "http", Host: p}).String()
    peers = append(peers, peer)
  }
  pool := groupcache.NewHTTPPool(self)
  pool.Context = imageserver_cache_groupcache.HTTPPoolContext
  pool.Transport = imageserver_cache_groupcache.NewHTTPPoolTransport(nil)
  pool.Set(peers...)
}

func groupcacheStatsHTTPHandler(w http.ResponseWriter, req *http.Request) {
  gp := groupcache.GetGroup(groupcacheName)
  if gp == nil {
    http.Error(w, fmt.Sprintf("group %s not found", groupcacheName), http.StatusServiceUnavailable)
    return
  }
  type cachesStats struct {
    Main groupcache.CacheStats
    Hot  groupcache.CacheStats
  }
  type stats struct {
    Group  groupcache.Stats
    Caches cachesStats
  }
  data, err := json.MarshalIndent(
    stats{
      Group: gp.Stats,
      Caches: cachesStats{
        Main: gp.CacheStats(groupcache.MainCache),
        Hot:  gp.CacheStats(groupcache.HotCache),
      },
    },
    "",
    "	",
  )
  if err != nil {
    http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
    return
  }
  _, _ = w.Write(data)
}