网络直播营销方式/北京seo公司哪家好
创建redis client
使用go-redis库进行创建redis客户端比较简单,只需要调用redis.NewClient接口创建一个客户端
redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379",Password: "",DB: 0,
})
NewClient接口只接收一个参数redis.Options,在Options里面存放了所有创建Client需要的参数,我们来具体看下各个参数字段的内容以及使用方式,这些字段包括但不限于:
网络连接相关
-
Network:
- 类型:
string
- 描述:网络类型,可以是
tcp
或unix
。默认值为tcp
。
- 类型:
-
Addr:
- 类型:
string
- 描述:Redis 服务器的地址,格式为
host:port
。
- 类型:
-
Dialer:
- 类型:
func(ctx context.Context, network, addr string) (net.Conn, error)
- 描述:自定义的拨号函数,用于创建网络连接。如果设置了
Dialer
,则Network
和Addr
的设置将失效。
- 类型:
-
OnConnect:
- 类型:
func(ctx context.Context, cn *Conn) error
- 描述:连接建立成功时的回调函数。
- 类型:
-
DialTimeout:
- 类型:
time.Duration
- 描述:拨号超时时间,默认为 5 秒。
- 类型:
-
ReadTimeout:
- 类型:
time.Duration
- 描述:同步等待回复的超时时间。默认为 3 秒,
-1
表示阻塞等待,-2
表示完全禁用SetReadDeadline
调用。
- 类型:
-
WriteTimeout:
- 类型:
time.Duration
- 描述:写操作的超时时间。默认为 3 秒,
-1
表示阻塞等待,-2
表示完全禁用SetWriteDeadline
调用。
- 类型:
-
ContextTimeoutEnabled:
- 类型:
bool
- 描述:是否尊重
Context
上下文的超时时间。默认为false
。
- 类型:
认证和权限相关
-
ClientName:
- 类型:
string
- 描述:每个连接都会执行
CLIENT SETNAME
命令为每个连接设置客户端名字。
- 类型:
-
Username:
- 类型:
string
- 描述:用于 Redis ACL 系统的身份验证用户名。
- 类型:
-
Password:
- 类型:
string
- 描述:用于 Redis ACL 系统的身份验证密码。
- 类型:
-
CredentialsProvider:
- 类型:
func() (username string, password string)
- 描述:允许动态更改用户名和密码。
- 类型:
-
CredentialsProviderContext:
- 类型:
func(ctx context.Context) (username string, password string, err error)
- 描述:增强版的
CredentialsProvider
,存在时会忽略CredentialsProvider
。
- 类型:
协议和功能相关
-
Protocol:
- 类型:
int
- 描述:使用的协议版本,2 或 3。默认值为 3。
- 类型:
-
UnstableResp3:
- 类型:
bool
- 描述:启用 Redis Search 模块的不稳定模式,并使用 RESP3 协议。
- 类型:
连接池相关
-
PoolFIFO:
- 类型:
bool
- 描述:连接池类型,
true
表示 FIFO 连接池,false
表示 LIFO 连接池。默认为false
。
- 类型:
-
PoolSize:
- 类型:
int
- 描述:连接池中基础套接字连接数量。默认情况下每个可用的 CPU 核心会有 10 个连接。
- 类型:
-
PoolTimeout:
- 类型:
time.Duration
- 描述:当所有连接都忙时,客户端从连接池中获取连接的超时时间。默认为
ReadTimeout + 1
,即 6 秒。
- 类型:
-
MinIdleConns:
- 类型:
int
- 描述:连接池中最小空闲连接数量。默认为 0。
- 类型:
-
MaxIdleConns:
- 类型:
int
- 描述:连接池中最大空闲连接数量。默认为 0。
- 类型:
-
MaxActiveConns:
- 类型:
int
- 描述:最大活跃连接数量。0 表示不设限制。
- 类型:
-
ConnMaxIdleTime:
- 类型:
time.Duration
- 描述:连接最长空闲时间。默认为 30 分钟,
-1
表示禁用空闲超时检查。
- 类型:
-
ConnMaxLifetime:
- 类型:
time.Duration
- 描述:连接可以被重用的最大时间。默认不关闭空闲连接。
- 类型:
重试机制
-
MaxRetries:
- 类型:
int
- 描述:尝试次数,默认为 3 次,
-1
表示关闭重试,0 表示不尝试只执行一次。
- 类型:
-
MinRetryBackoff:
- 类型:
time.Duration
- 描述:每次重试之间的最小重试间隔。默认为 8 毫秒,
-1
表示禁用重试间隔。
- 类型:
-
MaxRetryBackoff:
- 类型:
time.Duration
- 描述:每次重试之间最大时间间隔。默认为 512 毫秒,
-1
表示禁用重试间隔。
- 类型:
其他配置
-
DB:
- 类型:
int
- 描述:选择哪个数据库,支持 0-15。
- 类型:
-
TLSConfig:
- 类型:
*tls.Config
- 描述:使用的 TLS 配置。设置后,TLS 将进行协商。
- 类型:
-
Limiter:
- 类型:
Limiter
- 描述:限制器接口,用于实现断路器或速率限制器。
- 类型:
-
readOnly:
- 类型:
bool
- 描述:在备机(slave/follower)节点上使能只读模式。
- 类型:
-
DisableIndentity:
- 类型:
bool
- 描述:是否禁用客户端设置标识符,默认为
false
。
- 类型:
-
IdentitySuffix:
- 类型:
string
- 描述:为客户端名字添加后缀,默认为空。
- 类型:
type Options struct {// 网络类型,tcp or unix 默认 tcpNetwork string// host:port 地址.Addr string// 每个连接都会执行 CLIENT SETNAME ClientName 命令为每个连接设置客户端名字ClientName string// Dialer 会创建网络连接,并且有限Network和Addr,也就是说一旦创建Network和Addr设置的网络连接将失效Dialer func(ctx context.Context, network, addr string) (net.Conn, error)// Hook 当连接建立成功的时候会回调该函数.OnConnect func(ctx context.Context, cn *Conn) error// Protocol 2 or 3. 用来和redis协商使用哪个协议版本的字段// Default is 3.Protocol int//ACL(Access Control List):Redis 6.0 引入了 ACL 系统,用于更细粒度地控制客户端对 Redis 服务器的访问权限。// Username 字段用于在连接到使用 Redis ACL 系统的 Redis 6.0 或更高版本实例时,指定用于身份验证的用户名。Username string// Redis ACL系统支持通过密码认证,该字段就是密码Password string// CredentialsProvider 允许更改用户名和密码,当更新之前这里返回原先的用户名和密码CredentialsProvider func() (username string, password string)// CredentialsProviderContext 是 CredentialsProvider 的增强版本,// CredentialsProviderContext 存在会忽略 CredentialsProvider,后期会合并两个接口只保留一个CredentialsProviderContext func(ctx context.Context) (username string, password string, err error)// 选择哪个数据库,下支持0-15DB int// 尝试次数,默认是3次,-1 (not 0)关闭重试,0不尝试只执行一次MaxRetries int// 每次重试之间的最小重试间隔。默认值为8毫秒;-1表示禁用重试间隔MinRetryBackoff time.Duration// 每次重试之间最大时间间隔,默认为512毫秒,-1表示禁用重试间隔MaxRetryBackoff time.Duration// 拨号超时时间 默认是5秒DialTimeout time.Duration// 同步等待回复超时时间,如果超时命令执行失败// - `0` - 默认 (3 seconds).// - `-1` - 阻塞等待 (block indefinitely).// - `-2` - 完全禁用SetReadDeadline调用ReadTimeout time.Duration// 写超时时间// - `0` - 默认 (3 seconds).// - `-1` - 阻塞等待 (block indefinitely).// - `-2` - 完全禁止SetWriteDeadline调用WriteTimeout time.Duration// ContextTimeoutEnabled 为true的情况下会尊重Context上下文的超时时间,否则会忽略.// See https://redis.uptrace.dev/guide/go-redis-debugging.html#timeoutsContextTimeoutEnabled bool// 连接池类型// true 是 FIFO 连接池, false 代表 LIFO 连接池.// 请注意,FIFO的开销比LIFO略高,// 但它有助于更快地关闭空闲连接,从而减小池的大小。PoolFIFO bool// 连接池中基础套接字连接数量// 默认情况下每个可用的CPU核心会有10个连接 runtime.GOMAXPROCS.// 当连接池中被耗尽时,客户端会被分配额外的连接// 当然你可以使用MaxActiveConns限制连接池大小。PoolSize int// 表示当所有连接都忙时,客户端从连接池中获取连接的超时时间默认ReadTimeout + 1 为 6 秒。// 如果所有连接都在忙,并且客户端在 6 秒内无法获取到连接,则会返回一个错误PoolTimeout time.Duration// 连接池中最小空闲连接数量// Default is 0. 空闲连接默认不会被关闭.MinIdleConns int// 连接池中最大空闲连接数量// Default is 0. 空闲连接默认不会被关闭.MaxIdleConns int// 最大活跃连接数量// 0表示不设限制MaxActiveConns int// ConnMaxIdleTime 一个连接最长空闲时间.// 最后比系统超时时间少,否则将不起作用.//过期的连接可能会在重新使用之前被懒惰地关闭。如果d小于或等于0,则由于连接处于空闲状态,不会关闭连接。//默认值为30分钟。“-1”禁用空闲超时检查。ConnMaxIdleTime time.Duration// ConnMaxLifetime是一个连接可以被重用的最大时间。//// 过期的连接可能会在重用之前惰性关闭。// 如果<= 0,连接不会因为连接的"超期"(age)而关闭。//// 默认不关闭空闲连接。ConnMaxLifetime time.Duration// 使用的TLS配置。设置后,TLS将进行协商。TLSConfig *tls.Config// 限制器接口,用于实现断路器或速率限制器。Limiter Limiter// 在备机 slave/follower 节点上使能只读模式化.readOnly bool// 是否禁用客户端设置标识符,默认false.DisableIndentity bool// 为客户端名字添加后缀,默认空.IdentitySuffix string// EnableUnstable 字段用于启用 Redis Search 模块的不稳定模式(Unstable mode),// 并且该模式使用 RESP3 协议.UnstableResp3 bool
}
用户可以根据需要在创建redis客户端时进行选择性配置。
redis.NewClient的实现
NewClient
函数,用于创建一个新的 Redis 客户端实例。先看下函数调用流程
以下是代码的详细总结:
-
函数签名:
func NewClient(opt *Options) *Client
- 输入参数:
opt *Options
,指向Options
结构体的指针,用于配置 Redis 客户端。 - 返回值:
*Client
,返回一个指向Client
结构体的指针,表示新创建的 Redis 客户端实例。
- 输入参数:
type Client struct {*baseClientcmdablehooksMixin
}
-
初始化
Options
:opt.init()
- 调用
opt.init()
方法,对传入的Options
进行初始化。这一步确保Options
中的某些默认值被正确设置。
- 调用
-
创建
Client
实例:c := Client{baseClient: &baseClient{opt: opt,}, }
- 创建一个新的
Client
实例c
。 baseClient
是Client
的嵌入结构体,用于封装基本的客户端逻辑。- 将初始化后的
Options
传递给baseClient
。
- 创建一个新的
-
初始化
Client
:c.init()
- 调用
c.init()
方法,对Client
实例进行初始化。这一步可能包括设置一些内部状态或初始化其他资源。
- 调用
-
创建连接池:
c.connPool = newConnPool(opt, c.dialHook)
- 调用
newConnPool
函数,创建一个新的连接池connPool
。 newConnPool
函数接受Options
和dialHook
作为参数,返回一个连接池实例。dialHook
是Client
中的一个方法,用于在创建连接时执行一些额外的操作。
- 调用
-
返回
Client
实例:return &c
- 返回初始化完成的
Client
实例。
- 返回初始化完成的
NewClient
函数的主要作用是根据传入的 Options
配置创建并初始化一个新的 Redis 客户端实例。具体步骤包括:
- 初始化
Options
。 - 创建
Client
实例并初始化其嵌入的baseClient
。 - 初始化
Client
实例。 - 创建并设置连接池。
- 返回初始化完成的
Client
实例。
初始化Options
函数签名
// 因为是小写,因此redis包外不能调用
func (opt *Options) init()
- 输入参数:
opt *Options
,指向Options
结构体的指针。 - 返回值:无。
初始化逻辑
-
地址 (
Addr
)if opt.Addr == "" {opt.Addr = "localhost:6379" }
- 如果
Addr
为空,则设置为默认值"localhost:6379"
。
- 如果
-
网络类型 (
Network
)if opt.Network == "" {if strings.HasPrefix(opt.Addr, "/") {opt.Network = "unix"} else {opt.Network = "tcp"} }
- 如果
Network
为空,则根据Addr
的前缀判断是否为 Unix 套接字,如果是则设置Network
为"unix"
,否则设置为"tcp"
。
- 如果
-
连接超时时间 (
DialTimeout
)if opt.DialTimeout == 0 {opt.DialTimeout = 5 * time.Second }
- 如果
DialTimeout
为 0,则设置为默认值5 * time.Second
。
- 如果
-
拨号器 (
Dialer
)if opt.Dialer == nil {opt.Dialer = NewDialer(opt) }
- 如果
Dialer
为nil
,则使用NewDialer
函数创建一个新的拨号器,并赋值给Dialer
。
- 如果
-
连接池大小 (
PoolSize
)if opt.PoolSize == 0 {opt.PoolSize = 10 * runtime.GOMAXPROCS(0) }
- 如果
PoolSize
为 0,则设置为10 * runtime.GOMAXPROCS(0)
,即最大处理器数的 10 倍。
- 如果
-
读取超时时间 (
ReadTimeout
)switch opt.ReadTimeout { case -2:opt.ReadTimeout = -1 case -1:opt.ReadTimeout = 0 case 0:opt.ReadTimeout = 3 * time.Second }
- 根据
ReadTimeout
的不同值进行处理:-2
设置为-1
,完全禁止SetWriteDeadline调用。-1
设置为0
,表示阻塞调用0
设置为默认值3 * time.Second
。
- 根据
-
写入超时时间 (
WriteTimeout
)switch opt.WriteTimeout { case -2:opt.WriteTimeout = -1 case -1:opt.WriteTimeout = 0 case 0:opt.WriteTimeout = opt.ReadTimeout }
- 根据
WriteTimeout
的不同值进行处理:-2
设置为-1
。-1
设置为0
。0
设置为ReadTimeout
的值。
- 根据
-
连接池超时时间 (
PoolTimeout
)if opt.PoolTimeout == 0 {if opt.ReadTimeout > 0 {opt.PoolTimeout = opt.ReadTimeout + time.Second} else {opt.PoolTimeout = 30 * time.Second} }
- 如果
PoolTimeout
为 0,则根据ReadTimeout
的值进行设置:- 如果
ReadTimeout
大于 0,则设置为ReadTimeout + time.Second
。 - 否则设置为默认值
30 * time.Second
。
- 如果
- 如果
-
连接最大空闲时间 (
ConnMaxIdleTime
)
if opt.ConnMaxIdleTime == 0 {opt.ConnMaxIdleTime = 30 * time.Minute
}
- 如果
ConnMaxIdleTime
为 0,则设置为默认值30 * time.Minute
。
-
最大重试次数 (
MaxRetries
)if opt.MaxRetries == -1 {opt.MaxRetries = 0 } else if opt.MaxRetries == 0 {opt.MaxRetries = 3 }
- 如果
MaxRetries
为-1
,则设置为0
。 - 如果
MaxRetries
为0
,则设置为默认值3
。
- 如果
-
最小重试间隔 (
MinRetryBackoff
)switch opt.MinRetryBackoff { case -1:opt.MinRetryBackoff = 0 case 0:opt.MinRetryBackoff = 8 * time.Millisecond }
- 根据
MinRetryBackoff
的不同值进行处理:-1
设置为0
。0
设置为默认值8 * time.Millisecond
。
- 根据
-
最大重试间隔 (
MaxRetryBackoff
)switch opt.MaxRetryBackoff { case -1:opt.MaxRetryBackoff = 0 case 0:opt.MaxRetryBackoff = 512 * time.Millisecond }
- 根据
MaxRetryBackoff
的不同值进行处理:-1
设置为0
。0
设置为默认值512 * time.Millisecond
。
- 根据
Client结构体初始化
按照数据初始化过程,可以得到如下数据结构组织图:
可以将以上数据结构组成分解成如下几个部分:
Client结构体如下:
type Client struct {// 无论是直接将结构体放到这里还是将结构体的指针类型放到这里都能起到"继承"的作用*baseClient// 如果只有类型没有变量这里会创建一个和类型名称相同的成员变量cmdablehooksMixin
}
// NewClient returns a client to the Redis Server specified by Options.
func NewClient(opt *Options) *Client {opt.init()c := Client{baseClient: &baseClient{opt: opt,},}c.init()c.connPool = newConnPool(opt, c.dialHook)c.String()return &c
}
创建Client时只传入了一个opt, 我们来看下Client.init方法里面干了什么
func (c *Client) init() {c.cmdable = c.Processc.initHooks(hooks{dial: c.baseClient.dial,process: c.baseClient.process,pipeline: c.baseClient.processPipeline,txPipeline: c.baseClient.processTxPipeline,})
}
type hooksMixin struct {// 共享锁hooksMu *sync.Mutexslice []Hookinitial hookscurrent hooks
}
// 因为Client继承了hooksMixin,所以这里可以直接调用initHooks
func (hs *hooksMixin) initHooks(hooks hooks) {hs.hooksMu = new(sync.Mutex)hs.initial = hooks// 生成hooks链表,这个hooks可以根据需要中途替换hooks,具体建AddHook方法hs.chain()
}
baseClient.dial方法
func (c *baseClient) dial(ctx context.Context, network, addr string) (net.Conn, error) {return c.opt.Dialer(ctx, network, addr)
}// 用户没有自定义拨号函数的情况下,就使用默认的拨号函数
if opt.Dialer == nil {opt.Dialer = NewDialer(opt)
}func NewDialer(opt *Options) func(context.Context, string, string) (net.Conn, error) {return func(ctx context.Context, network, addr string) (net.Conn, error) {netDialer := &net.Dialer{Timeout: opt.DialTimeout,KeepAlive: 5 * time.Minute,}// 不支持tls直接直接进行context拨号if opt.TLSConfig == nil {return netDialer.DialContext(ctx, network, addr)}return tls.DialWithDialer(netDialer, network, addr, opt.TLSConfig)}
}
baseClient.process
func (c *baseClient) process(ctx context.Context, cmd Cmder) error {var lastErr error// c.opt.MaxRetries尝试次数,默认是3次,-1 (not 0)关闭重试,只执行一次for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {// 这里还需要防止闭包??还是为了编程习惯良好保持的?attempt := attemptretry, err := c._process(ctx, cmd, attempt)if err == nil || !retry {// err == nil 说明成功需要返回// 如果retry为0就算失败return err}lastErr = err}return lastErr
}
func (c *baseClient) _process(ctx context.Context, cmd Cmder, attempt int) (bool, error) {if attempt > 0 {// 每次重试之间的最小重试间隔。默认值为8毫秒;-1表示禁用重试间隔// 每次重试之间最大时间间隔,默认为512毫秒,-1表示禁用重试间隔if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {return false, err}}retryTimeout := uint32(0)if err := c.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error {if err := cn.WithWriter(c.context(ctx), c.opt.WriteTimeout, func(wr *proto.Writer) error {// 发送命令return writeCmd(wr, cmd)}); err != nil {// 进行原子+1 说明发送命令失败,这里需要返回一个失败err 并将retruTimeout技术增加atomic.StoreUint32(&retryTimeout, 1)return err}readReplyFunc := cmd.readReply// Apply unstable RESP3 search module.if c.opt.Protocol != 2 && c.assertUnstableCommand(cmd) {readReplyFunc = cmd.readRawReply}// 读取返回值if err := cn.WithReader(c.context(ctx), c.cmdTimeout(cmd), readReplyFunc); err != nil {if cmd.readTimeout() == nil {atomic.StoreUint32(&retryTimeout, 1)} else {atomic.StoreUint32(&retryTimeout, 0)}return err}return nil}); err != nil {retry := shouldRetry(err, atomic.LoadUint32(&retryTimeout) == 1)return retry, err}return false, nil
}
代码解释
这段代码定义了 baseClient
结构体的 _process
方法,用于实际处理 Redis 命令的执行,并返回是否需要重试以及执行过程中遇到的错误。以下是代码的详细解释:
函数签名
func (c *baseClient) _process(ctx context.Context, cmd Cmder, attempt int) (bool, error)
- 输入参数:
ctx context.Context
:上下文,用于传递请求的生命周期信息和取消信号。cmd Cmder
:表示要执行的 Redis 命令。attempt int
:当前的尝试次数。
- 返回值:
bool
:表示是否需要重试。error
:执行命令过程中遇到的错误。
方法逻辑
-
处理重试间隔
if attempt > 0 {if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {return false, err} }
- 如果当前尝试次数大于 0,调用
internal.Sleep
方法等待一段时间,时间间隔由c.retryBackoff(attempt)
计算得出。 - 如果在等待过程中上下文被取消或超时,返回
false
和相应的错误。
- 如果当前尝试次数大于 0,调用
-
初始化重试超时标志
retryTimeout := uint32(0)
- 声明一个原子变量
retryTimeout
,用于标记是否因超时而需要重试。
- 声明一个原子变量
-
处理连接和命令执行
if err := c.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error {if err := cn.WithWriter(c.context(ctx), c.opt.WriteTimeout, func(wr *proto.Writer) error {return writeCmd(wr, cmd)}); err != nil {atomic.StoreUint32(&retryTimeout, 1)return err}readReplyFunc := cmd.readReply// Apply unstable RESP3 search module.if c.opt.Protocol != 2 && c.assertUnstableCommand(cmd) {readReplyFunc = cmd.readRawReply}if err := cn.WithReader(c.context(ctx), c.cmdTimeout(cmd), readReplyFunc); err != nil {if cmd.readTimeout() == nil {atomic.StoreUint32(&retryTimeout, 1)} else {atomic.StoreUint32(&retryTimeout, 0)}return err}return nil }); err != nil {retry := shouldRetry(err, atomic.LoadUint32(&retryTimeout) == 1)return retry, err }
- 调用
c.withConn
方法获取连接,并在连接上执行命令。 - 使用
cn.WithWriter
方法写入命令:- 调用
writeCmd
方法将命令写入连接。 - 如果写入过程中出错,设置
retryTimeout
为 1 并返回错误。
- 调用
- 根据命令类型选择读取回复的方法:
- 默认使用
cmd.readReply
方法读取回复。 - 如果使用的是 RESP3 协议且命令不稳定,使用
cmd.readRawReply
方法读取原始回复。
- 默认使用
- 使用
cn.WithReader
方法读取回复:- 调用
cmd.readReply
或cmd.readRawReply
方法读取回复。 - 如果读取过程中出错,检查是否因超时而需要重试,设置
retryTimeout
相应的值并返回错误。
- 调用
- 如果命令执行成功,返回
nil
。
- 调用
-
判断是否需要重试
retry := shouldRetry(err, atomic.LoadUint32(&retryTimeout) == 1) return retry, err
- 调用
shouldRetry
方法判断是否需要重试,传入错误和retryTimeout
的值。 - 返回是否需要重试和错误。
- 调用
-
返回成功
return false, nil
- 如果命令执行成功,返回
false
和nil
。
- 如果命令执行成功,返回
详细解析
-
处理重试间隔
- 如果当前尝试次数大于 0,调用
internal.Sleep
方法等待一段时间,时间间隔由c.retryBackoff(attempt)
计算得出。这一步是为了避免频繁重试导致的高负载。 - 如果在等待过程中上下文被取消或超时,返回
false
和相应的错误。
- 如果当前尝试次数大于 0,调用
-
初始化重试超时标志
- 声明一个原子变量
retryTimeout
,用于标记是否因超时而需要重试。初始值为 0。
- 声明一个原子变量
-
处理连接和命令执行
- 调用
c.withConn
方法获取连接,并在连接上执行命令。 - 使用
cn.WithWriter
方法写入命令:- 调用
writeCmd
方法将命令写入连接。 - 如果写入过程中出错,设置
retryTimeout
为 1 并返回错误。
- 调用
- 根据命令类型选择读取回复的方法:
- 默认使用
cmd.readReply
方法读取回复。 - 如果使用的是 RESP3 协议且命令不稳定,使用
cmd.readRawReply
方法读取原始回复。
- 默认使用
- 使用
cn.WithReader
方法读取回复:- 调用
cmd.readReply
或cmd.readRawReply
方法读取回复。 - 如果读取过程中出错,检查是否因超时而需要重试,设置
retryTimeout
相应的值并返回错误。
- 调用
- 如果命令执行成功,返回
nil
。
- 调用
-
判断是否需要重试
- 调用
shouldRetry
方法判断是否需要重试,传入错误和retryTimeout
的值。shouldRetry
方法会根据错误类型和超时情况决定是否需要重试。 - 返回是否需要重试和错误。
- 调用
-
返回成功
- 如果命令执行成功,返回
false
和nil
。
- 如果命令执行成功,返回
baseClient.processPipeline
func (c *baseClient) processPipeline(ctx context.Context, cmds []Cmder) error {if err := c.generalProcessPipeline(ctx, cmds, c.pipelineProcessCmds); err != nil {return err}return cmdsFirstErr(cmds)
}
baseClient.processTxPipeline
func (c *baseClient) processTxPipeline(ctx context.Context, cmds []Cmder) error {if err := c.generalProcessPipeline(ctx, cmds, c.txPipelineProcessCmds); err != nil {return err}return cmdsFirstErr(cmds)
}
redis.newConnPool
redis.newConnPool属于线程池,比较复杂这里不进行说明后面会单独抽一节进行说明
func newConnPool(opt *Options,dialer func(ctx context.Context, network, addr string) (net.Conn, error),
) *pool.ConnPool {return pool.NewConnPool(&pool.Options{Dialer: func(ctx context.Context) (net.Conn, error) {return dialer(ctx, opt.Network, opt.Addr)},PoolFIFO: opt.PoolFIFO,PoolSize: opt.PoolSize,PoolTimeout: opt.PoolTimeout,MinIdleConns: opt.MinIdleConns,MaxIdleConns: opt.MaxIdleConns,MaxActiveConns: opt.MaxActiveConns,ConnMaxIdleTime: opt.ConnMaxIdleTime,ConnMaxLifetime: opt.ConnMaxLifetime,})
}
总结
经过上述过程,一个完整的Client算是创建完成了,后面你就可以使用Client对redis进行操作了
附录
- 数据来源-《go-redis》
- 代码仓库:gitee note_lab
- redis gitee redis
- go-redis gitee go-redis
相关文章:

[go-redis]客户端的创建与配置说明
创建redis client 使用go-redis库进行创建redis客户端比较简单,只需要调用redis.NewClient接口创建一个客户端 redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379",Password: "",DB: 0, })NewClient接口只接收一个参数red…...

Qt入门7——Qt事件
目录 1. Qt事件介绍: 2. 事件的处理 示例1:鼠标进入(enterEvent)与离开事件(leaveEvent) 示例2:鼠标点击事件(mousePressEvent) 示例3:鼠标移动事件(mouseMoveEvent) 3. 按键事件 4. 定时器 5. 窗口事件 1. Qt事件介绍&a…...

CTF之密码学(仓颉编码)
一、仓颉码(用于建立中文索引) 定义与目标: 仓颉码是为了建立中文的索引观念而设计的一种编码方式。其主要目标是方便对中文资料或程式进行索引功能的处理。 工作原理: 仓颉码的索引以ASCII的字符码为基准,但在内部会转…...

面向人工智能安全的多维应对策略
• 制定并实施人工智能伦理框架 国家和行业层面需建立AI伦理原则,将其融入研发与应用中,强化科研人员的伦理培训,推动全球AI伦理框架的制定。 • 加强可信数字内容体系建设 构建可信的互联网内容体系以应对深度伪造带来的安全威胁ÿ…...

考研英语翻译与大小作文
名词动化词 1 持有 harbor2 2 反映 mirror 3 缩短 bridge 4 使用 harness 5 掩饰 mask/veil 6 修改 tailor 7 汇集 pool 8 控制 curb 9 想象 picture 10 激发 trigger 拉丁…...

视频监控汇聚平台Liveweb视频安防监控实时视频监控系统操作方案
Liveweb国标GB28181视频平台是一种基于国标GB/T28181协议的安防视频流媒体能力平台。它支持多种视频功能,包括实时监控直播、录像、检索与回看、语音对讲、云存储、告警以及平台级联等功能。该平台部署简单、可扩展性强,支持全终端、全平台分发接入的视频…...

算法第一弹-----双指针
目录 1.移动零 2.复写零 3.快乐数 4.盛水最多的容器 5.有效三角形的个数 6.查找总价值为目标值的两个商品 7.三数之和 8.四数之和 双指针通常是指在解决问题时,同时使用两个指针(变量,常用来指向数组、链表等数据结构中的元素位置&am…...

linux环境GitLab服务部署安装及使用
一、GitLab介绍 GitLab是利用Ruby onRails一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。 二、GitLab安装 1、先安装相关依赖 yum -y install policycoreutils openssh-server openssh-clients postf…...

MotorCAD:定子绕组中的趋肤效应和邻近效应损耗
MotorCAD 有助于减少定子绕组中的集肤效应和邻近效应损失,优化电动机性能。 了解集肤和邻近效应损失 集肤效应:交流电场在导体中感应出电流回路,增加了中心的磁通链路,导致该位置的电抗更高,结果是电流在表面附近流动…...

R语言机器学习论文(二):数据准备
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据一、数据描述二、数据预处理(一)修改元素名称(二)剔除无关变量(三)缺失值检查(四)重复值检查(五)异常值检查三、描述性统计(一)连续变量数据情…...

FFmpeg:强大的音视频处理工具指南
FFmpeg:强大的音视频处理工具指南 1. FFmpeg简介2. 核心特性2.1 基础功能2.2 支持的格式和编解码器 3. 主要组件3.1 命令行工具3.2 开发库 4. 最新发展5. 安装指南5.1 Windows系统安装5.1.1 直接下载可执行文件5.1.2 使用包管理器安装 5.2 Linux系统安装5.2.1 Ubunt…...

NiFi-从部署到开发(图文详解)
NiFi简介 Apache NiFi 是一款强大的开源数据集成工具,旨在简化数据流的管理、传输和自动化。它提供了直观的用户界面和可视化工具,使用户能够轻松设计、控制和监控复杂的数据流程,NiFi 具备强大的扩展性和可靠性,可用于处理海量数…...

Scala的条件匹配
条件匹配 在 Scala 中,条件匹配主要通过match表达式来实现,它类似于其他语言中的switch语句,但功能更强。 基本语法:match表达式通常与case关键字一起使用。语法格式如下: 输入一段数字,判断属于那个范围…...

如何手搓一个智能激光逗猫棒
背景 最近家里的猫胖了,所以我就想做个逗猫棒。找了一圈市场上的智能逗猫棒,运行轨迹比较单一,互动性不足。 轨迹单一,活动范围有限 而我希望后续可以结合人工智能物联网,通过摄像头来捕捉猫的位置,让小…...

leetcode LCP 开幕式焰火
LCP 44. 开幕式焰火 - 力扣(LeetCode) 「力扣挑战赛」开幕式开始了,空中绽放了一颗二叉树形的巨型焰火。 给定一棵二叉树 root 代表焰火,节点值表示巨型焰火这一位置的颜色种类。请帮小扣计算巨型焰火有多少种不同的颜色。 示例…...

使用GDI对象绘制UI时需要注意的若干细节问题总结
目录 1、一个bitmap不能同时被选进两个dc中 2、CreateCompatibleDC和CreateCompatibleBitmap要使用同一个dc作为参数 3、不能删除已经被选入DC中的GDI对象 4、使用完的GDI对象,要将之释放掉,否则会导致GDI对象泄漏 5、CreateCompatibleBitmap返回错…...

51单片机(STC89C52RC版本)学习笔记(更新中...)
文章目录 参考资料1. 准备工作1.1 win10配置51单片机开发环境1.1 Ubuntu配置51单片机开发环境问题1:mcs51/8051.h依赖于mcs51/lint.h问题2:提示找不到头文件mcs51/8051.h 2. 认识51单片机2.1 STC89C52单片机2.2 管脚图2.3 原理图2.4 按键抖动2.5 头文件说…...

七:仪表盘安装-controller node
一:工具、环境准备-controller node 二:OpenStack环境准备-controller node 三:安装服务-controller node 四:工具、环境准备-compute node 五:OpenStack环境准备-compute node 六:安装服务-compute node 七…...

C++设计模式之外观模式
动机 下图中左边方案的问题在于组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战。 如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系统…...

显卡(Graphics Processing Unit,GPU)比特币挖矿
1. 比特币挖矿基本原理 比特币挖矿是通过参与比特币网络的共识机制——工作量证明(Proof of Work, PoW) 来完成的。具体来说,矿工通过不断尝试不同的哈希值,以解决一个难度逐渐增大的数学问题,从而验证交易并获得比特…...

【SARL】单智能体强化学习(Single-Agent Reinforcement Learning)《纲要》
📢本篇文章是博主强化学习(RL)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅…...

CSS 动画效果实现:图片展示与交互
🌈个人主页:前端青山 🔥系列专栏:Css篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Css篇专栏内容:CSS 动画效果实现:图片展示与交互 前言 在现代网页设计中,动态效果能够显著…...

【机器学习】—Transformers的扩展应用:从NLP到多领域突破
好久不见!喜欢就关注吧~ 云边有个稻草人-CSDN博客 目录 引言 一、Transformer架构解析 (一)、核心组件 (二)、架构图 二、领域扩展:从NLP到更多场景 1. 自然语言处理(NLP) 2…...

Linux权限机制深度解读:系统安全的第一道防线
文章目录 前言‼️一、Linux权限的概念‼️二、Linux权限管理❕2.1 文件访问者的分类(人)❕2.2 文件类型和访问权限(事物属性)✔️1. 文件类型✔️2. 基本权限✔️3. 权限值的表示方法 ❕2.3 文件访问权限的相关设置方法✔️1. ch…...

NineData云原生智能数据管理平台新功能发布|2024年11月版
本月发布 8 项更新,其中重点发布 2 项、功能优化 6 项。 重点发布 数据库 Devops - 数据生成支持多个数据源 NineData 支持在数据库中自动生成符合特定业务场景的随机数据,用于模拟实际生产环境中的数据情况,帮助用户在不使用真实数据的情况…...

Vue中控制组件的挂载位置
在 Vue 中,append-to-body“true” 主要用于一些第三方组件(如 Element UI 或 Ant Design Vue 中的弹出框、下拉菜单等)来控制组件的挂载位置。具体来说,当你设置 append-to-body“true” 时,它会将该组件的 DOM 元素插…...

查看docker容器日志
容器里面的服务运行报错了,要查看容器的日志 要查看 Docker 容器的日志,可以使用 docker logs 命令。以下是一些常见的使用方法: 基本用法 docker logs <container_name_or_id> 查看最近的日志 docker logs --tail 100 <contai…...

Apache Commons工具类库使用整理
文章目录 Apache Commons工具类库分类- commons-lang3字符串工具:StringUtils日期工具:DateUtils数值工具:NumberUtils对象工具:ObjectUtils数组工具:ArrayUtils异常工具:ExceptionUtils枚举工具࿱…...

力扣第89题 格雷编码
题目描述 格雷编码序列是一个二进制数字序列,其中的每两个相邻的数字只有一个二进制位不同。给定一个整数 n,表示格雷编码的位数,要求返回 n 位的格雷编码序列。 示例 1 输入: n 2输出: [0, 1, 3, 2]解释&#x…...

Linux C/C++编程中的多线程编程基本概念
【图书推荐】《Linux C与C一线开发实践(第2版)》_linux c与c一线开发实践pdf-CSDN博客《Linux C与C一线开发实践(第2版)(Linux技术丛书)》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com…...