gitee.com/h79/goutils@v1.22.10/dao/redis/adapter.go (about)

     1  package redis
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	commonoption "gitee.com/h79/goutils/common/option"
     8  	"gitee.com/h79/goutils/common/result"
     9  	commontls "gitee.com/h79/goutils/common/tls"
    10  	"gitee.com/h79/goutils/dao/config"
    11  	"gitee.com/h79/goutils/dao/option"
    12  	daotls "gitee.com/h79/goutils/dao/util"
    13  	"github.com/go-redis/redis/v8"
    14  	"go.uber.org/zap"
    15  	"strings"
    16  	"time"
    17  )
    18  
    19  var _ Redis = (*Adapter)(nil)
    20  
    21  type Adapter struct {
    22  	client   *redis.Client
    23  	sentinel *redis.SentinelClient
    24  	cluster  *redis.ClusterClient
    25  	name     string
    26  }
    27  
    28  var DefaultTlsFunc = func(cnf *config.RedisConfig) (*tls.Config, error) {
    29  	if strings.EqualFold(cnf.Tls.Key, "false") {
    30  		return nil, result.RErrNotSupport
    31  	}
    32  	if strings.EqualFold(cnf.Tls.Key, "true") {
    33  		return &tls.Config{}, nil
    34  	}
    35  	if strings.EqualFold(cnf.Tls.Key, "skip-verify") ||
    36  		strings.EqualFold(cnf.Tls.Key, "preferred") {
    37  		return &tls.Config{InsecureSkipVerify: true}, nil
    38  	}
    39  	cert, rootCertPool, err := commontls.GetCertificate(&cnf.Tls)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	return &tls.Config{
    44  		RootCAs:      rootCertPool,
    45  		Certificates: []tls.Certificate{cert},
    46  	}, nil
    47  }
    48  
    49  func WithTlsOption(f func(cnf *config.RedisConfig) (*tls.Config, error)) commonoption.Option {
    50  	return tlsFunc(f)
    51  }
    52  
    53  type tlsFunc func(cnf *config.RedisConfig) (*tls.Config, error)
    54  
    55  func (t tlsFunc) String() string {
    56  	return "redis:tls"
    57  }
    58  func (t tlsFunc) Type() int          { return option.TypeRedisTls }
    59  func (t tlsFunc) Value() interface{} { return t }
    60  
    61  func tlsFuncExist(opts ...commonoption.Option) tlsFunc {
    62  	if r, ok := commonoption.Exist(option.TypeRedisTls, opts...); ok {
    63  		return r.Value().(tlsFunc)
    64  	}
    65  	return nil
    66  }
    67  
    68  func UseTls(cnf *config.RedisConfig, opts ...commonoption.Option) (*tls.Config, error) {
    69  	fn := tlsFuncExist(opts...)
    70  	if fn == nil {
    71  		fn = DefaultTlsFunc
    72  	}
    73  	return fn(cnf)
    74  }
    75  
    76  func NewAdapter(name string, cfg *config.RedisConfig, sentinelCfg *config.RedisConfig, clusterCfg *config.RedisConfig, opts ...commonoption.Option) (*Adapter, error) {
    77  	cli, err := newClient(cfg)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	sentinel, err := newSentinelClient(sentinelCfg)
    82  	if err != nil {
    83  		zap.L().Warn("Redis: newSentinelClient", zap.Error(err))
    84  	}
    85  	cluster, err := newClusterClient(clusterCfg)
    86  	if err != nil {
    87  		zap.L().Warn("Redis: newClusterClient", zap.Error(err))
    88  	}
    89  	return &Adapter{client: cli, sentinel: sentinel, cluster: cluster, name: name}, nil
    90  }
    91  
    92  func newClient(cfg *config.RedisConfig, opts ...commonoption.Option) (*redis.Client, error) {
    93  	if len(cfg.Host) == 0 || len(cfg.Host[0]) <= 0 {
    94  		return nil, result.RErrParam
    95  	}
    96  	tlsCfn, _ := UseTls(cfg, opts...)
    97  	cli := redis.NewClient(&redis.Options{
    98  		Network:      "tcp",
    99  		Addr:         cfg.Host[0],
   100  		Username:     cfg.User,
   101  		Password:     cfg.Pwd, // no password set
   102  		DB:           cfg.DB,  // use default DB
   103  		DialTimeout:  cfg.DialTimeout * time.Second,
   104  		ReadTimeout:  cfg.ReadTimeout * time.Second,
   105  		WriteTimeout: cfg.WriteTimeout * time.Second,
   106  		IdleTimeout:  cfg.IdleTimeout * time.Minute,
   107  		TLSConfig:    tlsCfn,
   108  	})
   109  
   110  	ctx := context.Background()
   111  	if _, err := cli.Ping(ctx).Result(); err != nil {
   112  		daotls.Alarm(result.ErrRdsPingInternal, "", fmt.Sprintf("master redis ping(%s)", cfg.Host), err)
   113  	}
   114  	return cli, nil
   115  }
   116  
   117  func newSentinelClient(cfg *config.RedisConfig, opts ...commonoption.Option) (*redis.SentinelClient, error) {
   118  	if len(cfg.Host) == 0 || len(cfg.Host[0]) <= 0 {
   119  		return nil, result.RErrParam
   120  	}
   121  	tlsCfn, _ := UseTls(cfg, opts...)
   122  	cli := redis.NewSentinelClient(&redis.Options{
   123  		Network:      "tcp",
   124  		Addr:         cfg.Host[0],
   125  		Username:     cfg.User,
   126  		Password:     cfg.Pwd, // no password set
   127  		DB:           cfg.DB,  // use default DB
   128  		DialTimeout:  cfg.DialTimeout * time.Second,
   129  		ReadTimeout:  cfg.ReadTimeout * time.Second,
   130  		WriteTimeout: cfg.WriteTimeout * time.Second,
   131  		IdleTimeout:  cfg.IdleTimeout * time.Minute,
   132  		TLSConfig:    tlsCfn,
   133  	})
   134  
   135  	ctx := context.Background()
   136  	if _, err := cli.Ping(ctx).Result(); err != nil {
   137  		daotls.Alarm(result.ErrRdsPingInternal, "", fmt.Sprintf("sentinel redis ping(%s)", cfg.Host), err)
   138  	}
   139  	return cli, nil
   140  }
   141  
   142  func newClusterClient(cfg *config.RedisConfig, opts ...commonoption.Option) (*redis.ClusterClient, error) {
   143  	if len(cfg.Host) == 0 || len(cfg.Host[0]) <= 0 {
   144  		return nil, result.RErrParam
   145  	}
   146  	tlsCfn, _ := UseTls(cfg, opts...)
   147  	cli := redis.NewClusterClient(&redis.ClusterOptions{
   148  		Addrs:        cfg.Host,
   149  		Username:     cfg.User,
   150  		Password:     cfg.Pwd, // no password set
   151  		DialTimeout:  cfg.DialTimeout * time.Second,
   152  		ReadTimeout:  cfg.ReadTimeout * time.Second,
   153  		WriteTimeout: cfg.WriteTimeout * time.Second,
   154  		IdleTimeout:  cfg.IdleTimeout * time.Minute,
   155  		TLSConfig:    tlsCfn,
   156  	})
   157  
   158  	ctx := context.Background()
   159  	if _, err := cli.Ping(ctx).Result(); err != nil {
   160  		daotls.Alarm(result.ErrRdsPingInternal, "", fmt.Sprintf("cluster redis ping(%s)", cfg.Host), err)
   161  	}
   162  	return cli, nil
   163  }
   164  
   165  func (a *Adapter) Rds() *redis.Client {
   166  	return a.client
   167  }
   168  
   169  func (a *Adapter) Sentinel() *redis.SentinelClient {
   170  	return a.sentinel
   171  }
   172  
   173  func (a *Adapter) Cluster() *redis.ClusterClient {
   174  	return a.cluster
   175  }
   176  
   177  func (a *Adapter) Name() string {
   178  	return a.name
   179  }