1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-02-04 21:02:25 +08:00
Files
wechat/cache/redis.go
Gophlet 7d93d1b9c8 feat(redis): 优化配置语义并增强超时与连接池能力,同时保持向后兼容 (#869)
* fix: correct non-standard 'yml' tag to 'yaml' in RedisOpts

* fix: apply MaxActive config to Redis PoolSize

* fix: clarify config semantics, enhance timeout & pool options, and maintain backward compatibility

* test: update unit test for redis

* refactor: apply suggestions from code review

* test: add comprehensive coverage for redis

* refactor: resolve funlen linter errors in redis unit tests

* refactor: remove empty else-if branch in NewRedis function
2026-01-08 09:49:41 +08:00

148 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cache
import (
"context"
"crypto/tls"
"net"
"time"
"github.com/go-redis/redis/v8"
)
// Redis .redis cache
type Redis struct {
ctx context.Context
conn redis.UniversalClient
}
// RedisOpts redis 连接属性
type RedisOpts struct {
Host string `json:"host" yaml:"host"`
Username string `json:"username" yaml:"username"`
Password string `json:"password" yaml:"password"`
Database int `json:"database" yaml:"database"`
MinIdleConns int `json:"min_idle_conns" yaml:"min_idle_conns"` // 最小空闲连接数
PoolSize int `json:"pool_size" yaml:"pool_size"` // 连接池大小0 表示使用默认值(即 CPU 核心数 * 10
MaxRetries int `json:"max_retries" yaml:"max_retries"` // 最大重试次数,-1 表示不重试0 表示使用默认值(即 3 次)
DialTimeout int `json:"dial_timeout" yaml:"dial_timeout"` // 连接超时时间0 表示使用默认值(即 5 秒)
ReadTimeout int `json:"read_timeout" yaml:"read_timeout"` // 读取超时时间(秒),-1 表示不超时0 表示使用默认值(即 3 秒)
WriteTimeout int `json:"write_timeout" yaml:"write_timeout"` // 写入超时时间(秒),-1 表示不超时0 表示使用默认值(即 ReadTimeout
PoolTimeout int `json:"pool_timeout" yaml:"pool_timeout"` // 连接池获取连接超时时间0 表示使用默认值(即 ReadTimeout + 1 秒)
IdleTimeout int `json:"idle_timeout" yaml:"idle_timeout"` // 空闲连接超时时间(秒),-1 表示禁用空闲连接超时检查0 表示使用默认值(即 5 分钟)
UseTLS bool `json:"use_tls" yaml:"use_tls"` // 是否使用 TLS
// Deprecated: 应使用 MinIdleConns 代替
MaxIdle int `json:"max_idle" yaml:"max_idle"`
// Deprecated: 应使用 PoolSize 代替
MaxActive int `json:"max_active" yaml:"max_active"`
}
// NewRedis 实例化
func NewRedis(ctx context.Context, opts *RedisOpts) *Redis {
uniOpt := &redis.UniversalOptions{
Addrs: []string{opts.Host},
DB: opts.Database,
Username: opts.Username,
Password: opts.Password,
MinIdleConns: opts.MinIdleConns,
PoolSize: opts.PoolSize,
MaxRetries: opts.MaxRetries,
}
// 兼容旧的 MaxIdle 参数,仅在未显式设置 MinIdleConns 时生效
if opts.MaxIdle > 0 && opts.MinIdleConns == 0 {
uniOpt.MinIdleConns = opts.MaxIdle
}
// 兼容旧的 MaxActive 参数,仅在未显式设置 PoolSize 时生效
if opts.MaxActive > 0 && opts.PoolSize == 0 {
uniOpt.PoolSize = opts.MaxActive
}
applyTimeout := func(seconds int, target *time.Duration) {
if seconds > 0 {
*target = time.Duration(seconds) * time.Second
} else if seconds == -1 {
// 当 seconds 为 -1 时,表示禁用超时:按 go-redis 约定,将超时时间设置为负值(如 -1ns代表「无超时」
*target = -1
}
// 当 seconds 为 0 时,使用 go-redis 的默认超时配置:
// 不修改 target保持其零值0由 go-redis 解释为“使用默认值”
}
applyTimeout(opts.DialTimeout, &uniOpt.DialTimeout)
applyTimeout(opts.ReadTimeout, &uniOpt.ReadTimeout)
applyTimeout(opts.WriteTimeout, &uniOpt.WriteTimeout)
applyTimeout(opts.PoolTimeout, &uniOpt.PoolTimeout)
applyTimeout(opts.IdleTimeout, &uniOpt.IdleTimeout)
if opts.UseTLS {
h, _, err := net.SplitHostPort(opts.Host)
if err != nil {
h = opts.Host
}
uniOpt.TLSConfig = &tls.Config{
ServerName: h,
}
}
conn := redis.NewUniversalClient(uniOpt)
return &Redis{ctx: ctx, conn: conn}
}
// SetConn 设置conn
func (r *Redis) SetConn(conn redis.UniversalClient) {
r.conn = conn
}
// SetRedisCtx 设置redis ctx 参数
func (r *Redis) SetRedisCtx(ctx context.Context) {
r.ctx = ctx
}
// Get 获取一个值
func (r *Redis) Get(key string) interface{} {
return r.GetContext(r.ctx, key)
}
// GetContext 获取一个值
func (r *Redis) GetContext(ctx context.Context, key string) interface{} {
result, err := r.conn.Do(ctx, "GET", key).Result()
if err != nil {
return nil
}
return result
}
// Set 设置一个值
func (r *Redis) Set(key string, val interface{}, timeout time.Duration) error {
return r.SetContext(r.ctx, key, val, timeout)
}
// SetContext 设置一个值
func (r *Redis) SetContext(ctx context.Context, key string, val interface{}, timeout time.Duration) error {
return r.conn.SetEX(ctx, key, val, timeout).Err()
}
// IsExist 判断key是否存在
func (r *Redis) IsExist(key string) bool {
return r.IsExistContext(r.ctx, key)
}
// IsExistContext 判断key是否存在
func (r *Redis) IsExistContext(ctx context.Context, key string) bool {
result, _ := r.conn.Exists(ctx, key).Result()
return result > 0
}
// Delete 删除
func (r *Redis) Delete(key string) error {
return r.DeleteContext(r.ctx, key)
}
// DeleteContext 删除
func (r *Redis) DeleteContext(ctx context.Context, key string) error {
return r.conn.Del(ctx, key).Err()
}