123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- package cache
- import (
- "context"
- "errors"
- "fmt"
- "time"
- "git.bvbej.com/bvbej/base-golang/pkg/time_parse"
- "git.bvbej.com/bvbej/base-golang/pkg/trace"
- "github.com/redis/go-redis/v9"
- )
- type Option func(*option)
- type Trace = trace.T
- type option struct {
- Trace *trace.Trace
- Redis *trace.Redis
- }
- type RedisConfig struct {
- Addr string `yaml:"addr"`
- Pass string `yaml:"pass"`
- DB int `yaml:"db"`
- MaxRetries int `yaml:"maxRetries"` // 最大重试次数
- PoolSize int `yaml:"poolSize"` // Redis连接池大小
- MinIdleConn int `yaml:"minIdleConn"` // 最小空闲连接数
- }
- func newOption() *option {
- return &option{}
- }
- var _ Repo = (*cacheRepo)(nil)
- type Repo interface {
- i()
- Client() *redis.Client
- Set(key, value string, ttl time.Duration, options ...Option) error
- Get(key string, options ...Option) (string, error)
- TTL(key string) (time.Duration, error)
- Expire(key string, ttl time.Duration) bool
- ExpireAt(key string, ttl time.Time) bool
- Del(key string, options ...Option) bool
- Exists(keys ...string) bool
- Incr(key string, options ...Option) (int64, error)
- Decr(key string, options ...Option) (int64, error)
- HGet(key, field string, options ...Option) (string, error)
- HSet(key, field, value string, options ...Option) error
- HDel(key, field string, options ...Option) error
- HGetAll(key string, options ...Option) (map[string]string, error)
- HIncrBy(key, field string, incr int64, options ...Option) (int64, error)
- HIncrByFloat(key, field string, incr float64, options ...Option) (float64, error)
- LPush(key, value string, options ...Option) error
- LLen(key string, options ...Option) (int64, error)
- BRPop(key string, timeout time.Duration, options ...Option) (string, error)
- Close() error
- }
- type cacheRepo struct {
- client *redis.Client
- ctx context.Context
- }
- func New(cfg RedisConfig) (Repo, error) {
- client := redis.NewClient(&redis.Options{
- Addr: cfg.Addr,
- Password: cfg.Pass,
- DB: cfg.DB,
- MaxRetries: cfg.MaxRetries,
- PoolSize: cfg.PoolSize,
- MinIdleConns: cfg.MinIdleConn,
- })
- ctx := context.TODO()
- if err := client.Ping(ctx).Err(); err != nil {
- return nil, errors.Join(err, errors.New("ping redis err"))
- }
- return &cacheRepo{
- client: client,
- ctx: ctx,
- }, nil
- }
- func WithTrace(t Trace) Option {
- return func(opt *option) {
- if t != nil {
- opt.Trace = t.(*trace.Trace)
- opt.Redis = new(trace.Redis)
- }
- }
- }
- func (c *cacheRepo) i() {}
- func (c *cacheRepo) Client() *redis.Client {
- return c.client
- }
- func (c *cacheRepo) Set(key, value string, ttl time.Duration, options ...Option) error {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "set"
- opt.Redis.Key = key
- opt.Redis.Value = value
- opt.Redis.TTL = ttl.Minutes()
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- if err := c.client.Set(c.ctx, key, value, ttl).Err(); err != nil {
- return errors.Join(err, fmt.Errorf("redis set key: %s err", key))
- }
- return nil
- }
- func (c *cacheRepo) Get(key string, options ...Option) (string, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "get"
- opt.Redis.Key = key
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.Get(c.ctx, key).Result()
- if err != nil {
- return "", errors.Join(err, fmt.Errorf("redis get key: %s err", key))
- }
- return value, nil
- }
- func (c *cacheRepo) TTL(key string) (time.Duration, error) {
- ttl, err := c.client.TTL(c.ctx, key).Result()
- if err != nil {
- return -1, errors.Join(err, fmt.Errorf("redis get key: %s err", key))
- }
- return ttl, nil
- }
- func (c *cacheRepo) Expire(key string, ttl time.Duration) bool {
- ok, _ := c.client.Expire(c.ctx, key, ttl).Result()
- return ok
- }
- func (c *cacheRepo) ExpireAt(key string, ttl time.Time) bool {
- ok, _ := c.client.ExpireAt(c.ctx, key, ttl).Result()
- return ok
- }
- func (c *cacheRepo) Exists(keys ...string) bool {
- if len(keys) == 0 {
- return true
- }
- value, _ := c.client.Exists(c.ctx, keys...).Result()
- return value > 0
- }
- func (c *cacheRepo) Del(key string, options ...Option) bool {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "del"
- opt.Redis.Key = key
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- if key == "" {
- return true
- }
- value, _ := c.client.Del(c.ctx, key).Result()
- return value > 0
- }
- func (c *cacheRepo) Incr(key string, options ...Option) (int64, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "incr"
- opt.Redis.Key = key
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.Incr(c.ctx, key).Result()
- if err != nil {
- return 0, errors.Join(err, fmt.Errorf("redis incr key: %s err", key))
- }
- return value, nil
- }
- func (c *cacheRepo) Decr(key string, options ...Option) (int64, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "decr"
- opt.Redis.Key = key
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.Decr(c.ctx, key).Result()
- if err != nil {
- return 0, errors.Join(err, fmt.Errorf("redis decr key: %s err", key))
- }
- return value, nil
- }
- func (c *cacheRepo) HGet(key, field string, options ...Option) (string, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "hash get"
- opt.Redis.Key = key
- opt.Redis.Value = field
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.HGet(c.ctx, key, field).Result()
- if err != nil {
- return "", errors.Join(err, fmt.Errorf("redis hget key: %s field: %s err", key, field))
- }
- return value, nil
- }
- func (c *cacheRepo) HSet(key, field, value string, options ...Option) error {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "hash set"
- opt.Redis.Key = key
- opt.Redis.Value = field + "/" + value
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- if err := c.client.HSet(c.ctx, key, field, value).Err(); err != nil {
- return errors.Join(err, fmt.Errorf("redis hset key: %s field: %s err", key, field))
- }
- return nil
- }
- func (c *cacheRepo) HDel(key, field string, options ...Option) error {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "hash del"
- opt.Redis.Key = key
- opt.Redis.Value = field
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- if err := c.client.HDel(c.ctx, key, field).Err(); err != nil {
- return errors.Join(err, fmt.Errorf("redis hdel key: %s field: %s err", key, field))
- }
- return nil
- }
- func (c *cacheRepo) HGetAll(key string, options ...Option) (map[string]string, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "hash get all"
- opt.Redis.Key = key
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.HGetAll(c.ctx, key).Result()
- if err != nil {
- return nil, errors.Join(err, fmt.Errorf("redis hget all key: %s err", key))
- }
- return value, nil
- }
- func (c *cacheRepo) HIncrBy(key, field string, incr int64, options ...Option) (int64, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "hash incr int64"
- opt.Redis.Key = key
- opt.Redis.Value = fmt.Sprintf("field:%s incr:%d", field, incr)
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.HIncrBy(c.ctx, key, field, incr).Result()
- if err != nil {
- return 0, errors.Join(err, fmt.Errorf("redis hash incr int64 key: %s err", key))
- }
- return value, nil
- }
- func (c *cacheRepo) HIncrByFloat(key, field string, incr float64, options ...Option) (float64, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "hash incr float64"
- opt.Redis.Key = key
- opt.Redis.Value = fmt.Sprintf("field:%s incr:%d", field, incr)
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.HIncrByFloat(c.ctx, key, field, incr).Result()
- if err != nil {
- return 0, errors.Join(err, fmt.Errorf("redis hash incr float64 key: %s err", key))
- }
- return value, nil
- }
- func (c *cacheRepo) LPush(key, value string, options ...Option) error {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "list push"
- opt.Redis.Key = key
- opt.Redis.Value = value
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- _, err := c.client.LPush(c.ctx, key, value).Result()
- if err != nil {
- return errors.Join(err, fmt.Errorf("redis list push key: %s value: %s err", key, value))
- }
- return nil
- }
- func (c *cacheRepo) LLen(key string, options ...Option) (int64, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "list len"
- opt.Redis.Key = key
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.LLen(c.ctx, key).Result()
- if err != nil {
- return 0, errors.Join(err, fmt.Errorf("redis list len key: %s err", key))
- }
- return value, nil
- }
- func (c *cacheRepo) BRPop(key string, timeout time.Duration, options ...Option) (string, error) {
- ts := time.Now()
- opt := newOption()
- defer func() {
- if opt.Trace != nil {
- opt.Redis.Timestamp = time_parse.CSTLayoutString()
- opt.Redis.Handle = "list brpop"
- opt.Redis.Key = key
- opt.Redis.TTL = timeout.Seconds()
- opt.Redis.CostSeconds = time.Since(ts).Seconds()
- opt.Trace.AppendRedis(opt.Redis)
- }
- }()
- for _, f := range options {
- f(opt)
- }
- value, err := c.client.BRPop(c.ctx, timeout, key).Result()
- if err != nil {
- return "", errors.Join(err, fmt.Errorf("redis list len key: %s err", key))
- }
- return value[1], nil
- }
- func (c *cacheRepo) Close() error {
- return c.client.Close()
- }
|