mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-07 15:10:01 +08:00
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kevwan <1918356+kevwan@users.noreply.github.com> Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
100 lines
2.5 KiB
Go
100 lines
2.5 KiB
Go
package redis
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
red "github.com/redis/go-redis/v9"
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
)
|
|
|
|
// ClosableNode interface represents a closable redis node.
|
|
type ClosableNode interface {
|
|
RedisNode
|
|
Close()
|
|
}
|
|
|
|
// CreateBlockingNode creates a dedicated RedisNode for blocking operations.
|
|
//
|
|
// Blocking Redis commands (like BLPOP, BRPOP, XREADGROUP with block parameter) hold connections
|
|
// for extended periods while waiting for data. Using them with the regular Redis connection pool
|
|
// can exhaust all available connections, causing other operations to fail or timeout.
|
|
//
|
|
// CreateBlockingNode creates a separate Redis client with a minimal connection pool (size 1) that
|
|
// is dedicated to blocking operations. This ensures blocking commands don't interfere with regular
|
|
// Redis operations.
|
|
//
|
|
// Example usage:
|
|
//
|
|
// rds := redis.MustNewRedis(redis.RedisConf{
|
|
// Host: "localhost:6379",
|
|
// Type: redis.NodeType,
|
|
// })
|
|
//
|
|
// // Create a dedicated node for blocking operations
|
|
// node, err := redis.CreateBlockingNode(rds)
|
|
// if err != nil {
|
|
// // handle error
|
|
// }
|
|
// defer node.Close() // Important: close the node when done
|
|
//
|
|
// // Use the node for blocking operations
|
|
// value, err := rds.Blpop(node, "mylist")
|
|
// if err != nil {
|
|
// // handle error
|
|
// }
|
|
//
|
|
// The returned ClosableNode must be closed when no longer needed to release resources.
|
|
func CreateBlockingNode(r *Redis) (ClosableNode, error) {
|
|
timeout := readWriteTimeout + blockingQueryTimeout
|
|
|
|
switch r.Type {
|
|
case NodeType:
|
|
client := red.NewClient(&red.Options{
|
|
Addr: r.Addr,
|
|
Username: r.User,
|
|
Password: r.Pass,
|
|
DB: defaultDatabase,
|
|
MaxRetries: maxRetries,
|
|
PoolSize: 1,
|
|
MinIdleConns: 1,
|
|
ReadTimeout: timeout,
|
|
})
|
|
return &clientBridge{client}, nil
|
|
case ClusterType:
|
|
client := red.NewClusterClient(&red.ClusterOptions{
|
|
Addrs: splitClusterAddrs(r.Addr),
|
|
Username: r.User,
|
|
Password: r.Pass,
|
|
MaxRetries: maxRetries,
|
|
PoolSize: 1,
|
|
MinIdleConns: 1,
|
|
ReadTimeout: timeout,
|
|
})
|
|
return &clusterBridge{client}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unknown redis type: %s", r.Type)
|
|
}
|
|
}
|
|
|
|
type (
|
|
clientBridge struct {
|
|
*red.Client
|
|
}
|
|
|
|
clusterBridge struct {
|
|
*red.ClusterClient
|
|
}
|
|
)
|
|
|
|
func (bridge *clientBridge) Close() {
|
|
if err := bridge.Client.Close(); err != nil {
|
|
logx.Errorf("Error occurred on close redis client: %s", err)
|
|
}
|
|
}
|
|
|
|
func (bridge *clusterBridge) Close() {
|
|
if err := bridge.ClusterClient.Close(); err != nil {
|
|
logx.Errorf("Error occurred on close redis cluster: %s", err)
|
|
}
|
|
}
|