redis sentinel哨兵的使用

时间:2023-03-09 01:15:17
redis sentinel哨兵的使用

哨兵模式是Redis集群管理的一种方式。

下面以Go语言为例介绍其使用方式。

使用举例

package main
import (
"fmt"
"strings"
"github.com/garyburd/redigo/redis" "github.com/FZambia/sentinel"
) var RedisConnPool *redis.Pool func InitRedisSentinelConnPool() {
redisAddr := "192.168.1.11:26378,192.168.1.22:26378"
redisAddrs := strings.Split(redisAddr, ",")
masterName := "master1" // 根据redis集群具体配置设置 sntnl := &sentinel.Sentinel{
Addrs: redisAddrs,
MasterName: masterName,
Dial: func(addr string) (redis.Conn, error) {
timeout := 500 * time.Millisecond
c, err := redis.DialTimeout("tcp", addr, timeout, timeout, timeout)
if err != nil {
return nil, err
}
return c, nil
},
} RedisConnPool = &redis.Pool{
MaxIdle: redisConfig.MaxIdle,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) {
masterAddr, err := sntnl.MasterAddr()
if err != nil {
return nil, err
}
c, err := redis.Dial("tcp", masterAddr)
if err != nil {
return nil, err
}
return c, nil
},
TestOnBorrow: CheckRedisRole,
}
} func CheckRedisRole(c redis.Conn, t time.Time) error {
if !sentinel.TestRole(c, "master") {
return fmt.Errorf("Role check failed")
} else {
return nil
}
} func main(){
rc := RedisConnPool.Get()
defer rc.Close() for {
reply, err := redis.String(rc.Do("RPOP", "/queue/cmd"))
if err != nil {
if err != redis.ErrNil {
log.Println("Redis RPOP failed:", err)
} fmt.Println("reply:", reply)
break
}// if }// for
}

哨兵方式client端的实现原理

client查询集群中的master节点。

client查询Master

其代码如下:

// MasterAddr returns an address of current Redis master instance.
func (s *Sentinel) MasterAddr() (string, error) {
res, err := s.doUntilSuccess(func(c redis.Conn) (interface{}, error) {
return queryForMaster(c, s.MasterName)
})
if err != nil {
return "", err
}
return res.(string), nil
}

基本过程是:使用redis 服务器地址,创建连接,发送请求,返回Redis Master地址。

连接redis集群使用的是轮询方式(见doUntilSuccess函数)。

doUntilSuccess函数接收查询master的函数queryForMaster作为参数,queryForMaster的代码如下。

查询master节点

func queryForMaster(conn redis.Conn, masterName string) (string, error) {
res, err := redis.Strings(conn.Do("SENTINEL", "get-master-addr-by-name", masterName))
if err != nil {
return "", err
}
if len(res) < 2 {
return "", errors.New("redigo: malformed get-master-addr-by-name reply")
}
masterAddr := net.JoinHostPort(res[0], res[1])
return masterAddr, nil
}

轮询方式连接服务器doUntilSuccess

基本过程如下:

从Redis服务器地址中选择一台机器,尝试连接,并执行查询操作。如果成功,则直接返回结果。并将这台机器地址活跃性权重提升。

如果第一个地址失败,把这个地址从连接池中去掉,并降低活跃性权重。接着,尝试下一个地址。

如果所有地址都失败,则返回错误。

具体代码如下:

func (s *Sentinel) doUntilSuccess(f func(redis.Conn) (interface{}, error)) (interface{}, error) {
s.mu.RLock()
addrs := s.Addrs
s.mu.RUnlock() var lastErr error for _, addr := range addrs {
conn := s.get(addr)
reply, err := f(conn)
conn.Close()
if err != nil {
lastErr = err
s.mu.Lock()
pool, ok := s.pools[addr]
if ok {
pool.Close()
delete(s.pools, addr)
}
s.putToBottom(addr)
s.mu.Unlock()
continue
}
s.putToTop(addr)
return reply, nil
} return nil, NoSentinelsAvailable{lastError: lastErr}
}

参考

https://github.com/garyburd/redigo

https://github.com/FZambia/go-sentinel

https://godoc.org/github.com/FZambia/go-sentinel

https://redis.io/topics/sentinel-clients