赞
踩
go-redis里,sentinel只用来获取master和从节点的ip地址,在获取master和replica节点ip时,如果sentinel不可用,那么会换其他的sentinel重试,并将可用的sentinel换到第一个
先通过读锁获取c.sentinel,使用c.sentinel获取主节点信息
如果上面sentinel为空或者出错未获取到主节点,那么可能在此中间有地方重新设置了c.sentinel,继续用读写锁,使用c.sentinel获取主节点信息
如果以上两步的sentinel都有问题,那么遍历所有的sentinelAddr,创建sentinel进行处理
最后如果所有的sentinel都不可用,则报错
func (c *sentinelFailover) replicaAddrs(ctx context.Context, useDisconnected bool) ([]string, error) { // 1. 先通过读锁获取c.sentinel,使用c.sentinel获取主节点信息 c.mu.RLock() sentinel := c.sentinel c.mu.RUnlock() if sentinel != nil { // 通过哨兵查询从节点 addrs, err := c.getReplicaAddrs(ctx, sentinel) if err != nil { if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return nil, err } // Continue on other errors internal.Logger.Printf(ctx, "sentinel: Replicas name=%q failed: %s", c.opt.MasterName, err) } else if len(addrs) > 0 { return addrs, nil } } // 2. 如果上面sentinel为空或者出错未获取到主节点,那么可能在此中间有地方重新设置了c.sentinel,继续用读写锁,使用c.sentinel获取主节点信息 // 分读锁和读写锁这两步的原因是,提高并发性能 c.mu.Lock() defer c.mu.Unlock() if c.sentinel != nil { addrs, err := c.getReplicaAddrs(ctx, c.sentinel) if err != nil { _ = c.closeSentinel() if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return nil, err } // Continue on other errors internal.Logger.Printf(ctx, "sentinel: Replicas name=%q failed: %s", c.opt.MasterName, err) } else if len(addrs) > 0 { return addrs, nil } else { // No error and no replicas. _ = c.closeSentinel() } } // 3. 如果以上两步的sentinel都有问题,那么遍历所有的sentinelAddr,创建sentinel进行处理 var sentinelReachable bool for i, sentinelAddr := range c.sentinelAddrs { sentinel := NewSentinelClient(c.opt.sentinelOptions(sentinelAddr)) replicas, err := sentinel.Replicas(ctx, c.opt.MasterName, c.opt.ServerVersion).Result() if err != nil { _ = sentinel.Close() if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return nil, err } internal.Logger.Printf(ctx, "sentinel: Replicas master=%q failed: %s", c.opt.MasterName, err) continue } sentinelReachable = true addrs := parseReplicaAddrs(replicas, useDisconnected) if len(addrs) == 0 { continue } // Push working sentinel to the top. c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0] c.setSentinel(ctx, sentinel) return addrs, nil } if sentinelReachable { return []string{}, nil } // 4. 最后如果所有的sentinel都不可用,则报错 return []string{}, errors.New("redis: all sentinels specified in configuration are unreachable") }
和获取主节点方法处理逻辑类似
先通过读锁获取c.sentinel,使用c.sentinel获取从节点信息
如果上面sentinel为空或者出错未获取到从节点,那么可能在此中间有地方重新设置了c.sentinel,继续用读写锁,使用c.sentinel获取从节点信息
如果以上两步的sentinel都有问题,那么遍历所有的sentinelAddr,创建sentinel进行处理
最后如果所有的sentinel都不可用,则报错
func (c *sentinelFailover) replicaAddrs(ctx context.Context, useDisconnected bool) ([]string, error) { c.mu.RLock() sentinel := c.sentinel c.mu.RUnlock() if sentinel != nil { // 通过哨兵查询从节点 addrs, err := c.getReplicaAddrs(ctx, sentinel) if err != nil { if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return nil, err } // Continue on other errors internal.Logger.Printf(ctx, "sentinel: Replicas name=%q failed: %s", c.opt.MasterName, err) } else if len(addrs) > 0 { return addrs, nil } } c.mu.Lock() defer c.mu.Unlock() if c.sentinel != nil { addrs, err := c.getReplicaAddrs(ctx, c.sentinel) if err != nil { _ = c.closeSentinel() if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return nil, err } // Continue on other errors internal.Logger.Printf(ctx, "sentinel: Replicas name=%q failed: %s", c.opt.MasterName, err) } else if len(addrs) > 0 { return addrs, nil } else { // No error and no replicas. _ = c.closeSentinel() } } var sentinelReachable bool for i, sentinelAddr := range c.sentinelAddrs { sentinel := NewSentinelClient(c.opt.sentinelOptions(sentinelAddr)) replicas, err := sentinel.Replicas(ctx, c.opt.MasterName, c.opt.ServerVersion).Result() if err != nil { _ = sentinel.Close() if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return nil, err } internal.Logger.Printf(ctx, "sentinel: Replicas master=%q failed: %s", c.opt.MasterName, err) continue } sentinelReachable = true addrs := parseReplicaAddrs(replicas, useDisconnected) if len(addrs) == 0 { continue } // Push working sentinel to the top. c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0] c.setSentinel(ctx, sentinel) return addrs, nil } if sentinelReachable { return []string{}, nil } return []string{}, errors.New("redis: all sentinels specified in configuration are unreachable") }
对于外部调用方来讲,并不关心内部实现是否类似,要的只是获取主节点和从节点信息,分开两个方法,语义很清晰
如果底层合并成一个方法,很容易改动获取master节点的方法,影响到获取从节点的逻辑,造成bug。这也给我们一个启示,不要害怕代码重复,要更关注代码可改动性。如果看着代码类似就合并,很容易造成后面代码改不动,改一个地方,影响其他很多地方
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。