From 5fc12743c36d0612839c579b6d28c31226ff580c Mon Sep 17 00:00:00 2001 From: Rogee Date: Thu, 11 Sep 2025 16:38:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Redis=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=80=89=E9=A1=B9=EF=BC=8C=E5=A2=9E=E5=BC=BA=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E7=AE=A1=E7=90=86=E5=92=8C=E8=B6=85=E6=97=B6=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/providers/redis/config.go.tpl | 116 +++++++++++++++++- .../project/providers/redis/provider.go.tpl | 29 +++-- 2 files changed, 134 insertions(+), 11 deletions(-) diff --git a/templates/project/providers/redis/config.go.tpl b/templates/project/providers/redis/config.go.tpl index 46b7bbf..8f79b85 100644 --- a/templates/project/providers/redis/config.go.tpl +++ b/templates/project/providers/redis/config.go.tpl @@ -2,6 +2,9 @@ package redis import ( "fmt" + "time" + + "github.com/redis/go-redis/v9" "go.ipao.vip/atom/container" "go.ipao.vip/atom/opt" @@ -19,10 +22,25 @@ func DefaultProvider() container.ProviderContainer { } type Config struct { - Host string - Port uint - Password string - DB uint + Host string + Port uint + Password string + DB uint + Username string + ClientName string + + // Pool & retries + PoolSize int + MinIdleConns int + MaxRetries int + + // Timeouts (seconds); 0 = library default + DialTimeoutSeconds uint + ReadTimeoutSeconds uint + WriteTimeoutSeconds uint + ConnMaxIdleTimeSeconds uint + ConnMaxLifetimeSeconds uint + PingTimeoutSeconds uint } func (c *Config) format() { @@ -37,8 +55,98 @@ func (c *Config) format() { if c.DB == 0 { c.DB = 0 } + + if c.PingTimeoutSeconds == 0 { + c.PingTimeoutSeconds = 5 + } } func (c *Config) Addr() string { return fmt.Sprintf("%s:%d", c.Host, c.Port) } + +// DialTimeout returns the dial timeout duration, 0 means library default +func (c *Config) DialTimeout() time.Duration { + if c.DialTimeoutSeconds == 0 { + return 0 + } + return time.Duration(c.DialTimeoutSeconds) * time.Second +} + +// ReadTimeout returns the read timeout duration, 0 means library default +func (c *Config) ReadTimeout() time.Duration { + if c.ReadTimeoutSeconds == 0 { + return 0 + } + return time.Duration(c.ReadTimeoutSeconds) * time.Second +} + +// WriteTimeout returns the write timeout duration, 0 means library default +func (c *Config) WriteTimeout() time.Duration { + if c.WriteTimeoutSeconds == 0 { + return 0 + } + return time.Duration(c.WriteTimeoutSeconds) * time.Second +} + +// ConnMaxIdleTime returns the max idle time for a connection, 0 means default +func (c *Config) ConnMaxIdleTime() time.Duration { + if c.ConnMaxIdleTimeSeconds == 0 { + return 0 + } + return time.Duration(c.ConnMaxIdleTimeSeconds) * time.Second +} + +// ConnMaxLifetime returns the max lifetime for a connection, 0 means default +func (c *Config) ConnMaxLifetime() time.Duration { + if c.ConnMaxLifetimeSeconds == 0 { + return 0 + } + return time.Duration(c.ConnMaxLifetimeSeconds) * time.Second +} + +// PingTimeout returns the ping timeout duration, default 5s +func (c *Config) PingTimeout() time.Duration { + if c.PingTimeoutSeconds == 0 { + return 5 * time.Second + } + return time.Duration(c.PingTimeoutSeconds) * time.Second +} + +// Options builds a redis.Options based on the config values. +// Only non-zero/meaningful values are set, to preserve library defaults. +func (c *Config) Options() *redis.Options { + c.format() + ro := &redis.Options{ + Addr: c.Addr(), + Username: c.Username, + Password: c.Password, + DB: int(c.DB), + ClientName: c.ClientName, + } + if c.PoolSize > 0 { + ro.PoolSize = c.PoolSize + } + if c.MinIdleConns > 0 { + ro.MinIdleConns = c.MinIdleConns + } + if c.MaxRetries > 0 { + ro.MaxRetries = c.MaxRetries + } + if dt := c.DialTimeout(); dt > 0 { + ro.DialTimeout = dt + } + if rt := c.ReadTimeout(); rt > 0 { + ro.ReadTimeout = rt + } + if wt := c.WriteTimeout(); wt > 0 { + ro.WriteTimeout = wt + } + if it := c.ConnMaxIdleTime(); it > 0 { + ro.ConnMaxIdleTime = it + } + if lt := c.ConnMaxLifetime(); lt > 0 { + ro.ConnMaxLifetime = lt + } + return ro +} diff --git a/templates/project/providers/redis/provider.go.tpl b/templates/project/providers/redis/provider.go.tpl index 165d8a8..3a28759 100644 --- a/templates/project/providers/redis/provider.go.tpl +++ b/templates/project/providers/redis/provider.go.tpl @@ -2,9 +2,9 @@ package redis import ( "context" - "time" "github.com/redis/go-redis/v9" + log "github.com/sirupsen/logrus" "go.ipao.vip/atom/container" "go.ipao.vip/atom/opt" ) @@ -17,17 +17,32 @@ func Provide(opts ...opt.Option) error { } config.format() return container.Container.Provide(func() (redis.UniversalClient, error) { - rdb := redis.NewClient(&redis.Options{ - Addr: config.Addr(), - Password: config.Password, - DB: int(config.DB), - }) + // Build options via config helper (encapsulates defaults/decisions) + ro := config.Options() - ctx, _ := context.WithTimeout(context.Background(), 5*time.Second) + // Safe structured log (no password) + log.WithFields(log.Fields{ + "addr": ro.Addr, + "db": ro.DB, + "user": ro.Username, + "client_name": ro.ClientName, + "pool_size": ro.PoolSize, + "min_idle": ro.MinIdleConns, + "retries": ro.MaxRetries, + }).Info("opening Redis connection") + + rdb := redis.NewClient(ro) + + // ping to verify connectivity + ctx, cancel := context.WithTimeout(context.Background(), config.PingTimeout()) + defer cancel() if _, err := rdb.Ping(ctx).Result(); err != nil { return nil, err } + // close hook + container.AddCloseAble(func() { _ = rdb.Close() }) + return rdb, nil }, o.DiOptions()...) }