The introduction

  • Following on from the previous article, this implementation uses Memcached as the storage source
  • If you haven’t read the first two, please pay attention
  • The first day
  • The second day
  • Using a third party driver library written by memcached author github.com/bradfitz/go…
  • Case Code address

Define the Store structure

import (
	"fmt"
	"log"
	"time"

	"github.com/18211167516/gocache"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/gogf/gf/util/gconv"
)

type MemcachedStore struct {
	MeClient *memcache.Client
	SizeKey  string // Calculate the number of key names
}

var MemCachedName = "Memcached"
Copy the code

2. Instantiate store and register cache

func NewMemcached(addr string) *MemcachedStore {
	client := memcache.New(addr)

	err := client.Ping()
	iferr ! =nil {
		log.Panic("Cache store Memcached:", err)
	}
	return &MemcachedStore{
		MeClient: client,
		SizeKey:  "size",}}func RegisterMemcached(addr string) {
	gocache.Register(MemCachedName, NewMemcached(addr))
}
Copy the code

3, the method of implementation

// The GC periodically scans to delete expired keys
func (ms *MemcachedStore) GC(a){}func (ms *MemcachedStore) GetStoreName(a) string {
	return MemCachedName
}

func (ms *MemcachedStore) Get(key string) (interface{}, error) {
	item, err := ms.MeClient.Get(key)
	iferr ! =nil {
		return nil, err
	} else {
		return gconv.String(item.Value), err
	}
}

func NewMemItem(key string, value interface{}, d int) *memcache.Item {
	return &memcache.Item{Key: key, Value: gconv.Bytes(value), Expiration: int32(10)}}func (ms *MemcachedStore) Incr(key string, i int) error {
	if _, err := ms.MeClient.Increment(key, uint64(i)); err ! =nil {
		if err == memcache.ErrCacheMiss {
			return ms.Forever(key, gconv.String(i))
		} else {
			return err
		}
	}
	return nil
}

func (ms *MemcachedStore) Decr(key string, i int) error {
	if _, err := ms.MeClient.Decrement(key, uint64(i)); err ! =nil {
		if err == memcache.ErrCacheMiss {
			return ms.Forever(key, gconv.String(i))
		} else {
			return err
		}
	}
	return nil
}

func (ms *MemcachedStore) Set(key string, value interface{}, d int) error {
	item := NewMemItem(key, value, d)
	iferr := ms.MeClient.Set(item); err ! =nil {
		return err
	} else {
		return ms.Incr(ms.SizeKey, 1)}}func (ms *MemcachedStore) Delete(key string) error {

	iferr := ms.MeClient.Delete(key); err ! =nil {
		return err
	} else {
		return ms.Decr(ms.SizeKey, 1)}}func (ms *MemcachedStore) Has(key string) error {
	item, err := ms.MeClient.Get(key)
	iferr ! =nil {
		return err
	}

	if item == nil {
		return fmt.Errorf("Cache store Memcached key:%s not found", key)
	}

	return nil
}

func (ms *MemcachedStore) Forever(key string, value interface{}) error {
	return ms.Set(key, value, 0)}func (ms *MemcachedStore) Clear(a) error {
	return ms.MeClient.DeleteAll()
}

func (ms *MemcachedStore) Size(a) int {
	value, err := ms.Get(ms.SizeKey)
	iferr ! =nil {
		return 0
	} else {
		return gconv.Int(value) - 1}}func (ms *MemcachedStore) GetTTl(key string) (time.Duration, error) {
	iferr := ms.Has(key); err ! =nil {
		return 0, err
	} else {
		item, _ := ms.MeClient.Get(key)
		return time.Duration(item.Expiration), nil}}func (ms *MemcachedStore) IsExpire(key string) (bool, error) {
	iferr := ms.Has(key); err ! =nil {
		return false, err
	} else {
		item, _ := ms.MeClient.Get(key)
		return item.Expiration > 0.nil}}Copy the code

4. Problems encountered

At first I thought it would be easy to use memcached, but in fact it was stuck in the Size method. Memcached does not provide a similar method. There is a stats info like Redis to get the system information

If you are careful, you should have noticed that I have added one more step to set and delete. At the same time, I will share with you why I wrote this way

func (ms *MemcachedStore) Incr(key string, i int) error {  
	if _, err := ms.MeClient.Increment(key, uint64(i)); err ! =nil { / / the first line
		if err == memcache.ErrCacheMiss {                            / / the second line
			return ms.Forever(key, gconv.String(i))                  / / the third row
		} else {
			return err
		}
	}
	return nil
}
Copy the code
  • Memcached has a mechanism by which incrment increments and decrement returns a cache miss if the value does not exist
  • In the second line we determine if the error is CachEmiss, which is similar to get
  • In the third line, we permanently store the key that records size, and we also convert an int to a string. If we don’t do this, we’ll get an error

memcache: client error: cannot increment or decrement non-numeric value

5,Usage of the driver library

6, the end

This is the end of 3 days to build exclusive Cache, I hope you can have a little harvest, with progress

7. Series of articles

  • Serial set up a Golang environment
  • Serial 2 Install Gin
  • Serial three defines the directory structure
  • Serial four build case API1
  • Serial five build case API2
  • Serialized six access Swagger interface documents
  • Serialize seven log components
  • Serial eight graceful restart and stop
  • Serialize the external Makefile build
  • Serialize other Cron scheduled tasks
  • Serialized content to create command-line tools
  • 3 days to build exclusive Cache(First day)
  • 3 days to build exclusive Cache(Second day)
  • Third Day: Creating a dedicated Cache