搭建集群工作需要以下三个步骤:
1)准备节点。
2)节点握手。
3)分配槽。
Redis集群一般由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。每个节点需要开启配置cluster-enabled yes,让Redis运行在集群模式下。建议为集群内所有节点统一目录,一般划分三个目录:conf、data、log,分别存放配置、数据和日志相关文件。把6个节点配置统一放在conf目录下,集群相关配置如下:
其他配置和单机模式一致即可,配置文件命名规则redis-{port}.conf,准备好配置后启动所有节点。
Cluster集群启动过程如下图:
每个节点目前只能识别出自己的节点信息,可以执行cluster nodes命令获取集群节点状
态。
节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信,达到感知对方的过程。节点握手是集群彼此通信的第一步,由客户端发起命令:cluster meet{ip}{port}
cluster meet命令是一个异步命令,执行之后立刻返回。内部发起与目标节点进行握手通信,握手通信过程:
1)节点6379本地创建6380节点信息对象,并发送meet消息。
2)节点6380接受到meet消息后,保存6379节点信息并回复pong消息。
3)之后节点6379和6380彼此定期通过ping/pong消息进行正常的节点通
信。
分别执行meet命令让其他节点加入到集群中,
最后执行cluster nodes命令确认6个节点都彼此感知并组成集群。
节点建立握手之后集群还不能正常工作,这时集群处于下线状态,所有的数据读写都被禁止,通过cluster info命令可以获取集群当前状态。
Redis集群把所有的数据映射到16384个槽中。每个key会映射为一个固定的槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。通过cluster addslots命令为节点分配槽。这里利用bash特性批量设置槽(slots),命令如下:
执行cluster info查看集群状态,如下所示:
当前集群状态是OK,集群进入在线状态。所有的槽都已经分配给节点,执行cluster nodes命令可以看到节点和槽的分配关系:
集群模式下,Reids节点角色分为主节点和从节点。首次启动的节点和被分配槽的节点都是主节点,从节点负责复制主节点槽信息和相关的数据。使用cluster replicate{nodeId}命令让一个节点成为从节点。其中命令执行必须在对应的从节点上执行,nodeId是要复制主节点的节点ID,命令如下:
Redis集群模式下的主从复制使用了之前介绍的Redis复制流程,依然支持全量和部分复制。复制(replication)完成后,整个集群的结构如图:
集群搭建需要很多步骤当集群节点众多时,必然会加大搭建集群的复杂度和运维成本。因此Redis官方提供了redis-trib.rb工具方便我们快速搭建集群。
redis-trib.rb是采用Ruby实现的Redis集群管理工具。内部通过Cluster相关命令帮我们简化集群创建、检查、槽迁移和均衡等常见运维操作,使用之前需要安装Ruby依赖环境。
1、安装Ruby:
2、安装rubygem redis依赖:
3、安装redis-trib.rb:
4、安装完Ruby环境后,执行redis-trib.rb命令确认环境是否正确,输出如
下:
首先我们跟之前内容一样准备好节点配置并启动:
启动好6个节点之后,使用redis-trib.rb create命令完成节点握手和槽分配过程,命令如下:
–replicas参数指定集群中每个主节点配备几个从节点,这里设置为1。
如果部署节点使用不同的IP地址,redis-trib.rb会尽可能保证主从节点不分配在同一机器下,因此会重新排序节点列表顺序。节点列表顺序用于确定主从角色,先主节点之后是从节点。创建过程中首先会给出主从节点角色分配的计划,当我们同意这份计划之后输入yes,redis-trib.rb开始执行节点握手和槽分配操作。
集群完整性指所有的槽都分配到存活的主节点上,只要16384个槽中有一个没有分配给节点则表示集群不完整。可以使用redis-trib.rb check命令检测之前创建的集群是否成功,check命令只需要给出集群中任意一个节点地址就可以完成整个集群的检查工作,命令如下:
redis cluster集群部署
我们知道node是单进程的,但实际上目前cpu都是多核的,单进程只能跑一个核,这样cpu资源就没有得到充分利用。
cluster是一个node内置的集群模块,可以用于多进程管理。cluster非常好用,它大大减轻了我们多进程开发的难度,让我们可以轻松构建一个负载均衡集群。
express框架比较常用,我们可以在它下面集成cluster,使其拥有多进程的功能:
我的电脑是4核的,因此会fork出4个子进程,这个www文件会被执行5次,因为要构建5个进程: 1个主进程,4个子进程 。
这样做的好处是——主进程master只负责管理worker子进程,不直接参与业务处理。
给大家做个形象的比喻:
多进程模式下,master是主人,worker是工人。主人拿着皮鞭专门管理一群工人干活,看到哪个不干活(worker挂掉)就一鞭子抽过去(重启worker),主人会活的很好(master不会挂);而单进程模式就是主人亲自干活,一不小心自己也挂了(未捕获异常搞死进程),那就真起不来了。
当然,这只是一个比喻。实际上,哪个worker挂掉后,master并不会自动重启它,但master会接收到一个通知exit,我们可以新fork一个进程即可。
下面我们就来测试一下:
当我们访问网页,究竟是哪个worker给我们处理呢?
我们在express的首页index加上worker的监听信息:
用命令行模拟测试:
有mac电脑的同学可以打开命令行工具,输入命令top:
这是个linux命令,可以实时监控主机的进程。
寻找一下node进程,然后找到其中一个进程id,强制杀死它,看看进程是否自动重建了?
我们在写js时,用到最多的还是回调函数,而回调函数因其延迟执行的特性,有时确实会有些隐蔽的、会导致进程挂掉的高风险bug没被发现,这时,即使我们开了worker子进程,这些危险的bug仍会轮番攻击worker,导致exit事件频繁发生。
为保护进程的安全,我们需要捕获这些未知的危险bug,通过打印错误堆栈分析出错误原因,并及时修正。
多进程必然带来数据共享问题,比如session。
假如你在登录时是worker1帮你处理的,session也会记录在worker1下面,而当你去购物车下单时,可能就分配到worker2去了,worker2下面没有session,因此判断你没登录,提醒你去登录。
这样就出问题了,怎么办呢?
我们可以用mysql或redis来搞定,因为这二者都是独立于node进程之外的。
mysql是用表来记录session,而redis是共享内存,实战中redis使用的更普遍一些。
如果只是普通的数据,也可以用file做数据共享,这就看你的具体需求了。
redis cluster集群选主
一.服务器设置准备
1. 将6379端口在防火墙看开启
[root@redis1 ~]#vi /etc/sysconfig/iptables
-A INPUT -m state –state NEW -m tcp -p tcp –dport 6379 -j ACCEPT
注:必须加在 REJECT 前面。
2.修改Selinux参数
[root@redis1 ~]#vi /etc/selinux/config
SELINUX=disabled
注:分别在其它几个节点上同样配置防火墙和 SELINUX。
二.安装Redis
1.安装系统组件
安装gcc、tcl
yum install –y gcc-c++
yum install –y tcl
2.安装redis
2.1.解压 Redis 到/usr/local目录下
[root@redis1 ~]# tar -zxvf /root/software/redis-3.2.6.tar.gz -C /usr/local/
2.2.在解压后的目录中进行 make 和 make test
[root@redis1 ~]# cd /usr/local/redis-3.2.6
[root@redis1 redis-3.2.6]# make
[root@redis1 redis-3.2.6]# make test
注:要检查 make 和 make test 的结果是否都正确,如果报错,针对性检查并安装系统缺少的组件。
2.3. 复制 redis-server 和 redis-cli 到/usr/local/bin 目录下:
[root@redis1 redis-3.2.6]# cd src
[root@redis1 src]# cp redis-server /usr/local/bin/
[root@redis1 src]# cp redis-cli /usr/local/bin/
2.4验证 Redis 安装是否成功:
[root@redis1 ~]# redis-server
[root@redis1 ~]# redis-cli
注:安装其它 5 台服务器
三.配置集群模式
1.配置 redis.conf
1.1 配置 redis.conf
[root@redis1 ~]# mkdir /etc/redis
[root@redis1 ~]# cd /etc/redis
[root@redis ~]# vi redis.conf
port 6379
daemonize yes
cluster-enabled yes
cluster-config-file /etc/redis/nodes.conf
cluster-node-timeout 5000
appendonly yes
requirepass Ab123456
注 1:cluster-node-timeout 是集群中各节点相互通讯时,允许“失联”的最大毫秒数,本演示
中配置的为 5 秒,如果超过 5 秒某个节点没有向其它节点汇报成功,认为该节点挂了。
注 2:requirepass 是 Redis 访问密码,为了安全起见,该参数建议必须配置,从但客户端
Jedis 版本必须使用 2.8.x 以上的版本,否则需要通过扩展 JedisCluster 来实现对密码访问的
支持。此外几个 Redis 节点的密码应该设置为相同的。
注 3:分别在其它几个节点上创建与上面相同的 redis.conf 文件,内容也相同。
注 4:重启/重建 Redis 集群时,必须删除去/etc/redis/nodes.conf 文件。
1.2以次启动所有节点
[root@redis1 ~]# redis-server /etc/redis/redis.conf
[root@redis2 ~]# redis-server /etc/redis/redis.conf
[root@redis3 ~]# redis-server /etc/redis/redis.conf
[root@redis4 ~]# redis-server /etc/redis/redis.conf
[root@redis5 ~]# redis-server /etc/redis/redis.conf
[root@redis6 ~]# redis-server /etc/redis/redis.conf
2.安装 Redis 集群所需的 Ruby 工具
2.1安装 Ruby 工具:
Redis 集群需要借助其它工具将相关节点加入到 Cluster 中,而这个工具是由 Redis 提供
一个名为 redis-trib.rb 的 ruby 脚本,否则接下来创建 cluster 会失败。
[root@redis1 ~]# cd /usr/local/redis-3.2.6/src
[root@redis1 src]# yum install –y ruby
[root@redis1 src]# yum install -y rubygems
[root@redis1 src]# gem install redis –version 3.0.0
[root@redis1 src]# gem list
2.2 设置 Ruby 连接 Redis 的密码:
[root@redis1 ~]# vi /usr/lib/ruby/gems/1.8/gems/redis-3.0.0/lib/redis/client.rb
:password =>”Ab123456”
注:分别在其它几个节点上用同样的方式安装好 Ruby 工具
3 利用redis-trib.rb 创建 Redis集群
3. 1. 在 src 目录下运行以下脚本:
[root@redis1 ~]# cd /usr/local/redis-3.2.6/src
[root@redis1 src]# ./redis-trib.rb create –replicas 1 10.50.130.101:6379 10.50.130.102:6379
10.50.130.103:6379 10.50.130.104:6379 10.50.130.105:6379 10.50.130.106:6379
注 1:只需在其中某个个节点执行以上脚本(本例在第一个节点执行)。
注 2:利用 redis-trib 创建 Cluster,只需要操作一次即可,假设系统关机、重启,把所有的
节点全部关闭之后,下次重启后,即自动进入 Cluster 模式,不用现次执行 redis-trib.rb cteate
命令。
3.2查看 Cluster 进程:
[root@redis1 ~]# ps -ef|grep redis
[root@redis2 ~]# ps -ef|grep redis
[root@redis3 ~]# ps -ef|grep redis
[root@redis4 ~]# ps -ef|grep redis
[root@redis5 ~]# ps -ef|grep redis
[root@redis6 ~]# ps -ef|grep redis
3.3 查看节点属性(Master/Slave)
[root@redis1 ~]# cd /usr/local/redis-3.2.6/src
[root@redis1 src]# ./redis-trib.rb check 10.50.130.101:6379
[root@redis1 src]# ./redis-trib.rb check 10.50.130.102:6379
[root@redis1 src]# ./redis-trib.rb check 10.50.130.103:6379
[root@redis1 src]# ./redis-trib.rb check 10.50.130.104:6379
[root@redis1 src]# ./redis-trib.rb check 10.50.130.105:6379
[root@redis1 src]# ./redis-trib.rb check 10.50.130.106:6379
3.4查看节点/集群信息
redis-cli 客户端登录到任一个节点,查看:
4.Jedis 测试 Redis 集群
【Redis】Redis Cluster-集群故障转移
redis数据淘汰原理
redis过期数据删除策略
redis server事件模型
redis cluster mget 引发的讨论
redis 3.x windows 集群搭建
redis 命令执行过程
redis string底层数据结构
redis list底层数据结构
redis hash底层数据结构
redis set底层数据结构
redis zset底层数据结构
redis 客户端管理
redis 主从同步-slave端
redis 主从同步-master端
redis 主从超时检测
redis aof持久化
redis rdb持久化
redis 数据恢复过程
redis TTL实现原理
redis cluster集群建立
redis cluster集群选主
当slave发现自己的master变为FAIL状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave。Failover的过程需要经过类Raft协议的过程在整个集群内达到一致, 其过程如下:
在作为slave角色节点会定期发送ping命令来检测master的存活性,如果检测到master未响应,那么就将master节点标记为疑似下线。
clusterHandleSlaveFailover执行重新选主的核心逻辑。
clusterHandleSlaveFailover内部通过clusterRequestFailoverAuth方法向集群当中的所有节点发送CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST报文,通知大家slave准备执行failover。
当节点收到超过n/2+1个master的response后即升级为主。
在redis主从选举过程中报文相关的解析逻辑,clusterProcessPacket内部主要处理CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST和CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK报文。
redis cluster集群的源码分析(1)
Redis Cluster 实现细节
在集群定时任务 clusterCron 中,会遍历集群中的节点,对每个节点进行检查,判断节点是否下线。与节点下线相关的状态有两个,分别为 CLUSTER_NODE_PFAIL 和 CLUSTER_NODE_FAIL 。
CLUSTER_NODE_PFAIL :当前节点认为某个节点下线时,会将节点状态改为 CLUSTER_NODE_PFAIL ,由于可能存在误判,所以需要根据集群中的其他节点共同决定是否真的将节点标记为下线状态, CLUSTER_NODE_PFAIL 可以理解为疑似下线,类似哨兵集群中的主观下线 。
CLUSTER_NODE_FAIL :集群中有过半的节点标认为节点已下线,此时将节点置为 CLUSTER_NODE_FAIL 标记节点下线, CLUSTER_NODE_FAIL 表示节点真正处于下线状态,类似哨兵集群的客观下线 。
在集群定时任务遍历集群中的节点进行检查时,遍历到的每个节点记为 node ,当前节点记为 myself ,检查的内容主要有以下几个方面:
一、判断孤立主节点的个数
如果当前节点 myself 是从节点,正在遍历的节点 node 是主节点,并且 node 节点不处于下线状态,会判断孤立节点的个数,满足以下三个条件时,认定 node 是孤立节点,孤立节点个数增1:
二、检查连接
这一步主要检查和节点间的连接是否正常,有可能节点处于正常状态,但是连接有问题,此时需要释放连接,在下次执行定时任务时会进行重连,释放连接需要同时满足以下几个条件:
三、疑似下线判断
ping_delay 记录了当前时间距离向 node 节点发送PING消息的时间, data_delayd 记录了 node 节点向当前节点最近一次发送消息的时间,从ping_delay和data_delay中取较大的那个作为延迟时间。
如果延迟时间大于超时时间,判断 node 是否已经处于 CLUSTER_NODE_PFAIL 或者 CLUSTER_NODE_FAIL 状态,如果都不处于,将节点状态置为 CLUSTER_NODE_PFAIL ,认为节点疑似下线。
上述检查完成之后, 会判断当前节点是否是从节点,如果不处于 CLUSTER_MODULE_FLAG_NO_FAILOVER 状态,调用 clusterHandleSlaveFailover 处理故障转移,不过需要注意此时只是将节点置为疑似下线,并不满足故障转移条件,需要等待节点被置为FAIL下线状态之后,再次执行集群定时任务进入到 clusterHandleSlaveFailover 函数中才可以开始处理故障转移。
当前节点认为某个node下线时,会将node状态置为 CLUSTER_NODE_PFAIL 疑似下线状态,在定时向集群中的节点交换信息也就是发送PING消息时,消息体中记录了node的下线状态,其他节点在处理收到的PING消息时, 会将认为node节点下线的那个节点加入到node的下线链表fail_reports中,并调用 markNodeAsFailingIfNeeded 函数判断是否有必要将节点置为下线FAIL状态 :
markNodeAsFailingIfNeeded
markNodeAsFailingIfNeeded用于判断是否有必要将某个节点标记为FAIL状态:
clusterHandleSlaveFailover
由上面的内容可知,节点客观下线时会被置为 CLUSTER_NODE_FAIL 状态,下次执行集群定时任务时,在故障转移处理函数 clusterHandleSlaveFailover 中,就可以根据状态来检查是否需要执行故障转移。
不过在看 clusterHandleSlaveFailover 函数之前,先看一下 clusterState 中和选举以及故障切换相关的变量定义:
clusterHandleSlaveFailover函数中的一些变量
data_age : 记录从节点最近一次与主节点进行数据同步的时间 。如果与主节点处于连接状态,用当前时间减去最近一次与master节点交互的时间,否则使用当前时间减去与master主从复制中断的时间。
auth_age : 当前时间减去发起选举的时间 ,也就是距离发起选举过去了多久,用于判断选举超时、是否重新发起选举使用。
needed_quorum : quorum的数量,为集群中节点的数量的一半再加1 。
auth_timeout : 等待投票超时时间。
auth_retry_time : 等待重新发起选举进行投票的时间,也就是重试时间 。
一、故障转移条件检查
首先进行了一些条件检查,用于判断是否有必要执行故障转移,如果 处于以下几个条件之一,将会跳出函数,结束故障转移处理 :
二、主从复制进度校验
cluster_slave_validity_factor 设置了故障切换最大主从复制延迟时间因子,如果不为0需要校验主从复制延迟时间是否符合要求。
如果主从复制延迟时间 data_age 大于 mater向从节点发送PING消息的周期 + 超时时间 * 故障切换主从复制延迟时间因子 并且不是手动执行故障切换,表示主从复制延迟过大,不能进行故障切换终止执行。
三、是否需要重新发起选举
如果距离上次发起选举的时间大于超时重试时间,表示可以重新发起投票。
四、延迟发起选举
五、发起投票
如果满足执行故障的条件,接下来需从节点想集群中的其他节点广播消息,发起投票,不过只有主节点才有投票权。 failover_auth_sent 为0表示还未发起投票,此时开始发起投票:
六、执行故障切换
当某个节点获取到了集群中大多数节点的投票,即可进行故障切换,这里先不关注,在后面的章节会讲。
clusterGetSlaveRank用于计算当前节点的等级,遍历所属主节点的所有从节点,根据主从复制进度 repl_offset 计算, repl_offset 值越大表示复制主节点的数据越多,所以等级越高,对应的 rank 值就越低。
从节点在发起选举使用了 rank 的值作为延迟时间,值越低延迟时间越小,意味着选举优先级也就越高。
当从节点认为主节点故障需要发起投票,重新选举主节点时,在集群中广播了 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 消息,对应的处理在 clusterProcessPacket 函数中,里面会调用 clusterSendFailoverAuthIfNeeded 函数进行投票:
clusterSendFailoverAuthIfNeeded
clusterSendFailoverAuthIfNeeded函数用于进行投票,处理逻辑如下:
以上条件校验通过, 表示当前节点可以投票给发送请求的节点,此时更新 lastVoteEpoch ,记录最近一次投票的纪元(轮次),更新投票时间 node->slaveof->voted_time ,然后向发起请求的节点回复 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息。
主节点对发起投票请求节点的回复消息 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 同样在消息处理函数 clusterProcessPacket 中,会对发送回复消息的节点进行验证:
同时满足以上三个条件时, 表示发送者对当前节点进行了投票,更新当前节点记录的收到投票的个数, failover_auth_count 加1,此时有可能获取了大多数节点的投票,先调用 clusterDoBeforeSleep 设置一个 CLUSTER_TODO_HANDLE_FAILOVER 标记,在周期执行的时间事件中会调用对状态进行判断决定是否执行故障转移。
从节点收到投票后,会添加 CLUSTER_TODO_HANDLE_FAILOVER 标记,接下来看下对 CLUSTER_TODO_HANDLE_FAILOVER 状态的处理。
在 beforeSleep 函数(server.c文件中),如果开启了集群,会调用 clusterBeforeSleep 函数,里面就包含了对 CLUSTER_TODO_HANDLE_FAILOVER 状态的处理:
beforeSleep 函数是在Redis事件循环 aeMain 方法中被调用的,详细内容可参考 事件驱动框架源码分析 文章。
clusterBeforeSleep
在clusterBeforeSleep函数中,如果节点带有 CLUSTER_TODO_HANDLE_FAILOVER 标记,会调用 clusterHandleSlaveFailover 函数进行处理:
clusterHandleSlaveFailover 函数在上面我们已经见到过,这次我们来关注集群的故障转移处理。
如果当前节点获取了大多数的投票,也就是 failover_auth_count (得到的投票数量)大于等于 needed_quorum , needed_quorum 数量为集群中节点个数的一半+1,即可执行故障转移,接下来会调用 clusterFailoverReplaceYourMaster 函数完成故障转移。
clusterFailoverReplaceYourMaster
如果从节点收到了集群中过半的投票,就可以成为新的master节点,并接手下线的master节点的slot,具体的处理在clusterFailoverReplaceYourMaster函数中,主要处理逻辑如下:
总结
以上就是关于Redis Cluster集群的搭建全部的内容,如果了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!