You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
2.9 KiB
115 lines
2.9 KiB
package writer
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/alibaba/RedisShake/internal/client"
|
|
"github.com/alibaba/RedisShake/internal/entry"
|
|
"github.com/alibaba/RedisShake/internal/log"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const KeySlots = 16384
|
|
|
|
type RedisClusterWriter struct {
|
|
addresses []string
|
|
writers []Writer
|
|
router [KeySlots]Writer
|
|
}
|
|
|
|
func NewRedisClusterWriter(address string, username string, password string, isTls bool) Writer {
|
|
rw := new(RedisClusterWriter)
|
|
|
|
rw.loadClusterNodes(address, username, password, isTls)
|
|
|
|
log.Infof("redisClusterWriter connected to redis cluster successful. addresses=%v", rw.addresses)
|
|
return rw
|
|
}
|
|
|
|
func (r *RedisClusterWriter) loadClusterNodes(address string, username string, password string, isTls bool) {
|
|
client_ := client.NewRedisClient(address, username, password, isTls)
|
|
reply := client_.DoWithStringReply("cluster", "nodes")
|
|
reply = strings.TrimSpace(reply)
|
|
for _, line := range strings.Split(reply, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
words := strings.Split(line, " ")
|
|
if !strings.Contains(words[2], "master") {
|
|
continue
|
|
}
|
|
if len(words) < 9 {
|
|
log.Panicf("invalid cluster nodes line: %s", line)
|
|
}
|
|
log.Infof("redisClusterWriter load cluster nodes. line=%v", line)
|
|
// address
|
|
address := strings.Split(words[1], "@")[0]
|
|
|
|
// handle ipv6 address
|
|
tok := strings.Split(address, ":")
|
|
if len(tok) > 2 {
|
|
// ipv6 address
|
|
port := tok[len(tok)-1]
|
|
|
|
ipv6Addr := strings.Join(tok[:len(tok)-1], ":")
|
|
address = fmt.Sprintf("[%s]:%s", ipv6Addr, port)
|
|
}
|
|
|
|
r.addresses = append(r.addresses, address)
|
|
// writers
|
|
redisWriter := NewRedisWriter(address, username, password, isTls)
|
|
r.writers = append(r.writers, redisWriter)
|
|
// parse slots
|
|
for i := 8; i < len(words); i++ {
|
|
words[i] = strings.TrimSpace(words[i])
|
|
var start, end int
|
|
var err error
|
|
if strings.Contains(words[i], "-") {
|
|
seg := strings.Split(words[i], "-")
|
|
start, err = strconv.Atoi(seg[0])
|
|
if err != nil {
|
|
log.PanicError(err)
|
|
}
|
|
end, err = strconv.Atoi(seg[1])
|
|
if err != nil {
|
|
log.PanicError(err)
|
|
}
|
|
} else {
|
|
start, err = strconv.Atoi(words[i])
|
|
if err != nil {
|
|
log.PanicError(err)
|
|
}
|
|
end = start
|
|
}
|
|
for j := start; j <= end; j++ {
|
|
if r.router[j] != nil {
|
|
log.Panicf("redisClusterWriter: slot %d already occupied", j)
|
|
}
|
|
r.router[j] = redisWriter
|
|
}
|
|
}
|
|
}
|
|
for i := 0; i < KeySlots; i++ {
|
|
if r.router[i] == nil {
|
|
log.Panicf("redisClusterWriter: slot %d not occupied", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *RedisClusterWriter) Write(entry *entry.Entry) {
|
|
if len(entry.Slots) == 0 {
|
|
for _, writer := range r.writers {
|
|
writer.Write(entry)
|
|
}
|
|
return
|
|
}
|
|
|
|
lastSlot := -1
|
|
for _, slot := range entry.Slots {
|
|
if lastSlot == -1 {
|
|
lastSlot = slot
|
|
}
|
|
if slot != lastSlot {
|
|
log.Panicf("CROSSSLOT Keys in request don't hash to the same slot. argv=%v", entry.Argv)
|
|
}
|
|
}
|
|
r.router[lastSlot].Write(entry)
|
|
}
|
|
|