基于docker环境和docker-compose
当前redis 6.2.6
mac访问集群时可能出现链接超时问题。是因为mac没有docker0桥接,可参考docker-connettor方案
https://docs.docker.com/desktop/mac/networking/
https://github.com/wenjunxiao/mac-docker-connector/blob/master/README-ZH.md
# 配合brew安装的客户端以及路由文件 可以在mac中使用集群
mac-connector:
image: wenjunxiao/mac-docker-connector
container_name: mac-connector
restart: always
network_mode: host
cap_add:
- NET_ADMIN
redis-cli创建集群时不识别hostname,必须使用ip。ip不固定的话,也可以使用dig命令动态获取,安装dig工具
FROM redis:alpine
RUN echo -e "https://mirrors.aliyun.com/alpine/v3.15/main" > /etc/apk/repositories
RUN apk add bind-tools
CMD ["bash"]
sh -c "redis-cli --cluster create
$(dig redis_6380 +short):6380
$(dig redis_6381 +short):6381
$(dig redis_6382 +short):6382
$(dig redis_6383 +short):6383
$(dig redis_6384 +short):6384
$(dig redis_6385 +short):6385
--cluster-replicas 1"
集群链接时参数 -c
redis-cli -c [...]
redis 6380,6381,6382,6383,6384,6385 组成集群
redis 6379 单机
#端口
port 6379
#ip
bind 0.0.0.0
# 关闭本地访问保护模式
protected-mode no
#rdb 策略
# save "" 关闭rdb
save 900 1 # 900s内至少一次写操作则执行bgsave进行RDB持久化
save 300 10
save 60 10000
#rdb压缩 默认yes 消耗cpu
rdbcompression yes
# 保存rdb文件的时候,进行错误的检查校验,默认yes
rdbchecksum yes
#指定rdb文件的名称,默认是dump.rdb
dbfilename dump.rdb
#aof 策略
appendonly no
#1.no 依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差。
#2.always 每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全。
#3.everysec 每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据。
appendfsync everysec
#指定aof文件的名称,默认为appendonly.aof
appendfilename appendonly.aof
#设置RDB和AOF文件目录
dir /data
#端口
port 6379
#ip
bind 0.0.0.0
# 关闭本地访问保护模式
protected-mode no
#开启集群功能
cluster-enabled yes
#集群节点配置文件名,该文件不是人工编写的,由程序自动生成和修改
cluster-config-file nodes.conf
#集群节点超时时间,配合cluster-replica-validity-factor使用
cluster-node-timeout 15000
#探测集群节点超时不可用的次数,假设cluster-node-timeout设置为1000毫秒,cluster-replica-validity-factor为5,那么1000*5之间内集群节点还不可用的话,会被标记为疑似下线
cluster-replica-validity-factor 5
#主节点下最少的从节点数
cluster-migration-barrier 1
#yes 要求所有主节点正常工作,且所有hash slots被分配到工作的主节点,集群才能提供服务,如果想一部分hash slots即可响应请求,则设置为no
cluster-require-full-coverage yes
#yes 禁止当主节点挂掉时,让从节点不能竞选为主节点
cluster-replica-no-failover no
#rdb 策略
# save "" 关闭rdb
save 900 1 # 900s内至少一次写操作则执行bgsave进行RDB持久化
save 300 10
save 60 10000
#rdb压缩 默认yes 消耗cpu
rdbcompression yes
# 保存rdb文件的时候,进行错误的检查校验,默认yes
rdbchecksum yes
#指定rdb文件的名称,默认是dump.rdb
dbfilename dump.rdb
#aof 策略
appendonly no
#1.no 依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差。
#2.always 每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全。
#3.everysec 每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据。
appendfsync everysec
#指定aof文件的名称,默认为appendonly.aof
appendfilename appendonly.aof
#设置RDB和AOF文件目录
dir /data
# cd redis/
docker compose up .
version: '3.8'
networks:
rd_net:
driver: bridge
name: rd_net
ipam:
config:
- subnet: 172.18.0.0/16
gateway: 172.18.0.1
services:
# 单机 并且 用于创建集群
redis_6379:
image: redis:latest
hostname: redis_6379
container_name: redis_6379
restart: unless-stopped
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
REDIS_PASSWORD: 123456
volumes:
- ./redis-single/redis-6379/data:/data
- ./docker/redis.conf:/etc/redis/redis.conf
ports:
- 6379:6379
depends_on:
- redis_6380
- redis_6381
- redis_6382
- redis_6383
- redis_6384
- redis_6385
networks:
rd_net:
ipv4_address: 172.18.0.8
command:
- sh
- -c
- |
echo yes | redis-cli --cluster create 172.18.0.2:6380 172.18.0.3:6381 172.18.0.4:6382 172.18.0.5:6383 172.18.0.6:6384 172.18.0.7:6385 --cluster-replicas 1
redis-server /etc/redis/redis.conf
# 集群
redis_6380:
image: redis:latest
hostname: redis_6380
container_name: redis_6380
restart: unless-stopped
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
REDIS_PASSWORD: 123456
volumes:
- ./redis-cluster/redis-6380/data:/data
- ./docker/cluster_redis.conf:/etc/redis/redis.conf
ports:
- 6380:6380
networks:
rd_net:
ipv4_address: 172.18.0.2
command: redis-server /etc/redis/redis.conf --port 6380
redis_6381:
image: redis:latest
hostname: redis_6381
container_name: redis_6381
restart: unless-stopped
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
REDIS_PASSWORD: 123456
volumes:
- ./redis-cluster/redis-6381/data:/data
- ./docker/cluster_redis.conf:/etc/redis/redis.conf
ports:
- 6381:6381
networks:
rd_net:
ipv4_address: 172.18.0.3
command: redis-server /etc/redis/redis.conf --port 6381
redis_6382:
image: redis:latest
hostname: redis_6382
container_name: redis_6382
restart: unless-stopped
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
REDIS_PASSWORD: 123456
volumes:
- ./redis-cluster/redis-6382/data:/data
- ./docker/cluster_redis.conf:/etc/redis/redis.conf
ports:
- 6382:6382
networks:
rd_net:
ipv4_address: 172.18.0.4
command: redis-server /etc/redis/redis.conf --port 6382
redis_6383:
image: redis:latest
hostname: redis_6383
container_name: redis_6383
restart: unless-stopped
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
REDIS_PASSWORD: 123456
volumes:
- ./redis-cluster/redis-6383/data:/data
- ./docker/cluster_redis.conf:/etc/redis/redis.conf
ports:
- 6383:6383
networks:
rd_net:
ipv4_address: 172.18.0.5
command: redis-server /etc/redis/redis.conf --port 6383
redis_6384:
image: redis:latest
hostname: redis_6384
container_name: redis_6384
restart: unless-stopped
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
REDIS_PASSWORD: 123456
volumes:
- ./redis-cluster/redis-6384/data:/data
- ./docker/cluster_redis.conf:/etc/redis/redis.conf
ports:
- 6384:6384
networks:
rd_net:
ipv4_address: 172.18.0.6
command: redis-server /etc/redis/redis.conf --port 6384
redis_6385:
image: redis:latest
hostname: redis_6385
container_name: redis_6385
restart: unless-stopped
environment:
TZ: Asia/Shanghai
LANG: en_US.UTF-8
REDIS_PASSWORD: 123456
volumes:
- ./redis-cluster/redis-6385/data:/data
- ./docker/cluster_redis.conf:/etc/redis/redis.conf
ports:
- 6385:6385
networks:
rd_net:
ipv4_address: 172.18.0.7
command: redis-server /etc/redis/redis.conf --port 6385
# mac-docker-connector 服务端
# mac-connector:
# image: wenjunxiao/mac-docker-connector
# container_name: mac-connector
# restart: always
# network_mode: host
# cap_add:
# - NET_ADMIN
go-redis
package main
import (
"context"
"github.com/davecgh/go-spew/spew"
"github.com/go-redis/redis/v8"
)
type Model struct {
Str1 string `redis:"str1"`
Str2 string `redis:"str2"`
Int int `redis:"int"`
Bool bool `redis:"bool"`
Ignored struct{} `redis:"-"`
}
func main() {
ctx := context.Background()
// 单例连接
//rdb := redis.NewClient(&redis.Options{
// Addr: ":6379",
//})
// 集群链接
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"127.0.0.1:6380", "127.0.0.1:6381", "127.0.0.1:6382", "127.0.0.1:6383", "127.0.0.1:6384", "127.0.0.1:6385"},
})
// Set some fields.
if _, err := rdb.Pipelined(ctx, func(rdb redis.Pipeliner) error {
rdb.HSet(ctx, "key", "str1", "hello")
rdb.HSet(ctx, "key", "str2", "world")
rdb.HSet(ctx, "key", "int", 123)
rdb.HSet(ctx, "key", "bool", 1)
return nil
}); err != nil {
panic(err)
}
var model1, model2 Model
// Scan all fields into the model.
if err := rdb.HGetAll(ctx, "key").Scan(&model1); err != nil {
panic(err)
}
// Or scan a subset of the fields.
if err := rdb.HMGet(ctx, "key", "str1", "int").Scan(&model2); err != nil {
panic(err)
}
spew.Dump(model1)
spew.Dump(model2)
}
/*
GOROOT=/Users/zyj/go/go1.16 #gosetup
GOPATH=/Users/zyj/go #gosetup
/Users/zyj/go/go1.16/bin/go build -o /private/var/folders/18/c2k36qyx3hl6d3dg8ccs876r0000gn/T/___1go_build_lib_demo_redis_goredis lib_demo/redis/goredis #gosetup
/private/var/folders/18/c2k36qyx3hl6d3dg8ccs876r0000gn/T/___1go_build_lib_demo_redis_goredis #gosetup
(main.Model) {
Str1: (string) (len=5) "hello",
Str2: (string) (len=5) "world",
Int: (int) 123,
Bool: (bool) true,
Ignored: (struct {}) {
}
}
(main.Model) {
Str1: (string) (len=5) "hello",
Str2: (string) "",
Int: (int) 123,
Bool: (bool) false,
Ignored: (struct {}) {
}
}
Process finished with exit code 0
*/
redisgo(不支持集群)
package main
import (
"github.com/davecgh/go-spew/spew"
"github.com/gomodule/redigo/redis"
"log"
"time"
)
type Model struct {
Str1 string `redis:"str1"`
Str2 string `redis:"str2"`
Int int `redis:"int"`
Bool bool `redis:"bool"`
Ignored struct{} `redis:"-"`
}
func main() {
db := redis.DialDatabase(0) //库
//passwd := redis.DialPassword("123456") //密码
timeout := redis.DialConnectTimeout(5 * time.Second) //连接超时时间
readTimeout := redis.DialReadTimeout(5 * time.Second) //读超时时间
writeTimeout := redis.DialWriteTimeout(5 * time.Second) //写超时时间
//c1, err := redis.Dial("tcp", "127.0.0.1:6379", db, passwd, timeout, readTimeout, writeTimeout) 无密码
c1, err := redis.Dial("tcp", "127.0.0.1:6379", db,timeout, readTimeout, writeTimeout)
if err != nil {
log.Fatalln(err)
}
defer c1.Close()
// Set some fields.
c1.Do("HSET","key", "str1", "hello")
c1.Do("HSET","key", "str2", "world")
c1.Do("HSET","key", "int", 123)
c1.Do("HSET","key", "bool", 1)
var model1, model2 Model
// Scan all fields into the model.
if do, err := c1.Do("HGETALL", "key");err==nil{
redis.ScanStruct(do.([]interface{}),&model1)
}
keys:=[]interface{}{[]byte("str1"),[]byte("int")}
// Or scan a subset of the fields.
if do, err := c1.Do("HMGET", "key","str1", "int");err==nil{
r:=make([]interface{},0)
for i, data := range do.([]interface{}) {
r = append(r,keys[i])
r = append(r, data)
}
redis.ScanStruct(r, &model2)
}
spew.Dump(model1)
spew.Dump(model2)
}
/*
GOROOT=/Users/zyj/go/go1.16 #gosetup
GOPATH=/Users/zyj/go #gosetup
/Users/zyj/go/go1.16/bin/go build -o /private/var/folders/18/c2k36qyx3hl6d3dg8ccs876r0000gn/T/___go_build_lib_demo_redis_redisgo lib_demo/redis/redisgo #gosetup
/private/var/folders/18/c2k36qyx3hl6d3dg8ccs876r0000gn/T/___go_build_lib_demo_redis_redisgo #gosetup
(main.Model) {
Str1: (string) (len=5) "hello",
Str2: (string) (len=5) "world",
Int: (int) 123,
Bool: (bool) true,
Ignored: (struct {}) {
}
}
(main.Model) {
Str1: (string) (len=5) "hello",
Str2: (string) "",
Int: (int) 123,
Bool: (bool) false,
Ignored: (struct {}) {
}
}
Process finished with exit code 0
*/