wuxibin89 / redis-go-cluster

redis cluster client implementation in Go
Apache License 2.0
488 stars 145 forks source link

client can connect to Redis cluster, but can not Do(), and node.address is 172.16.7.16:8002@18002 #39

Open liangjfblue opened 4 years ago

liangjfblue commented 4 years ago

redis version: redis-5.0.5 redis-go-cluster version: 1.0.0

my code

cluster, err := redis.NewCluster(
    &redis.Options{
        StartNodes:   nodes,
        ConnTimeout:  50 * time.Millisecond,
        ReadTimeout:  50 * time.Millisecond,
        WriteTimeout: 50 * time.Millisecond,
        KeepAlive:    16,
        AliveTime:    60 * time.Second,
    })
if err != nil {
    return nil, err
}

fmt.Println(redis.String(cluster.Do("GET", "name")))

but when i go run, have this error

=== RUN   TestNewRedisPool
--- FAIL: TestNewRedisPool (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
    panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x544f2a]

goroutine 7 [running]:
testing.tRunner.func1(0xc0000ae100)
    /home/liangjf/app/go/src/testing/testing.go:874 +0x3a3
panic(0x5749a0, 0x6d8890)
    /home/liangjf/app/go/src/runtime/panic.go:679 +0x1b2
github.com/chasex/redis-go-cluster.(*redisConn).shutdown(...)
    /home/liangjf/ljf_home/code/go_home/pkg/mod/github.com/chasex/redis-go-cluster@v1.0.0/node.go:146
github.com/chasex/redis-go-cluster.(*redisNode).do(0xc0000b5200, 0x5ab7fa, 0x3, 0xc00004c760, 0x1, 0x1, 0x10, 0x10, 0xc00004c760, 0x546b0a)
    /home/liangjf/ljf_home/code/go_home/pkg/mod/github.com/chasex/redis-go-cluster@v1.0.0/node.go:192 +0x23a
github.com/chasex/redis-go-cluster.(*redisCluster).Do(0xc0000ac080, 0x5ab7fa, 0x3, 0xc00004c760, 0x1, 0x1, 0x1372e4f5, 0x1372e4f50000000f, 0x5ed5b3ba, 0xc00003af60)
    /home/liangjf/ljf_home/code/go_home/pkg/mod/github.com/chasex/redis-go-cluster@v1.0.0/cluster.go:254 +0x20f
command-line-arguments.(*RedisPool).Get(...)
    /home/liangjf/opensource/gpusher/common/db/redis_string.go:40
command-line-arguments.TestNewRedisPool(0xc0000ae100)
    /home/liangjf/opensource/gpusher/common/db/redis_test.go:23 +0x16b
testing.tRunner(0xc0000ae100, 0x5b5de8)
    /home/liangjf/app/go/src/testing/testing.go:909 +0xc9
created by testing.(*T).Run
    /home/liangjf/app/go/src/testing/testing.go:960 +0x350
FAIL    command-line-arguments  0.006s

at last i find the code:

func (node *redisNode) getConn() (*redisConn, error) {
    ...
    c, err := net.DialTimeout("tcp", node.address, node.connTimeout)
    if err != nil {
        return nil, err
    }
    ...
}

i find that, when panic happend, the node.address is "172.16.7.16:8002@18002"

but when is normal, the node.address is "172.16.7.16:8002"

so when run in debug mode, i modified it manually "172.16.7.16:8002@18002" to "172.16.7.16:8002", it is normal

one by one to follow the code call stack, i find that

//github.com/chasex/redis-go-cluster@v1.0.0/cluster.go:609
func (cluster *redisCluster) updateClustrInfo(node *redisNode) error {
    info, err := String(node.do("CLUSTER", "NODES"))
    infos := strings.Split(strings.Trim(info, "\n"), "\n")

    for i := range fields {
        fields[i] = strings.Split(infos[i], " ")
        if len(fields[i]) < kFieldSlot {
            return fmt.Errorf("missing field: %s [%d] [%d]", infos[i], len(fields[i]), kFieldSlot)
        }

        nodes[fields[i][kFieldAddr]] = &redisNode {
            name: fields[i][kFieldName],
            address: fields[i][kFieldAddr],
            slaves: make([]*redisNode, 0),
            connTimeout: cluster.connTimeout,
            readTimeout: cluster.readTimeout,
            writeTimeout: cluster.writeTimeout,
            keepAlive: cluster.keepAlive,
            aliveTime: cluster.aliveTime,
        }
    }
    ...
}

the function well update the cluster info after connect to cluster, it Mainly process the cluster information returned by the cluster nodes command, and then get the address of each node

such as:

172.16.7.16:8001> cluster nodes
78a8926d4ad002e88f7d499ff4e590f6385b8ffd 172.16.7.16:8005@18005 slave 37a21de453a1118217fed1ec3535463d1bfe5244 0 1591076153884 4 connected
37a21de453a1118217fed1ec3535463d1bfe5244 172.16.7.16:8002@18002 master - 0 1591076151881 2 connected 5462-10922
f83fc4f5b61e4886be681e3b19b952781de9d1bc 172.16.7.16:8003@18003 master - 0 1591076152000 0 connected 10923-16383
b1a7ab40c42a0e8c9c5037aa3df7a701377567cd 172.16.7.16:8006@18006 slave f83fc4f5b61e4886be681e3b19b952781de9d1bc 0 1591076153000 5 connected
3e1c4883e904adde6c4e917c9929954cf14a1a57 172.16.7.16:8001@18001 myself,master - 0 1591076152000 1 connected 0-5461
4e6f4de2fab077ea612b05a4e264cb85a6a9eda7 172.16.7.16:8004@18004 slave 3e1c4883e904adde6c4e917c9929954cf14a1a57 0 1591076151000 3 connected

at this fields[i][kFieldAddr] is 172.16.7.16:8002@18002, so client run Do() well error, becase the address error

i modified as follows:

//github.com/chasex/redis-go-cluster@v1.0.0/cluster.go:622
for i := range fields {
    fields[i] = strings.Split(infos[i], " ")
    if len(fields[i]) < kFieldSlot {
        return fmt.Errorf("missing field: %s [%d] [%d]", infos[i], len(fields[i]), kFieldSlot)
    }

    //added by me: Handling the address field
    if strings.Contains(fields[i][kFieldAddr], "@") {
        fields[i][kFieldAddr] = strings.Split(fields[i][kFieldAddr], "@")[0]
    }

    nodes[fields[i][kFieldAddr]] = &redisNode {
        name: fields[i][kFieldName],
        address: fields[i][kFieldAddr],
        slaves: make([]*redisNode, 0),
        connTimeout: cluster.connTimeout,
        readTimeout: cluster.readTimeout,
        writeTimeout: cluster.writeTimeout,
        keepAlive: cluster.keepAlive,
        aliveTime: cluster.aliveTime,
    }
}

or you can use the master, it had modify the way to updateClusterInfo:

cluster.go:314
func (cluster *Cluster) update(node *redisNode) error {
    info, err := Values(node.do("CLUSTER", "SLOTS"))
    ....
}