sentinel:
上一篇提到了主从切换,sentinel的作用是将这个过程自动化,实现高可用。
它的主要功能有以下几点:
- 不时地监控redis是否按照预期良好地运行;
- 如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);
- 能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址。
Sentinel本身也支持集群,只使用单个sentinel进程来监控redis集群是不可靠的,当sentinel进程宕掉后,sentinel本身也有单点问题。所以有必要将sentinel集群,这样有几个好处:
- 如果只有一个sentinel进程,如果这个进程运行出错,或者是网络堵塞,那么将无法实现redis集群的主备切换(单点问题)。
- 如果有多个sentinel,redis的客户端可以随意地连接任意一个sentinel来获得关于redis集群中的信息。
- sentinel集群自身也需要多数机制,也就是2个sentinel进程时,挂掉一个另一个就不可用了。
在默认情况下,Sentinel 使用TCP端口26379(普通 Redis 服务器使用的是 6379)。Sentinel 接受 Redis 协议格式的命令请求, 所以可以使用 redis-cli 或者任何其他 Redis 客户端来与 Sentinel 进行通讯。
# redis-cli -p 26379
127.0.0.1:26379> ping
PONG
启动sentinel:
配置文件:
port 26329
sentinel monitor myredis 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel notification-script <master-name> <script-path>
- 第一行port指定sentinel端口。
- 第二行monitor配置指示 Sentinel 去监视一个名为 myredis 的主redis实例。Sentinel的配置文件中不必说明此主实例属下的从实例,因为Sentinel实例可以通过询问主实例来获得所有从实例的信息。
行末2表示将这个主实例判断为失效至少需要2个 Sentinel 进程的同意,只要同意 Sentinel 的数量不达标,自动failover就不会执行。 - down-after-milliseconds 选项指定了 Sentinel 认为Redis实例已经失效所需的毫秒数。当实例超过该时间没有返回PING,或者直接返回错误, 那么 Sentinel 将这个实例标记为主观下线(subjectively down,简称 SDOWN )。
- failover-timeout表示如果在该时间(ms)内未能完成failover操作,则认为该failover失败。
- parallel-syncs如果全部从实例一起对新的主实例进行同步, 那么就可能会造成所有从Redis实例在短时间内全部不可用的情况出现。可以通过将这个值设为 1 来保证每次只有一个slave处于不能处理命令请求的状态。
- notification-script指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选。
启动Sentinel:
Sentinel只是一个运行在特殊模式下的 Redis实例, 你可以在启动一个普通 Redis实例时通过给定 –sentinel 选项来启动 Redis Sentinel 实例。如:
redis-server /path/to/sentinel.conf --sentinel
当然也可以运行Redis自带的redis-sentinel,如:
redis-sentinel /path/to/sentinel.conf
Sentinel集群:
1.和其他集群不同,你无须设置其他Sentinel的地址,Sentinel进程可以通过发布与订阅来自动发现正在监视相同主实例的其他Sentinel。当一个 Sentinel 发现一个新的 Sentinel 时,它会将新的 Sentinel 添加到一个列表中,这个列表保存了 Sentinel 已知的,监视同一个主服务器的所有其他Sentinel。
2.Sentinel集群中的Sentinel不会再同一时刻并发去failover同一个master,第一个进行failover的Sentinel如果失败了(上文配置的failover-timeout),另外一个才会重新进行failover,以此类推。
3.当Sentinel将一个slave选举为master并发送SLAVE OF NO ONE
后,即使其它的slave还没针对新master重新配置自己,failover也被认为是成功了。
4.上述过度过程中,若此时重启old master,则redis集群将处于无master状态,此时只能手动修改配置文件,然后重新启动集群.
5.Master-Slave切换后,Sentinel会改写master,slave和sentinel的conf配置文件。
6.一旦一个Sentinel成功地对一个master进行了failover,它将会把关于master的最新配置通过广播形式通知其它sentinel,其它的Sentinel则更新对应master的配置。
例子:
启动redis:
/etc/redis/redis-m.conf
daemonize yes
pidfile /var/run/redis/redis-server-m.pid
port 6379
loglevel notice
logfile /var/log/redis/redis-server-m.log
databases 16
##disable snapshot
save ""
dir /app/redis-m
appendonly yes
appendfilename "appendonly.aof"
##not to be a slave
#slaveof no one
/etc/redis/redis-s1.conf
daemonize yes
pidfile /var/run/redis/redis-server-s1.pid
port 6380
loglevel warning
logfile /var/log/redis/redis-server-s1.log
databases 16
##disable snapshot
save ""
dir /app/redis-s1
appendonly yes
appendfilename "appendonly.aof"
##to be a slave
slaveof 127.0.0.1 6379
/etc/redis/redis-s2.conf
daemonize yes
pidfile /var/run/redis/redis-server-s2.pid
port 6381
loglevel warning
logfile /var/log/redis/redis-server-s2.log
databases 16
##disable snapshot,enable AOF
save ""
dir /app/redis-s2
appendonly yes
appendfilename "appendonly.aof"
##to be a slave
slaveof 127.0.0.1 6379
查看进程实例:
ps -ef | grep redis
root 17560 1 0 09:48 ? 00:00:00 redis-server *:6379
root 17572 1 0 09:48 ? 00:00:00 redis-server *:6380
root 17609 1 0 09:49 ? 00:00:00 redis-server *:6381
redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=547,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=547,lag=1
master_repl_offset:547
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:546
启动sentinel:
sentinel1.conf
port 26379
sentinel monitor myredis 127.0.0.1 6379 2
sentinel down-after-milliseconds myredis 60000
sentinel failover-timeout myredis 180000
sentinel parallel-syncs myredis 1
sentinel notification-script myredis /etc/redis/log-issues.sh
sentinel2.conf
port 26380
sentinel monitor myredis 127.0.0.1 6379 2
sentinel down-after-milliseconds myredis 60000
sentinel failover-timeout myredis 180000
sentinel parallel-syncs myredis 1
sentinel notification-script myredis /etc/redis/log-issues.sh
log-issues.sh
#!/bin/bash
echo "master failovered at `date`" > /root/redis_issues.log
-------启动--------
nohup redis-sentinel /etc/redis/sentinel1.conf
nohup redis-sentinel /etc/redis/sentinel2.conf
连接:
127.0.0.1:26379> sentinel masters
1) 1) "name"
2) "myredis"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6379"
7) "runid"
8) "a88ffd6548e333f3ac895cf1b890ef32a527dd66"
......
37) "parallel-syncs"
38) "1"
39) "notification-script"
40) "/etc/redis/log-issues.sh"
127.0.0.1:26379> sentinel slaves myredis
1) 1) "name"
2) "127.0.0.1:6381"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6381"
......
2) 1) "name"
2) "127.0.0.1:6380"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6380"
sentinel reset : 重置所有名字和给定模式 pattern 相匹配的主服务器。 pattern 参数是一个 Glob 风格的模式。 重置操作清除该sentinel的所保存的所有状态信息,并进行一次重新的发现过程。
127.0.0.1:26379> sentinel reset myredis
(integer) 1
sentinel failover :进行一次主动的failover。即在不询问其他 Sentinel 意见的情况下, 强制开始一次自动故障迁移 。发起故障转移的 Sentinel 会向其他 Sentinel 发送一个新的配置,其他 Sentinel 会根据这个配置进行相应的更新。
127.0.0.1:26379> sentinel failover myredis
OK
master切换到了localhost:6381上:
127.0.0.1:26379> sentinel get-master-addr-by-name myredis
1) "127.0.0.1"
2) "6381"
## redis-cli中
redis-cli
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
客户端:
sentinel 的 failover 过程对客户端是透明的,以Java Jedis为例:
public class RedisSentinelClient {
/**
* @param args
*/
public static void main(String[] args) {
Set sentinels = new HashSet();
sentinels.add(new HostAndPort("172.30.37.73", 26379).toString());
sentinels.add(new HostAndPort("172.30.37.73", 26380).toString());
sentinels.add(new HostAndPort("172.30.37.73", 26381).toString());
JedisSentinelPool sentinelPool = new JedisSentinelPool("myredis", sentinels);
System.out.println("Current master: " + sentinelPool.getCurrentHostMaster().toString());
Jedis master = sentinelPool.getResource();
master.set("username","tom");
sentinelPool.returnResource(master);
Jedis master2 = sentinelPool.getResource();
String value = master2.get("username");
System.out.println("username: " + value);
master2.close();
sentinelPool.destroy();
}
}
cluster:
1.一个 Redis 集群包含 16384 个哈希槽(hash slot),数据库中的每个键都属于这 16384 个哈希槽的其中一个,集群中的每个节点负责处理一部分哈希槽。 例如一个集群有三个节点,其中:
节点 A 负责处理 0 号至 5500 号哈希槽。
节点 B 负责处理 5501 号至 11000 号哈希槽。
节点 C 负责处理 11001 号至 16384 号哈希槽。
这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。例如:
如果用户将新节点 D 添加到集群中, 那么集群只需要将节点 A 、B 、 C 中的某些槽移动到节点 D 就可以了。
如果用户要从集群中移除节点 A , 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C , 然后再移除空白(不包含任何哈希槽)的节点 A 就可以了。
2.Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)。
3.Redis 集群的节点间通过Gossip协议通信。
命令:
//集群(cluster)
CLUSTER INFO 打印集群的信息
CLUSTER NODES 列出集群当前已知的所有节点(node),以及这些节点的相关信息。
//节点(node)
CLUSTER MEET <ip> <port> 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。
CLUSTER FORGET <node_id> 从集群中移除 node_id 指定的节点。
CLUSTER REPLICATE <node_id> 将当前节点设置为 node_id 指定的节点的从节点。
CLUSTER SAVECONFIG 将节点的配置文件保存到硬盘里面。
//槽(slot)
CLUSTER ADDSLOTS <slot> [slot ...] 将一个或多个槽(slot)指派(assign)给当前节点。
CLUSTER DELSLOTS <slot> [slot ...] 移除一个或多个槽对当前节点的指派。
CLUSTER FLUSHSLOTS 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。
CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽 slot 迁移到 node_id 指定的节点中。
CLUSTER SETSLOT <slot> IMPORTING <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点。
CLUSTER SETSLOT <slot> STABLE 取消对槽 slot 的导入(import)或者迁移(migrate)。
//键 (key)
CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上。
CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的键值对数量。
CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键。
启动集群:
Redis Cluster如果数据冗余是1的话,至少要3个Master和3个Slave
192.168.XXX.XXX:7000
192.168.XXX.XXX:7001
192.168.XXX.XXX:7002
192.168.XXX.XXX:7003
192.168.XXX.XXX:7004
192.168.XXX.XXX:7005
配置文件:
bind 192.168.XXX.XXX //不能绑定到192.168.XXX.XXX或localhost,否则指导客户端重定向时会报”Connection refused”的错误。
port 7000
daemonize yes //后台运行
cluster-enabled yes //开启Cluster
cluster-require-full-coverage no //默认是yes,只要有结点宕机导致16384个槽没全被覆盖,整个集群就全部停止服务,改为no。
cluster-config-file nodes_7000.conf //集群配置文件,这个配置文件不是要我们去配的,而是Redis运行时保存配置的文件,所以我们也不可以修改这个文件。
cluster-node-timeout 5000 //超时多久则认为它宕机了。
appendonly yes
logfile ./redis_7000.log
启动各节点:redis-server redis_700X.conf
安装基于ruby的集群工具:
yum install ruby rubygems -y
wget https://rubygems.org/downloads/redis-3.2.1.gem
gem install -l redis-3.2.1.gem
cp redis-3.2.1/src/redis-trib.rb /usr/local/bin/redis-trib //复制集群管理程序到/usr/local/bin
创建集群:
redis-trib create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
--replicas 1 表示我们希望为集群中的每个主节点创建一个从节点,以上命令的意思就是让 redis-trib 程序创建一个包含三个主节点和三个从节点的集群。
测试:
$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"
值被放到了相应节点的Hash槽中,redis-cli不断在700X节点之前重定向跳转。如果启动时不加-c选项的话,就能看到以错误形式显示出的MOVED重定向消息。
重新分片:
重分片基本上就是从部分节点移动哈希槽到另外一部分节点上去,像创建集群一样也是通过使用 redis-trib 工具来完成。
添加新节点:
现在按照相同方法启动了两个新实例7010,7011。其中7010为主,7011为从。
//添加主节点
redis-trib.rb add-node 192.168.1.100:7010 192.168.1.100:7000
//查看节点信息,7010被成功添加
redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes
...
...
0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7010 master - 0 1442452249525 0 connected
...
...
//添加从节点
redis-trib.rb add-node --slave --master-id 0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7011 192.168.1.100:7000
迁移Slot:
# redis-trib.rb reshard 192.168.1.100:7000 //下面是主要过程
How many slots do you want to move (from 1 to 16384)? 1000 //设置slot数1000
What is the receiving node ID? 03ccad2ba5dd1e062464bc7590400441fafb63f2 //新节点node id
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:all //表示全部节点重新洗牌
Do you want to proceed with the proposed reshard plan (yes/no)? yes //确认重新分
删除节点:
如果删除的是主节点,她有slot,需要先去掉分配的slot,然后在删除主节点
# redis-trib.rb reshard 192.168.10.219:6378 //取消分配的slot,下面是主要过程
How many slots do you want to move (from 1 to 16384)? 1000 //被删除master的所有slot数量
What is the receiving node ID? 5d8ef5a7fbd72ac586bef04fa6de8a88c0671052 //接收6378节点slot的master
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:03ccad2ba5dd1e062464bc7590400441fafb63f2 //被删除master的node-id
Source node #2:done
Do you want to proceed with the proposed reshard plan (yes/no)? yes //取消slot后,reshard
//现在节点上已经没有slot
# redis-trib.rb del-node 192.168.10.219:6378 '03ccad2ba5dd1e062464bc7590400441fafb63f2'
Jedis客户端:
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
//Jedis Cluster will attempt to discover cluster nodes automatically
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));
JedisCluster jc = new JedisCluster(jedisClusterNodes);
jc.set("foo", "bar");
String value = jc.get("foo");
要点:
Sentinel是一种自动failover的解决方案。Cluster是一种分片的方案,自带failover,使用Cluster时不需要再用Sentinel。
Sentinel:
1.Sentinel的作用是将主从切换自动化。
2.Sentinel本身也支持集群,用Sentinel集群来监控redis集群。
3.Sentinel集群自身也需要多数机制。(避免单点问题,至少需要三个)
4.Sentinel只是一个运行在特殊模式下的 Redis实例。
通过给定 –sentinel 选项来启动 Redis Sentinel 实例。如:
redis-server /path/to/sentinel.conf --sentinel
当然也可以运行Redis自带的redis-sentinel,如:
redis-sentinel /path/to/sentinel.conf
在默认情况下,Sentinel 使用TCP端口26379。 可以使用 redis-cli 进行通讯 redis-cli -p 26379。
4.Sentinel进程可以通过发布与订阅来自动发现正在监视相同主实例的其他Sentinel,只需监控相同主实例无须设置其他Sentinel的地址。
#配置文件conf中
sentinel monitor myredis 127.0.0.1 6379 2
5.Sentinel 的 failover 过程对代码客户端是透明的。
参考:
http://blog.csdn.net/dc_726/article/details/48552531
http://blog.51yip.com/nosql/1726.html
http://carlosfu.iteye.com/blog/2243483