package client import ( "bytes" "errors" "io/ioutil" "net" "net/http" "strings" "github.com/buraksezer/consistent" "github.com/cespare/xxhash" ) type Client struct { hash *consistent.Consistent } type hasher struct{} func (h hasher) Sum64(data []byte) uint64 { return xxhash.Sum64(data) } type netAddr string func (na netAddr) String() string { return string(na) } func (na netAddr) Network() string { return "tcp" } func New(addrs ...string) (*Client, error) { cfg := consistent.Config{ PartitionCount: 71, ReplicationFactor: 20, Load: 1.25, Hasher: hasher{}, } hash := consistent.New(nil, cfg) for _, addr := range addrs { hash.Add(netAddr(addr)) } for _, addr := range hash.GetMembers() { conn, err := net.Dial("tcp", strings.TrimPrefix(strings.TrimPrefix(addr.String(), "http://"), "https://")) if err != nil { return nil, err } conn.Close() } return &Client{ hash: hash, }, nil } func (c *Client) Get(key string) ([]byte, error) { addr := c.hash.LocateKey([]byte(key)).String() resp, err := http.Get(addr + "/" + key) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode == http.StatusNotFound { return nil, nil } if resp.StatusCode != http.StatusOK { return nil, errors.New("bad status on get") } return ioutil.ReadAll(resp.Body) } func (c *Client) Set(key string, value []byte) error { addr := c.hash.LocateKey([]byte(key)).String() r, err := http.NewRequest("PUT", addr+"/"+key, bytes.NewBuffer(value)) if err != nil { return err } resp, err := (&http.Client{}).Do(r) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return errors.New("bad status on set") } return nil }