基本配置
构建三个节点,一主两从。
之所以是三节点,是因为哨兵模式(一种高可用)需要最少三个才稳健。
✨这里的三个指的是哨兵进程,而不是redis进程。redis可以只有主从两个。
服务类型 | 是否是主服务器 | IP地址 | 端口 |
---|---|---|---|
Redis | 是 | 10.200.16.51 | 6379 |
Redis | 否 | 10.200.16.52 | 6379 |
Redis | 否 | 10.200.16.53 | 6379 |
Sentinel | - | 10.200.16.51 | 26379 |
Sentinel | - | 10.200.16.52 | 26379 |
Sentinel | - | 10.200.16.53 | 26379 |
哨兵模式下,不应该使用的东西:
💥不应该使用主机名,因为当前哨兵模式下支持度不行(6.2版本有提到相关配置,但默认是禁止)。
💥不应该使用docker部署,除非你采用主机网络模式。原因在于:
- 哨兵进程基于"hello"信息相互发现,而"hello"信息里包含的IP和端口均为容器内端口,而不是映射后的端口信息。
- 哨兵进程通过master的info信息检测副本,而info信息依然不是映射后的端口。这就导致哨兵不知道副本信息,也就没法进行故障转移。
✨你可以使用docker主机网络模式,因为这种网络模式下不存在端口映射。
主配置
# Base
bind 0.0.0.0
protected-mode yes
port 6379
daemonize yes
pidfile "/export/redis_6379/redis.pid"
loglevel warning
logfile "/export/redis_6379/logs/redis.log"
dir "/export/redis_6379/data/"
# Auth
requirepass 123456
# Replica master
## 必须有一个从服务,才允许写入
min-replicas-to-write 1
## 从服务有10秒时间来发送异步确认,若超过则认为从不可用,进而因为 min-replicas-to-write 1 导致不可写入
min-replicas-max-lag 10
# Replica slave
## master 认证
#masterauth <master-password>
## 指定 master 地址
#replicaof <masterip> <masterport>
replica-read-only yes
replica-priority 100
# Memory
maxmemory 1g
maxmemory-policy allkeys-lru
# RDB
save 900 1
save 300 10
save 60 10000
dbfilename redis_6379.rdb
# AOF
appendonly yes
appendfilename appendonly_6379.aof
appendfsync everysec
no-appendfsync-on-rewrite no
aof-use-rdb-preamble yes
# rewrite command
rename-command FLUSHDB GOD_FLUSHDB
rename-command FLUSHALL GOD_FLUSHALL
#rename-command CONFIG GOD_CONFIG
rename-command KEYS GOD_KEYS
从节点
将主配置里略加修改即可
## master 认证
masterauth <master-password>
## 指定 master 地址
replicaof <masterip> <masterport>
命令行方式:
-> bin/redis-cli -h 127.0.0.1 -p 6379 -a 123456 replicaof <master_ip> 6379
链接状态
主节点:
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:1
slave0:ip=10.200.16.52,port=6379,state=online,offset=309,lag=1
……
从节点:
127.0.0.1:6379> INFO replication
# Replication
role:slave
master_host:10.200.16.51
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:365
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
从升级主
当主不可用的时候,从执行命令可升级为主
-> bin/redis-cli -h 127.0.0.1 -p 6379 -a 123456 SLAVEOF NO ONE
OK
健康检测
-> bin/redis-cli -h 127.0.0.1 -p 6379 -a 123456 monitor
OK
1448515184.249169 [0 10.86.255.166:6379] "PING"
可以通过ping
指令来确认节点是否正常。
-> bin/redis-cli -h 127.0.0.1 -p 6379 -a 123456 ping
PONG
如何实现主从自动切换
- 通过编写keepalived脚本来实现健康检测,从而实现VIP漂移。
- keepalived方式不适合云环境,因为没法实现vip。
- 通过官方sentinel哨兵来进行检测。
- 哨兵方式因为没有vip,所以需要客户端库实现及时获取正确的主节点
哨兵模式
https://redis.io/topics/sentinel
哨兵模式,通过哨兵来监控master节点,并通过调用master节点info命令中发现slave节点。
当故障切换的时候,哨兵需要执行config指令来修改配置信息。
💥因此,info和config两个指令不可自定义别名。
哨兵分为主观下线(sdown)和客观下线(odown)。主观下线就是自己监控master down掉,但此时还不会切换,客观下线就是其它哨兵也发现有问题,则此时满足客观下线条件。
当客观下线也满足的时候,就会开始选出执行故障转移的哨兵领导者(一个哨兵不会在两次连续的故障中被选为领导者)。
领导者选出一个从redis,并发送slaveof no one指令将其转为主。
之后,发送修改配置指令给其它节点,变更配置指向新主。
最后执行主切换指令。在此之后,通过哨兵查询主节点就会返回新的主地址给客户端。
配置
redis-sentinel.conf
protected-mode no
port 26379
daemonize yes
pidfile "/export/redis_6379/redis-sentinel.pid"
logfile "/export/redis_6379/logs/redis-sentinel.log"
dir "/export/redis_6379/data"
# 配置监听的主服务器
## mymaster 自定义一个主服务器的昵称
## 代表监控的主服务器
## 6379代表端口
## 2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。
sentinel monitor mymaster <主redis的ip> 6379 2
# 访问master所需的认证账户密码
## mymaster 自定义的主服务器昵称
## 123456 master 配置的密码
sentinel auth-pass mymaster <主redis的密码>
# 客户端访问哨兵所需的auth认证
requirepass <哨兵的密码>
sentinel down-after-milliseconds mymaster 10000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
✨这个配置里的信息会在哨兵启动后自动变更,不要在随意修改:
- 自动写入自己哨兵id
- 自动写入已经发现的其它哨兵信息
- 自动写入已经发现的从redis信息
启动
先启动主服务,然后启动2个从服务器,最后启动三个哨兵
# 启动Redis服务器进程
## 先启动主服务,然后启动从服务器
systemctl start redis
# 启动哨兵进程
## 最后启动哨兵
systemctl start redis-sentinel
状态
$ redis-cli -p 26379
127.0.0.1:26379> sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6379"
7) "runid"
8) "953ae6a589449c13ddefaee3538d356d287f509b"
9) "flags" # 如果master没了,这里会变成 s_down(主观认为) 或者 o_down(客观认为)
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "735"
19) "last-ping-reply"
20) "735"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "126"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "532439"
29) "config-epoch"
30) "1"
31) "num-slaves" # 通过master发现的副本数
32) "1"
33) "num-other-sentinels" # 发现的其它哨兵数
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"
其它状态命令
SENTINEL replicas mymaster # 获取副本信息
SENTINEL sentinels mymaster # 获取其它哨兵信息
客户端需要实时的获取master的地址,否则无法使用,哨兵有API可以拿到。命令行里获取master地址
SENTINEL get-master-addr-by-name mymaster
模拟主故障
bin/redis-cli -p 6379 -a 123456 DEBUG sleep 120
故障信息日志
被选为执行故障转移的领导者哨兵日志如下:
30977:X 15 Mar 2022 18:25:46.361 # +sdown master mymaster 10.200.16.51 6379 # 10.200.16.51 主观下线
30977:X 15 Mar 2022 18:25:46.419 # +odown master mymaster 10.200.16.51 6379 #quorum 2/2 # 10.200.16.51 满足客观下线
30977:X 15 Mar 2022 18:25:46.420 # +new-epoch 3 # 第三次出现问题
30977:X 15 Mar 2022 18:25:46.420 # +try-failover master mymaster 10.200.16.51 6379
30977:X 15 Mar 2022 18:25:46.422 # +vote-for-leader c4f6768af1f0f82e2b094d10e0cdaeba030a5412 3 # 选出哨兵领导,字符串是哨兵ID
30977:X 15 Mar 2022 18:25:46.424 # 2c0702024e54b96dfc750455d4fb927add2ca3fe voted for c4f6768af1f0f82e2b094d10e0cdaeba030a5412 3
30977:X 15 Mar 2022 18:25:46.427 # 52ac0f8bc13ff4ddd3a0557008664742a06db10f voted for c4f6768af1f0f82e2b094d10e0cdaeba030a5412 3
30977:X 15 Mar 2022 18:25:46.523 # +elected-leader master mymaster 10.200.16.51 6379
30977:X 15 Mar 2022 18:25:46.523 # +failover-state-select-slave master mymaster 10.200.16.51 6379
30977:X 15 Mar 2022 18:25:46.585 # +selected-slave slave 10.200.16.52:6379 10.200.16.52 6379 @ mymaster 10.200.16.51 6379 # 选定 10.200.16.52 从作为备选主
30977:X 15 Mar 2022 18:25:46.585 * +failover-state-send-slaveof-noone slave 10.200.16.52:6379 10.200.16.52 6379 @ mymaster 10.200.16.51 6379 # 发送 slaveof no one ,将 10.200.16.52 升级为主
30977:X 15 Mar 2022 18:25:46.675 * +failover-state-wait-promotion slave 10.200.16.52:6379 10.200.16.52 6379 @ mymaster 10.200.16.51 6379
30977:X 15 Mar 2022 18:25:47.486 # +promoted-slave slave 10.200.16.52:6379 10.200.16.52 6379 @ mymaster 10.200.16.51 6379
30977:X 15 Mar 2022 18:25:47.486 # +failover-state-reconf-slaves master mymaster 10.200.16.51 6379 # 重写 10.200.16.51 配置为从
30977:X 15 Mar 2022 18:25:47.540 * +slave-reconf-sent slave 10.200.16.53:6379 10.200.16.53 6379 @ mymaster 10.200.16.51 6379 # 重写 10.200.16.53 从配置
30977:X 15 Mar 2022 18:25:48.490 * +slave-reconf-inprog slave 10.200.16.53:6379 10.200.16.53 6379 @ mymaster 10.200.16.51 6379
30977:X 15 Mar 2022 18:25:48.491 * +slave-reconf-done slave 10.200.16.53:6379 10.200.16.53 6379 @ mymaster 10.200.16.51 6379
30977:X 15 Mar 2022 18:25:48.542 # -odown master mymaster 10.200.16.51 6379 # 10.200.16.51 客观下线事件结束
30977:X 15 Mar 2022 18:25:48.542 # +failover-end master mymaster 10.200.16.51 6379 # 10.200.16.51 故障转移结束
30977:X 15 Mar 2022 18:25:48.542 # +switch-master mymaster 10.200.16.51 6379 10.200.16.52 6379 # 切换主
30977:X 15 Mar 2022 18:25:48.543 * +slave slave 10.200.16.53:6379 10.200.16.53 6379 @ mymaster 10.200.16.52 6379
30977:X 15 Mar 2022 18:25:48.543 * +slave slave 10.200.16.51:6379 10.200.16.51 6379 @ mymaster 10.200.16.52 6379 # 51成为从
30977:X 15 Mar 2022 18:25:58.554 # +sdown slave 10.200.16.51:6379 10.200.16.51 6379 @ mymaster 10.200.16.52 6379 # 51从主观下线(因为debug指令还未结束)
30977:X 15 Mar 2022 18:30:36.203 # -sdown slave 10.200.16.51:6379 10.200.16.51 6379 @ mymaster 10.200.16.52 6379 # 51从恢复(debug指令结束)
30977:X 15 Mar 2022 18:30:46.159 * +convert-to-slave slave 10.200.16.51:6379 10.200.16.51 6379 @ mymaster 10.200.16.52 6379
监控主节点
-> watch bin/redis-cli -h 127.0.0.1 -a 123456 -p 26379 SENTINEL get-master-addr-by-name mymaster
===
10.200.16.52
6379
添加一个哨兵
只需要正常配置一个哨兵并启动即可
删除一个哨兵
- 停止要删除的哨兵
- 在所有剩余哨兵里执行重置命令
SENTINEL RESET <master昵称>
- 检查哨兵,确认其它哨兵数量是否一致
SENTINEL MASTER <master昵称>
删除一个节点
在你删除一个旧的主节点或者不可用的节点后,应在所有哨兵里执行重置命令
SENTINEL RESET <master昵称>
客户端(JAVA)
网上找的一个JAVA例子=。=
jedis通过配置哨兵列表从而满足从哨兵中动态的获取主节点信息。
💥如果哨兵里配置了requirepass “your_password_here”,则需要jedis支持。
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
public class RedisSentinel {
private static Logger log = Logger.getLogger(RedisSentinel.class);
public static void redisSentinel() {
Set<String> sentinels = new HashSet<String>();
sentinels.add("192.168.170.128:26379");//192.168.170.11
sentinels.add("192.168.170.129:26379");//192.168.170.12
sentinels.add("192.168.170.130:26379");//192.168.170.13
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels);
Jedis jedis = jedisSentinelPool.getResource();
String a = jedis.get("a");
String b = jedis.get("b");
System.out.println("a=" + a);
System.out.println("b=" + b);
jedis.close();
jedisSentinelPool.close();
}
/**
* 测试哨兵转移
*/
public static void redisSentinelTransfer() {
Set<String> sentinels = new HashSet<String>();
sentinels.add("192.168.170.11:26379");
sentinels.add("192.168.170.12:26379");
sentinels.add("192.168.170.13:26379");
@SuppressWarnings("resource")
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels);
Jedis jedis = null;
while(true) {
try {
//不能放在while里面,这样连接池在8次后会被用完,不执行redis方法,也不报错。
//如果放在while,必须回收。
jedis = jedisSentinelPool.getResource();
int randInt = new Random().nextInt(10000);
String key = "k_" + randInt;
String value = "v_" + randInt;
jedis.set(key, value);
log.info(key + " = " + value);
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
log.info(e);
} finally {
if(jedis != null) {
jedis.close();
}
}
}
}
public static void main(String[] args) {
//redisSentinel();
redisSentinelTransfer();
}
}