1 实验环境准备
1.1 构建Redis的Docker镜像
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker pull redis
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest f1b6973564e9 4 weeks ago 113MB
1.2 准备redis.conf配置文件
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# ls -al
drwxr-xr-x 4 root root 110 2月 26 18:40 .
drwxr-xr-x 3 root root 19 2月 26 12:16 ..
-rw-r--r-- 1 root root 93724 2月 26 12:27 redis_1.conf
-rw-r--r-- 1 root root 93724 2月 26 18:39 redis_2.conf
-rw-r--r-- 1 root root 93724 2月 26 18:40 redis_3.conf
-rw-r--r-- 1 root root 93724 2月 26 12:17 redis.conf
修改配置中的端口:
redis_1.conf
# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6371
redis_2.conf
# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6372
redis_3.conf
# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6373
1.3 使用Docker启动redis镜像
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest f1b6973564e9 4 weeks ago 113MB
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker run -p 6371:6371 -v /home/docker/redis/redis_1.conf:/etc/redis/redis.conf -d f1b6973564e9 redis-server /etc/redis/redis.conf
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker run -p 6372:6372 -v /home/docker/redis/redis_2.conf:/etc/redis/redis.conf -d f1b6973564e9 redis-server /etc/redis/redis.conf
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker run -p 6373:6373 -v /home/docker/redis/redis_3.conf:/etc/redis/redis.conf -d f1b6973564e9 redis-server /etc/redis/redis.conf
命令解释:
docker run # 运行Docker镜像
-p 6373:6373 # 端口映射,宿主机端口:容器端口
-v /home/docker/redis/redis_3.conf:/etc/redis/redis.conf # 容器卷挂载,宿主机文件:容器文件
-d f1b6973564e9 # 将要运行的Docker镜像id,-d为后台运行
redis-server /etc/redis/redis.conf # Docker容器的启动命令,用相应配置文件启动
运行结果:
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
59839b8a5622 f1b6973564e9 "docker-entrypoint.s…" 4 seconds ago Up 2 seconds 0.0.0.0:6373->6373/tcp, :::6373->6373/tcp, 6379/tcp thirsty_elgamal
2d57a6653087 f1b6973564e9 "docker-entrypoint.s…" 17 seconds ago Up 16 seconds 0.0.0.0:6372->6372/tcp, :::6372->6372/tcp, 6379/tcp agitated_mcnulty
df262a37be21 f1b6973564e9 "docker-entrypoint.s…" 6 hours ago Up 6 hours 0.0.0.0:6371->6371/tcp, :::6371->6371/tcp, 6379/tcp stupefied_moser
1.4 查看Docker部署的Redis网络
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker inspect 59839b8a5622
"Networks": {
"bridge": {
......
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.4",
......
}
}
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker inspect 2d57a6653087
"Networks": {
"bridge": {
......
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
......
}
}
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker inspect df262a37be21
"Networks": {
"bridge": {
......
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
......
}
}
网络拓扑草图:
2 集群方式一:主从模式
2.1 修改配置文件
(1)主节点配置文件:redis_1.conf
#75 # bind 127.0.0.1 -::1
bind 0.0.0.0
# ......
#90 # By default protected mode is enabled. You should disable it only if
#91 # you are sure you want clients from other hosts to connect to Redis
#92 # even if no authentication is configured, nor a specific set of interfaces
#93 # are explicitly listed using the "bind" directive.
#94 protected-mode yes
protected-mode no
# ......
#901 # requirepass foobared
requirepass 123456 # 可选
(2)从节点1配置文件:redis_2.conf
#75 # bind 127.0.0.1 -::1
bind 0.0.0.0
#...
#90 # By default protected mode is enabled. You should disable it only if
#91 # you are sure you want clients from other hosts to connect to Redis
#92 # even if no authentication is configured, nor a specific set of interfaces
#93 # are explicitly listed using the "bind" directive.
#94 protected-mode yes
protected-mode no
#476 #
#477 # replicaof <masterip> <masterport>
replicaof 172.17.0.2 6371
#479 # If the master is password protected (using the "requirepass" configuration
#480 # directive below) it is possible to tell the replica to authenticate before
#481 # starting the replication synchronization process, otherwise the master will
#482 # refuse the replica request.
#483 #
#484 # masterauth <master-password>
masterauth 123456 # 可选,根据主节点配置
#485 #
#898 # The requirepass is not compatable with aclfile option and the ACL LOAD
#899 # command, these will cause requirepass to be ignored.
#900 #
#901 # requirepass foobared
requirepass 123456 # 可选
(3)从节点2配置文件:redis_3.conf
#75 # bind 127.0.0.1 -::1
bind 0.0.0.0
#...
#90 # By default protected mode is enabled. You should disable it only if
#91 # you are sure you want clients from other hosts to connect to Redis
#92 # even if no authentication is configured, nor a specific set of interfaces
#93 # are explicitly listed using the "bind" directive.
#94 protected-mode yes
protected-mode no
#476 #
#477 # replicaof <masterip> <masterport>
replicaof 172.17.0.2 6371
#479 # If the master is password protected (using the "requirepass" configuration
#480 # directive below) it is possible to tell the replica to authenticate before
#481 # starting the replication synchronization process, otherwise the master will
#482 # refuse the replica request.
#483 #
#484 # masterauth <master-password>
masterauth 123456 # 可选,根据主节点配置
#485 #
#898 # The requirepass is not compatable with aclfile option and the ACL LOAD
#899 # command, these will cause requirepass to be ignored.
#900 #
#901 # requirepass foobared
requirepass 123456 # 可选
2.2 重启Redis
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
59839b8a5622 f1b6973564e9 "docker-entrypoint.s…" 34 minutes ago Up 34 minutes 0.0.0.0:6373->6373/tcp, :::6373->6373/tcp, 6379/tcp thirsty_elgamal
2d57a6653087 f1b6973564e9 "docker-entrypoint.s…" 34 minutes ago Up 34 minutes 0.0.0.0:6372->6372/tcp, :::6372->6372/tcp, 6379/tcp agitated_mcnulty
df262a37be21 f1b6973564e9 "docker-entrypoint.s…" 7 hours ago Up 7 hours 0.0.0.0:6371->6371/tcp, :::6371->6371/tcp, 6379/tcp stupefied_moser
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker restart NAMES
Error response from daemon: No such container: NAMES
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker restart 59839b8a5622
59839b8a5622
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker restart 2d57a6653087
2d57a6653087
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# docker restart df262a37be21
df262a37be21
2.3 可用性测试
主节点测试:
[root@iZ2ze4m2ri7irkf6h6n8zoZ ~]# docker exec -it df262a37be21 /bin/bash
root@df262a37be21:/data# cd /usr/local/bin/
root@df262a37be21:/usr/local/bin# ./redis-cli -p 6371
127.0.0.1:6371> set name zs
OK
127.0.0.1:6371> set age 23
OK
127.0.0.1:6371>
从节点测试:
[root@iZ2ze4m2ri7irkf6h6n8zoZ ~]# docker exec -it 59839b8a5622 /bin/bash
root@59839b8a5622:/data# cd /usr/local/bin/
root@59839b8a5622:/usr/local/bin# ./redis-cli -p 6373
127.0.0.1:6373> get name
"zs"
127.0.0.1:6373> get age
(nil)
127.0.0.1:6373> get age
"23"
127.0.0.1:6373> set address beijing
(error) READONLY You can't write against a read only replica.
[root@iZ2ze4m2ri7irkf6h6n8zoZ ~]# docker exec -it 2d57a6653087 /bin/bash
root@2d57a6653087:/data# cd /usr/local/bin/
root@2d57a6653087:/usr/local/bin# ./redis-cli -p 6372
127.0.0.1:6372> get name
"zs"
127.0.0.1:6372> get age
(nil)
127.0.0.1:6372> get age
"23"
2.4 结论
优点:
- 同一个Master可以同步多个Slaves。
- 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成。即便如此,系统的伸缩性还是得到了很大的提高。
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
缺点:
- Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
- Redis的主从复制采用全量复制,复制过程中主机会fork出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
3 集群方式二:哨兵模式
Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。Sentinel由一个或多个Sentinel 实例 组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
3.1 修改配置文件
[root@iZ2ze4m2ri7irkf6h6n8zoZ redis]# ls -l
-rw-r--r-- 1 root root 93736 2月 26 19:03 redis_1.conf
-rw-r--r-- 1 root root 93761 2月 26 19:14 redis_2.conf
-rw-r--r-- 1 root root 93762 2月 26 19:15 redis_3.conf
-rw-r--r-- 1 root root 93724 2月 26 12:17 redis.conf
-rw-r--r-- 1 root root 13768 2月 27 12:36 sentinel_1.conf
-rw-r--r-- 1 root root 13768 2月 27 12:37 sentinel_2.conf
-rw-r--r-- 1 root root 13768 2月 27 12:37 sentinel_3.conf
-rw-r--r-- 1 root root 13768 2月 27 12:23 sentinel.conf
sentinel_1.conf
#13 # For example you may use one of the following:
#14 #
#15 # bind 127.0.0.1 192.168.1.1
bind 0.0.0.0
#16 #
#17 # protected-mode no
protected-mode no
#18
#19 # port <sentinel-port>
#20 # The port that this sentinel instance will run on
#21 port 26379
port 26379
#82 # Note: master name should not include special characters or spaces.
#83 # The valid charset is A-z 0-9 and the three characters ".-_".
sentinel monitor mymaster 172.17.0.2 6371 2
#85
#86 # sentinel auth-pass <master-name> <password>
sentinel_2.conf
#13 # For example you may use one of the following:
#14 #
#15 # bind 127.0.0.1 192.168.1.1
bind 0.0.0.0
#16 #
#17 # protected-mode no
protected-mode no
#18
#19 # port <sentinel-port>
#20 # The port that this sentinel instance will run on
#21 port 26379
port 26378
#82 # Note: master name should not include special characters or spaces.
#83 # The valid charset is A-z 0-9 and the three characters ".-_".
sentinel monitor mymaster 172.17.0.2 6371 2
#85
#86 # sentinel auth-pass <master-name> <password>
sentinel_3.conf
#13 # For example you may use one of the following:
#14 #
#15 # bind 127.0.0.1 192.168.1.1
bind 0.0.0.0
#16 #
#17 # protected-mode no
protected-mode no
#18
#19 # port <sentinel-port>
#20 # The port that this sentinel instance will run on
#21 port 26379
port 26377
#82 # Note: master name should not include special characters or spaces.
#83 # The valid charset is A-z 0-9 and the three characters ".-_".
sentinel monitor mymaster 172.17.0.2 6371 2
#85
#86 # sentinel auth-pass <master-name> <password>
拓扑:
3.2 启动redis哨兵
按照上面的主从集群环境进行文件的挂载,然后到容器中根据如下命令进行启动:
[root@iZ2ze4m2ri7irkf6h6n8zoZ src]# ./redis-sentinel ../sentinel.conf
3.3 结论
哨兵的作用就是监控Redis系统的运行状况。它的功能包括以下两个。
(1)监控主服务器和从服务器是否正常运行。
(2)主服务器出现故障时自动将从服务器转换为主服务器。
优点:
- 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
- 主从可以自动切换,系统更健壮,可用性更高。
缺点:
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
4 集群方式三:Redis-Cluster集群
Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
由于Redis cluster搭建方式和所需资源较为复杂,因此在这里就不做演示,具体的搭建方式读者可根据搜索引擎快速找出,在这里只提供概念层面的讲解。
核心概念(转自:https://www.cnblogs.com/runnerjack/p/10269277.html):
redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。
Redis-Cluster采用无中心结构,它的特点如下:
- 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
- 节点的fail是通过集群中超过半数的节点检测失效时才生效。
- 客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
工作方式:
在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
为了保证高可用,redis-cluster集群引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点A1都宕机了,那么该集群就无法再提供服务了。
5 总结
本篇内容介绍了Redis集群搭建的三种不同方式和各自的特点,虽然单机的Redis就有着很好的并发吞吐量,但是在大规模的qps下仍然会显得稍有逊色,因此需要分布式集群的方式来使Redis拥有更高的可用性和容错性。
三种方式中主从复制最为简单,但是只适用于小规模应用中,并且容错性较差,其次是哨兵模式,增强了可用性,但是结构复杂,最后是 Redis-Cluster方式,采用无中心的方式,很好的支持了可用性和容错性,并且应用了一致性哈希的经典思想,目前广泛的应用于Redis集群的搭建中。
参考文章:
https://www.cnblogs.com/pinghengxing/p/11139997.html
https://www.cnblogs.com/runnerjack/p/10269277.html