高可用高并发Redis集群配置搭建(Docker+Redis)
- 高可用:24小时对外提供服务
- 高并发:同一时间段能处理的请求数
文章目录
1. 中心化和去中心化
1.1 中心化
意思是所有的节点都要有一个主节点
-
特点:就是一个路由作用。
-
缺点:中心挂了,服务就挂了,中心处理数据的能力有限,不能把节点性能发挥到最大。
1.2 去中心化
特点:去掉路由,我自己来路由
1.3 总结
- 中心化:几个经过认证的嘉宾在‘讲话’,所有其他人在听。
- 去中心化:每个人都可以‘讲话’,每个人都可以选择听或者讲。
2. Redis集群的执行流程分析
如何连接集群:连接集群中的任意一个主redis,都相当于连上了集群,因为该集群是去中心化的。
向集群中写数据的流程:
-
- 连上任意一个redis指定写入key与value;
-
- 拿到key的值,进行hash算法,得到一个随机数;
-
- 通过这个数字就可以匹配到集群中的某一个redis:
- 3.1 将该数字对16384取余,余数一定是0~16383之间的某一个数字;
- 3.2 将这个余数与每一个redis被分配的哈希槽进行匹配,匹配上哪一个redis就将这个数据添加到哪一个redis中。
2.1 哈希槽说明
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384
计算这个key应该分布到哪个hash slot中,一个hash slot中会有很多key和value。你可以理解成表的分区,使用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster以后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪个分区,每个分区里有很多key。
2.2 执行流程分析
- 假如redis集群里面能存放90个key,那么redis集群把90key平分到3个主机;
- redis对每个主机里面30个存储位置都编号,当应用连接到主机1上面时,应该发送一个写的命令;
- 主机使用crc16算出槽号:
- 如果槽号在1-30 可以直接操作主机1;
- 如果槽号在31-60那么redis会转发到主机2;
- 如果应该再发一个命令set age 22,那么主机2使用crc16再算槽号再转发。
3. 集群的搭建
3.1 原理
去中心化
3.2 集群规则
机器编号 | ip | port |
---|---|---|
1 | 47.105.128.151 | 7000 |
2 | 47.105.128.151 | 7001 |
3 | 47.105.128.151 | 7002 |
4 | 47.105.128.151 | 7003 |
5 | 47.105.128.151 | 7004 |
6 | 47.105.128.151 | 7005 |
3.3 搭建过程
-
- 准备一个目录用来存放集群中Redis的配置文件:
mkdir redis-cluster
-
- 准备一个服务端程序
redis-server
,方便启动Redis服务
- 准备一个服务端程序
-
-
准备6个redis的配置文件:
#redis-7000.conf 后面几个配置文件如法炮制,修改后面编号就行 bind 0.0.0.0 port 7000 daemonize yes # 打开aof 持久化 appendonly yes # 开启集群 cluster-enabled yes # 集群的配置文件,该文件自动生成 此处的编号也要修改 cluster-config-file nodes-7000.conf # 集群的超时时间 cluster-node-timeout 5000
-
-
- 启动六个redis:
/usr/local/redis/bin/redis-server /usr/local/redis/conf/credis-cluster/redis-7000.conf
其他五个一样启动。
-
-
使用docker下载
redis-trib
的镜像运行- 安装docker(如果已安装可忽略)
yum install docker
-
启动docker
systemctl start docker
-
下载镜像
docker pull inem0o/redis-trib
-
开启集群(IP地址和端口号按上面的规则)
docker run -it --net host inem0o/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
-it
是为了可以输入,--net host
是为了让docker容器能连接上本地的宿主机。
-
测试集群环境
./redis-cli -c -h 127.0.0.1 -p 7000
-c 表示连接集群
至此,集群搭建完毕。
注意:
- 搭建集群的所有redis不能有数据,因为集群中的每一个数据都应该有哈希槽,而提前放进去就刚好没有,所以不允许;
- 配置文件中最好不要设置密码,如果设置了密码,则需要在创建集群的对应服务命令的端口后添加
-a passwod
,如:docker run -it --net host inem0o/redis-trib create --replicas 1 127.0.0.1:7000 -a password 127.0.0.1:7001 -a password 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
-
-
查询集群:
-
-c
:表示以集群方式连接惹redis-h
:指定IP地址-p
:指定端口cluster nodes
:查询集群节点信息cluster info
: 查询集群状态信息查询集群节点信息:
如:
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7001 cluster nodes
myself:表示当前所查的服务节点;
master:表示主服务,最后的数字范围表示哈希槽范围;
slave:表示从服务。
查询集群节点信息:
如:/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7001 cluster info
4. 使用Docker搭建Redis集群
4.1 创建内网
为防止地址端口冲突,以及宿主机为docker重启后分配地址变化的情况发生,可以使用docker创建一个专用的网卡,命令如下:
docker network create 网卡名 --subnet 子网掩码(如:172.38.0.0/16)
通过命令docker network ls
可以查看到目前已知的docker网络,
也可以通过docker network inspect 网卡名
查看网络的详细信息
4.2 创建配置文件
创建六个redis启动所需要的配置文件,命令创建,路径可自定义,
cluster相关的配置一定要有,port 就是之后启动容器时必须要映射的端口,这里不要配置daemonize yes
,会影响docker的启动,默认为no,这是受docker的机制影响的,如果将其设置为yes,也会导致docker容器启动失败。
mkdir -p /usr/local/redis/docker/node-${port}/conf
touch /usr/local/redis/docker/node-${port}/conf/redis.conf
cat << EOF >/usr/local/redis/docker/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
4.3 通过挂载数据卷的方式启动容器
挂载刚才创建好的配置文件,并使用其配置文件启动redis:
docker run -p 7001:6379 -p 17001:16379 --privileged=true --name redis-1 \
> -v /usr/local/redis/docker/node-1/data:/data \
> -v /usr/local/redis/docker/node-1/conf/redis.conf:/etc/redis/redis.conf \
> -d --net redis --ip 172.38.0.11 redis:5.0.14 redis-server /etc/redis/redis.conf
注意:
-
- 防火墙可以关闭,也可以打卡对应端口号,我这里打开了
7001~7006
,以及总集线端口(端口号+10000)17001~17006
;
- 防火墙可以关闭,也可以打卡对应端口号,我这里打开了
-
- 加
--privileged=true
是为了防止Centos将启动停止,Centos7的安全机制;
- 加
-
-
当发现启动的容器自动退出,无法正常运行时,将
-d
取消掉重新运行,就能看到实际的错误,比如我出现了这个错误:意思是我的权限不够,可以试着添加第二点里的命令,还是不行就将配置文件的权限修改为"777";
-
-
--ip
一定是刚才自己建的网卡ip地址,每个容器对应一个;
输入docker ps
显示如下信息表示启动成功并正常运行:
输入docker ps -a
显示如下信息,表示异常退出:
补充知识:docker有一个机制,后台运行的程序必须有一个前台程序,否则这个docker容器就会觉得自己无事可做而自杀。
4.4 创建集群节点
当六个Redis都正常运行时,就可以创建节点了,进入任意一个redis服务:
docker exec -it 容器名 bash
创建节点:
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
ip号一定要和创建容器时的ip对应,端口也要和redis配置文件中的端口对应。
遇到询问,输入yes
即可
到此,搭建工作完成
4.5 测试集群
进入集群一定注意输入:redis-cli -c -p 端口号
而不是原来的单体redis-cli -p 端口号
,默认端口是6379
输入cluster info
查看该集群节点信息;
输入cluster nodes
查看所有节点信息,master表示主机,slave表示从机,myself表示当前所在节点
向主机中写入数据,发现根据哈希算法计算,得到**[15495]**所以自动插入到3号主机的哈希槽中
测试故障转移:
故意关掉三号主机:docker stop redis-3
,获取刚才插入的值
发现已经转移到4号redis上,并且可以发现4号机已经自动升级成了master,说明转移成功。
自此,搭建完成!!!
参考视频:狂神说java[Docker搭建Redis集群部署实战]
5. 外界访问集群的搭建方式
新的风暴已经出现…
按着前面搭建集群的方法确实可以搭建成功,但是有个问题,那就是只能在自己的虚拟机中访问,通过外界工具无法访问,外部连接此集群会存在无法连接的情况,这不符和我的要求。。。
4.6.1 问题分析
初步分析,竟然内部能操作集群,但是外部不能访问,猜测大概是网络的问题,前面创建reids容器时,是让它在自己创建的dcoker的redis网络中运行,外部无法访问,之前我的理解是,宿主机可以直接通过端口映射就能访问集群了,没考虑过是否与网络有关。
4.6.2 尝试解决
之前创建容器时使用的redis网络,那么这次我就直接使用docker的host网络,和宿主机关联;redis的启动配置文件没做改动,创建容器的命令只需要修改网络部分,将--net redis
改为--net host
,为了能看到启动失败时的错误信息,我没有选择后台运行:
docker run -p 7001:6379 -p 17001:16379 --privileged=true --name redis-1 \
> -v /usr/local/redis/docker/node-1/data:/data \
> -v /usr/local/redis/docker/node-1/conf/redis.conf:/etc/redis/redis.conf \
> --net host redis:5.0.14 redis-server /etc/redis/redis.conf
果然,启动失败,,,
原因是我的6379端口被占用,看了一下我的6379端口是我宿主机的redis在用;那么问题来了,前面创建docker内部集群时,映射的端口全是6379和16379;为什么这次会报被占用的错呢?估计是因为我把网络改成了本地宿主机,docker映射时直接映射在了宿主机上,所以才产生了冲突;尝试着修改配置文件再试试,其中一个修改如下:
#每个配置文件的端口都要不一样 我的是7001~7006
port 7006
bind 0.0.0.0
#这里的配置是在官网看到的,redis的一种安全保护机制,3.2开始的,防止外部网络访问,这里我直接禁用掉
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
#修改
cluster-announce-port 7006
#修改
cluster-announce-bus-port 17006
appendonly yes
重新启动,修改启动中的端口映射:
docker run -p 7001:7001 -p 17001:17001 --privileged=true --name redis-1 \
> -v /usr/local/redis/docker/node-1/data:/data \
> -v /usr/local/redis/docker/node-1/conf/redis.conf:/etc/redis/redis.conf \
> -d --net host redis:5.0.14 redis-server /etc/redis/redis.conf
启动成功!!!
依次启动其余五个容器,
再通过命令建立集群,记得修改每个容器的ip和端口:
docker run -it --net host inem0o/redis-trib create --replicas 1 宿主机ip:7001 宿主机ip:7002 宿主机ip:7003 宿主机ip:7004 宿主机ip:7005 宿主机ip:7006
之后的测试和前面的一样,另外这时也可以通过外界访问了!!!
4.6.3 最终测试
通过管理工具存入"hello world"键值对:
在集群中查看:
我的最终目的是在java程序中也能访问集群,尝试通过java程序访问:
@Test
public void testClusters(){
//创建集群服务
Set<HostAndPort> hostAndPorts = new HashSet<>();
hostAndPorts.add(new HostAndPort("192.168.**.**",7001));//这里是自己宿主机的ip
hostAndPorts.add(new HostAndPort("192.168.**.**",7002));
hostAndPorts.add(new HostAndPort("192.168.**.**",7003));
hostAndPorts.add(new HostAndPort("192.168.**.**",7004));
hostAndPorts.add(new HostAndPort("192.168.**.**",7005));
hostAndPorts.add(new HostAndPort("192.168.**.**",7006));
//创建集群
JedisCluster cluster = new JedisCluster(hostAndPorts);
cluster.set("JAVA", "NO1");
System.out.println("set = " + cluster.get("JAVA"));
cluster.close();
}
输出台输出:
再查看Redis集群中:
目的达成,完结撒花!!!
4.6 总结
-
- 使用docker搭建redis集群走了很多弯路,遇到了最主要的两个问题就是创建集群的时候一直显示
Waiting for cluster join...
,主要原因是ip地址和端口不正确,无法找到节点,最好的方式就是自己为docker创建一个网卡,可以避免很多不必要的麻烦,但是这样外部就不能访问了,如果需要外部访问,还是得使用宿主机的ip地址,这时每个容器的端口也必须唯一,不能都是默认的6379了;
- 使用docker搭建redis集群走了很多弯路,遇到了最主要的两个问题就是创建集群的时候一直显示
-
- 输入创建节点命令时,会出现
not IP:端口 as a cluster nodes
,这是因为创建容器时,没有将Redis中的cluster-enabled
设置为yes;
- 输入创建节点命令时,会出现
-
- 启动redis容器失败,始终异常退出,原因可能是:
-
- 命令错误,可以在启动时不加
-d
,就能发现错误原因; - 配置文件权限没设置,
chmod 777 redis.conf
; - 关掉Centos的安全机制或者添加
--privileged=true
; - 数据卷挂载失败,容器启动时没有按照配置文件的内容启动,默认启动中
cluster-enabled
为no,并且很多集群相关的配置信息都没有,所以要仔细检查数据卷的挂载路径宿主机路径:容器内路径
,注意一个-v
表示配置文件目录,另一个-v
表示docker以后生成的数据文件存储目录,并且不能以/
结束,redis的启动文件以docker内的路径为准,因为这时已经将路径映射到宿主机上了。
- 命令错误,可以在启动时不加
-
- 启动redis容器失败,始终异常退出,原因可能是:
因为对网络部分的知识和redis不熟悉,走了很多弯路,此篇文章记录下自己的问题解决过程,加深印象。