From 81fa9b76903dcce3fc26b3a04ba6a750e2271063 Mon Sep 17 00:00:00 2001 From: Yun Date: Wed, 20 May 2026 19:46:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8D=95=E6=9C=BA=E3=80=81?= =?UTF-8?q?=E5=93=A8=E5=85=B5=E3=80=81=E9=9B=86=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 8 +++--- go.sum | 37 ++++++++++++++---------- options.go | 57 +++++++++++++++++++++++++++++++----- redisx.go | 84 +++++++++++++++++++++++++++++++++++------------------- 4 files changed, 130 insertions(+), 56 deletions(-) diff --git a/go.mod b/go.mod index 00c1c51..2408b92 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module code.yun.ink/pkg/redisx -go 1.20 +go 1.24 -require github.com/go-redis/redis/v8 v8.11.5 +require github.com/redis/go-redis/v9 v9.19.0 require ( - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + go.uber.org/atomic v1.11.0 // indirect ) diff --git a/go.sum b/go.sum index 7342ff8..41952ed 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,22 @@ -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= +github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/options.go b/options.go index 77e4bd5..a69a802 100644 --- a/options.go +++ b/options.go @@ -1,24 +1,67 @@ package redisx type redisOption struct { - addr string - password string - db int - poolSize int + mode RedisMode // 模式:single / cluster / sentinel,默认 single + address []string // 单机/集群地址:["127.0.0.1:6379"] + password string // 密码 + db int // 单机/哨兵 + poolSize int + enableTLS bool `yaml:"enable_tls"` // 是否启用TLS,默认false + sentinels []string `yaml:"sentinels"` // 哨兵模式地址,例如: ["127.0.0.1:26379"] + masterName string `yaml:"master_name"` // 哨兵监控的主节点名称 } +type RedisMode string + +const ( + RedisModeSingle RedisMode = "single" // 单机模式 + RedisModeCluster RedisMode = "cluster" // 集群模式 + RedisModeSentinel RedisMode = "sentinel" // 哨兵模式 +) + func defaultOptions() redisOption { return redisOption{ - addr: "localhost:6379", + mode: "single", + address: []string{"localhost:6379"}, } } type Option func(*redisOption) // 127.0.0.1:6379 -func SetAddress(addr string) Option { +func SetAddress(addrs []string) Option { return func(o *redisOption) { - o.addr = addr + o.address = addrs + } +} + +func SetMode(mode RedisMode) Option { + return func(o *redisOption) { + o.mode = mode + } +} + +func SetPoolSize(size int) Option { + return func(o *redisOption) { + o.poolSize = size + } +} + +func SetEnableTLS(enableTLS bool) Option { + return func(o *redisOption) { + o.enableTLS = enableTLS + } +} + +func SetSentinels(sentinels []string) Option { + return func(o *redisOption) { + o.sentinels = sentinels + } +} + +func SetMasterName(masterName string) Option { + return func(o *redisOption) { + o.masterName = masterName } } diff --git a/redisx.go b/redisx.go index 4805c01..c911b4f 100644 --- a/redisx.go +++ b/redisx.go @@ -2,8 +2,9 @@ package redisx import ( "context" + "crypto/tls" - "github.com/go-redis/redis/v8" + redis "github.com/redis/go-redis/v9" ) // PoolSize:连接池中的最大连接数。 @@ -14,43 +15,66 @@ import ( // IdleCheckFrequency:检查空闲连接的频率。 // 单机 -func NewRedis(opts ...Option) redis.UniversalClient { +func NewRedis(ctx context.Context, opts ...Option) redis.UniversalClient { opt := defaultOptions() for _, apply := range opts { apply(&opt) } - client := redis.NewClient(&redis.Options{ - Addr: opt.addr, - Password: opt.password, // no password set - DB: opt.db, // use default DB + var tlsConfig *tls.Config + if opt.enableTLS { + tlsConfig = &tls.Config{InsecureSkipVerify: true} + } - // 连接池配置参数 - // PoolSize: 100, // 连接池最大连接数 - // MinIdleConns: 10, // 最小空闲连接数 - // MaxConnAge: 30 * time.Minute, // 连接最大寿命 - // PoolTimeout: 4 * time.Second, // 等待连接池连接的最长时间 - // IdleTimeout: 5 * time.Minute, // 空闲连接的生命周期 - // IdleCheckFrequency: 60 * time.Second, // 空闲连接检查频率 - }) - _, err := client.Ping(context.Background()).Result() + var client redis.UniversalClient + + switch opt.mode { + case RedisModeCluster: + // 集群模式 + if len(opt.address) == 0 { + panic("redis cluster mode requires at least one address") + } + client = redis.NewClusterClient( + &redis.ClusterOptions{ + Addrs: opt.address, + Password: opt.password, // no password set + TLSConfig: tlsConfig, + }, + ) + case RedisModeSentinel: + // 哨兵模式 + if len(opt.sentinels) == 0 { + panic("redis sentinel mode requires at least one sentinel address") + } + client = redis.NewFailoverClient( + &redis.FailoverOptions{ + MasterName: opt.masterName, + SentinelAddrs: opt.sentinels, + Password: opt.password, // no password set + TLSConfig: tlsConfig, + DB: opt.db, // use default DB + // SentinelPassword: global.Config.Redis.Password, // 哨兵认证密码(如果有) + // SentinelUsername: global.Config.Redis.Username, // 哨兵认证用户名(如果有) + }, + ) + default: + // 单机模式 + if len(opt.address) == 0 { + panic("redis single mode requires an address") + } + client = redis.NewClient( + &redis.Options{ + Addr: opt.address[0], + Password: opt.password, // no password set + DB: opt.db, // use default DB + TLSConfig: tlsConfig, + }, + ) + } + + _, err := client.Ping(ctx).Result() if err != nil { panic(err) } return client } - -// 集群 - -// 哨兵 -// client = redis.NewFailoverClient(&redis.FailoverOptions{ -// MasterName: "mymaster", -// SentinelAddrs: []string{"127.0.0.1:26379", "127.0.0.1:26380"}, -// Password: "", -// DB: 0, -// }) -// for { -// reply, err := client.Incr("pvcont").Result() -// fmt.Printf("reply=%v err=%v\n", reply, err) -// time.Sleep(1 * time.Second) -// }