分布式之Redis【二】高可用集群搭建

分布式之Redis【二】高可用集群搭建

一、Redis集群方案比较

  • 哨兵模式

在这里插入图片描述

在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态, 如果master节点异常,则会做主从切换,将某一台slave作为master,哨兵的配置略微复杂,并且性能和高可用性等各方面表现一般,特别是在主从切换的瞬间存在访问瞬断的情况,而且哨兵模式只有一个主节点对外提供服务,没法支持很高的并发,且单个主节点内存也不宜设置得过大,否则会导致持久化文件过大,影响数据恢复或主从同步的效率。

  • 高可用集群模式

在这里插入图片描述

Redis集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到16384个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单

二、Redis高可用集群搭建

本次在三个服务器上一共搭建6个节点,演示Redis的高可用集群搭建。

首先在每台服务器上执行以下相同命令:

mkdir /my-service
cd /my-service
wget http://download.redis.io/releases/redis-5.0.2.tar.gz
tar xzf redis-5.0.2.tar.gz
cd redis-5.0.2
make

完成以上命令后就相当于在每台服务器上安装好了Redis。

可以都运行一下验证是否可以正常运行,验证完后开始正式的集群搭建。

cd /my-service
mkdir redis-cluster
cd redis-cluster

这里开始每台服务器的命令就不相同了;

以下就以一个服务器的举例。

在第一台服务器新建的redis-cluster文件夹下新建6381和6384文件夹。

mkdir 6381 6384

然后将redis-5.0.2文件中的redis.conf复制到6381和6384文件下面

cp /my-service/redis-5.0.2/redis.conf /my-service/redis-cluster/6381/
cp /my-service/redis-5.0.2/redis.conf /my-service/redis-cluster/6384/

接下来就是修改复制过来的配置文件了

vim /6381/redis.conf
# 具体修改一下几项
daemonize yes  # 后台启动
port 6381 # 修改端口号
dir /my-service/local/redis-cluster/6381/ # 修改文件保存地点
cluster-enabled yes # 启动集群模式
cluster-config-file nodes-6381.conf # 集群节点信息文件
# bind 127.0.0.1  # 去掉bind绑定访问ip信息
protected-mode no  #(关闭保护模式)
appendonly yes   # 开启aof
# 如果要设置密码需要增加如下配置:
requirepass clustertest  # 设置redis访问密码
masterauth zhuge clustertest # 设置集群节点间访问密码,跟上面一致

以上修改在每个服务器的638*文件中都修改。

接下来就是启动6个redis实例;

/my-service/redis-5.0.2/src/redis-service /my-service/redis-cluster/638*/redis.conf

查看是否启动成功

ps -ef|grep redis

然后用 redis-cli 创建整个redis集群

/my-service/redis-5.0.2/src/redis-cli -a clustertest --cluster create --cluster-replicas 1 x.x.x.x:6381 x.x.x.x:6384 x.x.x.x:6382 x.x.x.x:6385 x.x.x.x:6383 x.x.x.x:6386

最后可以验证集群

# 随便连接其中一台实例
my-service/redis-5.0.2/src/redis-cli -a clustertest -c -h x.x.x.x -p 6381
# 连接上后,查看集群信息
cluster info
cluster nodes # 查看节点信息

三、Java操作redis集群

(一)添加jedis依赖

<dependency> 
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

(二)Java编写访问redis集群的代码

package redis;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * @author tzh
 * @projectName code-demo
 * @title RedisCluster
 * @description
 * @date 2021/3/6  12:05
 */
public class RedisCluster {

    public static void main(String[] args) throws Exception {
        Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
        jedisClusterNode.add(new HostAndPort("x.x.x.x", 6381));
        jedisClusterNode.add(new HostAndPort("x.x.x.x", 6382));
        jedisClusterNode.add(new HostAndPort("x.x.x.x", 6383));
        jedisClusterNode.add(new HostAndPort("x.x.x.x", 6384));
        jedisClusterNode.add(new HostAndPort("x.x.x.x", 6385));
        jedisClusterNode.add(new HostAndPort("x.x.x.x", 6386));
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100); config.setMaxIdle(10);
        config.setTestOnBorrow(true);
        // connectionTimeout:指的是连接一个url的连接等待时间
        // soTimeout:指的是连接上一个url,获取response的返回等待时间
        JedisCluster jedisCluster = new JedisCluster(jedisClusterNode, 6000, 5000, 10, "clustertest", config);
        System.out.println(jedisCluster.set("student", "zxx"));
        System.out.println(jedisCluster.set("age", "19"));
        System.out.println(jedisCluster.get("student"));
        System.out.println(jedisCluster.get("age"));
        jedisCluster.close();
    }
}

四、Redis集群原理分析

Redis Cluster 将所有数据划分为 16384 的 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。当 Redis Cluster 的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存在客户端本地。这样当客户端要查找某个 key 时,可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不一致的情况,还需要纠正机制来实现槽位信息的校验调整。

槽位定位算法

Cluster 默认会对 key 值使用 CRC16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。

HASH_SLOT = CRC16(key) mod 16384

跳转重定位

当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表。

网络抖动

真实世界的机房网络往往并不是风平浪静的,它们经常会发生各种各样的小问题。比如网络抖动就是非常常见的一种现象,突然之间部分连接变得不可访问,然后很快又恢复正常。

为解决这种问题,Redis Cluster 提供了一种选项cluster-node-timeout,表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,需要进行主从切换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。

五、Redis水平扩展节点

之前就提到了,集群比哨兵模式好处之一就是可以支持水平拓展,当我们刚才的三个主从不足以支持我们现有业务的时候我们需要在增加一主从。

首先在新的服务器上还是按照之前的步骤先把redis服务启动起来。

这时候我们就得到了新的端口为6387和6388的redis实例。接下来就是需要将这两个实例添加到redis集群中去。

# 将6387和6388 通过6381节点添加到集群中 
/my-service/redis-5.0.2/src/redis-cli -a clustertest --cluster add-node x.x.x.x:6387 x.x.x.x:6381

/my-service/redis-5.0.2/src/redis-cli -a clustertest --cluster add-node x.x.x.x:6388 x.x.x.x:6381

为新加入的6387节点分配hash槽

# 新添加到集群中的节点都会变为master节点,并且会没有分配hash槽
# 使用redis-cli命令为6387分配hash槽,找到集群中的任意一个主节点,对其进行重新分片工作。
/my-service/redis-5.0.2/src/redis-cli -a clustertest --cluster reshard x.x.x.x:8001
# 接下来会问你需要移动多少个槽到节点上
How many slots do you want to move (from 1 to 16384)? 600
# 输入你想要分配的个数后回车(例如600),接下来会问你把这600个槽分配给谁,需要指定节点id
What is the receiving node ID?eb57a5700ee6f9ff099b3ce0d03b1a50ff247c3c
# 接下来是让你选择从哪里来获取这个600个槽,你可以选择第一个all,就是所有master节点分别抽取200个到新节点中,也可以输入指定节点的id,然后done。
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
# 最后输入yes确认执行

然后将6388节点变为6387的从节点

# 我们需要执行replicate命令来指定当前节点(从节点)的主节点id为哪个,首先需要连接新加的6388节点的客户端,然后使用集群命令进行操作,把当前的6388(slave)节点指定到一个主节点下。
/my-service/redis-5.0.2/src/redis-cli -a clustertest -c -h x.x.x.x -p 6388
cluster replicate eb57a5700ee6f9ff0922z3ce0d03b1a50ff2423za

以上就是扩展节点的做法了,然后当一段时间业务量下去了,继续搭建这个都节点岂不是很浪费,所以需要删去新增的节点。

接下来我们删除节点6387和6388。

首先需要删除从节点6388

/my-service/redis-5.0.2/src/redis-cli -a clustertest --cluster del-node x.x.x.x:6388 1805b6339d91b0e051f46845eebacb9bc43baefe 

接下来我们需要删除主节点6387,但是删除之前我们需要先把hash槽给放入其他可用的master节点中,然后再进行移除节点操作,不然会出现数据丢失问题(目前只能把master的数据迁移到一个节点上,暂时做不了平均分配功能),执行命令如下:

/my-service/redis-5.0.2/src/redis-cli -a clustertest --cluster reshard x.x.x.x:6387
# 接下来会问你需要移动多少个槽到节点上,刚才我们分配了600个这次把这600个全部分配走
How many slots do you want to move (from 1 to 16384)? 600
# 接下来会问你把这600个槽分配给谁,需要指定节点id
What is the receiving node ID?eb57a5700ee6f9ff099b3ce0d03b1a50ff247c3c
# 接下来是让你选择从哪里来获取这个600个槽,你可以选择第一个all,就是所有master节点分别抽取200个到新节点中,也可以输入指定节点的id,然后done。这里就需要输入我们需要删除的id了 然后done
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:eb57a5700ee6f9ff0922z3ce0d03b1a50ff2423za
Source node 2:done
# 最后输入yes确认执行

现在就可以查看6387上是否还存在hahs槽,不存在就可以安心删除了。

/my-service/redis-5.0.2/src/redis-cli -a clustertest --cluster del-node x.x.x.x:6387    eb57a5700ee6f9ff099b3ce0d03b1a50ff247c3c

六、Redis集群选举原理分析

当slave发现自己的master变为FAIL状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程, 其过程如下:

  1. slave发现自己的master变为FAIL

  2. 将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST 信息

  3. 其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack

  4. 尝试failover的slave收集FAILOVER_AUTH_ACK

  5. 超过半数后变成新Master

  6. 广播Pong通知其他集群节点。

从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票

  • 延迟计算公式:

DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms

  • SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举(理论上)。

--------------最后感谢大家的阅读,愿大家技术越来越流弊!--------------

在这里插入图片描述

--------------也希望大家给我点支持,谢谢各位大佬了!!!--------------

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页