func NewRedisStore(rawurl string, options ...redis.DialOption) (Store, error) {
	c, err := redis.DialURL(rawurl, options...)
	if err != nil {
		return nil, err
	}
	return &redisStore{conn: c}, nil
}
NewRedisStore creates an store using redis
package restrictor
import (
	"errors"
	"fmt"
	"time"
	"github.com/EagleChen/mapmutex"
	"github.com/garyburd/redigo/redis"
	"github.com/gogo/protobuf/proto"
	cache "github.com/patrickmn/go-cache"
)
var unlockScript = redis.NewScript(1, `
	if redis.call("get",KEYS[1]) == ARGV[1] then
		return redis.call("del",KEYS[1])
	else
		return 0
	end
`)
type Store interface {
	GetLimiter(key string) (*Limiter, time.Time, bool)
	SetLimiter(key string, value *Limiter, expireAfter int) error
	TryLock(key, mark string) (bool, error)
	Unlock(key, mark string) error
}
type localCacheStore struct {
	cache cache.Cache
	m     *mapmutex.Mutex
}
func (l *localCacheStore) GetLimiter(key string) (*Limiter, time.Time, bool) {
	result, t, found := l.cache.GetWithExpiration(key)
	if found {
		return result.(*Limiter), t, true
	}
	return nil, t, false
}
func (l *localCacheStore) SetLimiter(key string, value *Limiter, expireAfter int) error {
	l.cache.Set(key, value, time.Duration(expireAfter)*time.Second)
	return nil
}
func (l *localCacheStore) TryLock(key, mark string) (bool, error) {
	return l.m.TryLock(key), nil
}
func (l *localCacheStore) Unlock(key, mark string) error {
	l.m.Unlock(key)
	return nil
}
type redisStore struct {
	conn redis.Conn
}
func (r *redisStore) GetLimiter(key string) (*Limiter, time.Time, bool) {
	now := time.Now()
	r.conn.Send("MULTI")
	r.conn.Send("GET", key)
	r.conn.Send("TTL", key)
	reply, err := redis.Values(r.conn.Do("EXEC"))
	if err != nil {
		return nil, now, false
	}
	r1, ok := reply[0].([]byte)
	if !ok {
		return nil, now, false
	}
	ttl, ok := reply[1].(int64)
	if !ok {
		return nil, now, false
	}
	var l Limiter
	if err = proto.Unmarshal(r1, &l); err != nil {
		return nil, now, false
	}
	return &l, now.Add(time.Duration(ttl) * time.Second), true
}
func (r *redisStore) SetLimiter(key string, l *Limiter, expireAfter int) error {
	out, err := proto.Marshal(l)
	if err != nil {
		return err
	}
	reply, err := redis.String(r.conn.Do("SETEX", key, expireAfter, out))
	if err != nil {
		return err
	}
	if reply != "OK" {
		return fmt.Errorf("redis err reply: %s", reply)
	}
	return nil
}
func (r *redisStore) TryLock(key, mark string) (bool, error) {
	reply, err := redis.String(r.conn.Do("SET", key, mark,
		"NX", "EX", 3))  
	if err != nil {
		return false, fmt.Errorf("fail to call trylock: %w", err)
	}
	if reply != "OK" {
		return false, fmt.Errorf("redis err reply: %s", reply)
	}
	return true, nil
}
func (r *redisStore) Unlock(key, mark string) error {
	if res, err := redis.Int(unlockScript.Do(r.conn, key, mark)); err != nil {
		return err
	} else if res != 1 {
		return errors.New("Unlock failed, key or value incorrect")
	}
	return nil
}
func NewMemoryStore() (Store, error) {
	c := cache.New(cache.DefaultExpiration, 10*time.Minute)
	l := localCacheStore{
		cache: *c,
		m:     mapmutex.NewMapMutex(),
	}
	return &l, nil
}