Redis高可用部署:3台服务器打造哨兵集群
1、Redis集群介绍Redis 集群(Redis Cluster)是Redis提供的一种分布式部署方式,旨在提供高可用性。如果某个主节点发生故障,集群能够自动进行故障转移,将副本提升为主节点,从而保证数据的持续可用,Redis 集群非常适合于大规模缓存系统和大数据量场景。
2、搭建集群的几种方式
[*]主从复制(Master-Slave):这种模式下,主节点(Master)负责数据写入和读取,而从节点(Slave)则只能用于数据读取和备份,不能够写入数据。若主节点发生故障,需人工介入,将某个从节点提升为新的主节点。该模式没有自动故障转移,需要手动处理故障,因此可用性较差。最少需要2台服务器搭建一主一从或者多台服务器搭建一主多从。在资源不允许的情况下,可以借助高可用工具实现自动故障转移。如:Keepalived + Redis主从实现主备双活。
[*]哨兵模式(Redis Sentinel): 哨兵模式下,需要额外搭建哨兵(Sentinel)服务用于监控Redis集群中的主节点,并且能够在主节点发生故障时自动进行故障转移,通过选举的方式,将从节点升级为主节点。它提供高可用性和自动恢复能力。常见的集群方式为:1主2从 + 3个Sentinel进程,哨兵(Sentinel)进程可以部署在相同的3个节点上,以减少服务器资源。比较适用于需要高可用的中小型应用,数据量不是特别大的场景。
[*]分片集群(Redis Cluster):这一模式支持数据分片(Sharding)存储和多节点水平扩展,有效的提高了内存利用率和写入性能。插入的数据会根据一定的规则(哈希槽)被分布到不同的节点上,每个节点负责数据的一个子集(通过哈希槽的范围),它会自动管理数据的分布和迁移。它采用了去中心化的多主多从架构,最低配置要求为3个主节点(Master)和3个从节点(Slave),通常情况下可以在3台服务器中每台服务器配置一主一从进行搭建,虽然这样会影响一定的性能,但是可以减少服务器所需的资源。如果需要发挥其最大的价值,至少需要6台服务器节点才可以。该集群非常适用于更大规模和更高要求的数据处理场景。
3、选择集群模式
这里根据所属的业务系统需要的Redis需求进行合适的选择。使用Redis Cluster 模式搭建分片集群的要求比较高,客户端必须该支持集群模式,而且配置Redis Cluster 需要比普通的Redis主从模式复杂的多,需要的成本比较高。只有当应用需要高性能、高吞吐量,并且对数据分布和扩展性有需求时,Redis Cluster是更好的选择。使用Redis Sentinel模式搭建集群相对来说不是特别复杂,只需要搭建Redis主从模式后在启动相关的Sentinel即可。如果数据量不是特别大,使用Redis集群只是用来提供高可用性和故障转移,可以考虑使用 Redis Sentinel 。本篇使用 哨兵模式(Redis Sentinel) 进行搭建Redis集群。
哨兵模式(Redis Sentinel)相关介绍:
Redis哨兵模式(Redis Sentinel)首次引入是在 Redis 2.8.0 版本中。该版本于 2013年12月 发布。Redis Sentinel的设计初衷是为了弥补 Redis 在高可用性和自动故障转移方面的不足,特别是对于主从复制架构中的主节点宕机的容错处理。通过Redis Sentinel可以实现自动检测故障和自动故障转移,从而提高 Redis 的可用性和可靠性。在这一模式中,哨兵负责实时监控主节点的运行状况。如果主节点出现故障,哨兵将基于预设的投票机制,自动将某个从节点晋升为新的主节点,以保障服务的连续性和数据的可用性。哨兵本身是一个独立的进程,运行在Redis本身进程之外。它通过持续监控 Redis 服务器的状态,包括主节点和从节点,并定期通过 ping 主从节点来确认它们是否仍然可用,来判断被监控的Redis实例是否正常运行来确保Redis服务的稳定性。
4、环境准备
4.1、服务器信息
根据以上搭建的几种方式和相关建议,在3台服务器节点的情况下,首选Redis Sentinel哨兵模式进行搭建。以下是服务器信息:
服务器IP 地址部署应用node1192.168.42.131Redis Master +Sentinelnode2192.168.42.132Redis Slave + Sentinelnode3192.168.42.133Redis Slave + Sentinel4.2、软件版本
[*]操作系统:CentOS 7.9
[*]Redis 版本:5.0
5、配置Redis主从复制
5.1、创建目录
分别在三台服务器中创建以下目录:
# 选择Redis存储文件目录,创建以下目录mkdir -p /root/docker/redis/config /root/docker/redis/data /root/docker/redis/sentinel# config 存入redis配置文件信息 如:redis.conf# data 存入持久化Redis数据# sentinel 存入哨兵的配置文件信息 如:sentinel.conf下载配置文件redis.conf到本地,并上传到服务器config目录,下载地址:https://github.com/redis/redis/blob/5.0.4/redis.conf
5.2、配置Redis配置文件
在 node1、node2 和 node3 上更新Redis配置文件信息 vim ~/docker/redis/config/redis.conf,配置密码和主从关系。
主节点(node1):
# 运行端口port 6379# 允许外部所有IP访问bind 0.0.0.0# 是否以守护进程运行,Docker运行必须设置为no。daemonize no# redis密码requirepass "1234qwer"# 配置主从连接密码masterauth "1234qwer"# 开启持久化appendonly yes#对外固定开放IP地址slave-announce-ip 192.168.42.131#对外固定开放端口slave-announce-port 6379从节点(node2):
port 6379bind 0.0.0.0daemonize norequirepass "1234qwer"masterauth "1234qwer"appendonly yes# 对外固定开放IP地址slave-announce-ip 192.168.42.132# 对外固定开放端口slave-announce-port 6379# 配置主节点的IP和端口REPLICAOF192.168.42.131 6379从节点(node3)
port 6379bind 0.0.0.0daemonize norequirepass "1234qwer"masterauth "1234qwer"appendonly yes# 对外固定开放IP地址slave-announce-ip 192.168.42.133# 对外固定开放端口slave-announce-port 6379# 配置主节点的IP和端口REPLICAOF192.168.42.131 63795.3、启动Redis容器
主节点(node1):
docker run -d -p 6379:6379 --restart=always --privileged=true --name redis-master -v ~/docker/redis/config/redis.conf:/etc/redis/redis.conf -v ~/docker/redis/data:/data redis:5.0 redis-server /etc/redis/redis.conf从节点(node2):
docker run -d -p 6379:6379 --restart=always --privileged=true --name redis-slave -v ~/docker/redis/config/redis.conf:/etc/redis/redis.conf -v ~/docker/redis/data:/data redis:5.0 redis-server /etc/redis/redis.conf从节点(node3):
docker run -d -p 6379:6379 --restart=always --privileged=true --name redis-slave -v ~/docker/redis/config/redis.conf:/etc/redis/redis.conf -v ~/docker/redis/data:/data redis:5.0 redis-server /etc/redis/redis.conf5.4、验证主从复制
登录redis输入:info replication 可以查看Redis集群配置信息。如果搭建成功,会显示节点信息。
主节点(node1)显示以下信息:
# 首先通过docker进入redis容器docker exec -it redis-master bash# 进入成功后,登录redisredis-cli -p 6379# 登录成功,进行密码认证,然后查看集群配置信息127.0.0.1:6379> auth 1234qwerok127.0.0.1:6379> info replication# Replicationrole:masterconnected_slaves:2slave0:ip=192.168.42.132,port=6379,state=online,offset=280,lag=0slave1:ip=192.168.42.133,port=6379,state=online,offset=280,lag=0master_replid:a052149baf6e1dbb345f55ceb37476e95efca431master_replid2:0000000000000000000000000000000000000000master_repl_offset:280second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:280从节点(node2)显示以下信息:
# 首先通过docker进入redis容器docker exec -it redis-slave bash# 进入成功后,登录redisredis-cli -p 6379# 登录成功,进行密码认证,然后查看集群配置信息127.0.0.1:6379> auth 1234qwerok127.0.0.1:6379> info replication# Replicationrole:slavemaster_host:192.168.42.131master_port:6379master_link_status:upmaster_last_io_seconds_ago:10master_sync_in_progress:0slave_repl_offset:448slave_priority:100slave_read_only:1connected_slaves:0master_replid:a052149baf6e1dbb345f55ceb37476e95efca431master_replid2:0000000000000000000000000000000000000000master_repl_offset:448second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:15repl_backlog_histlen:434从节点(node3)显示以下信息:
# 首先通过docker进入redis容器docker exec -it redis-slave bash# 进入成功后,登录redisredis-cli -p 6379# 登录成功,进行密码认证,然后查看集群配置信息127.0.0.1:6379> auth 1234qwerok127.0.0.1:6379> info replication# Replicationrole:slavemaster_host:192.168.42.131master_port:6379master_link_status:upmaster_last_io_seconds_ago:3master_sync_in_progress:0slave_repl_offset:532slave_priority:100slave_read_only:1connected_slaves:0master_replid:a052149baf6e1dbb345f55ceb37476e95efca431master_replid2:0000000000000000000000000000000000000000master_repl_offset:532second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:532输出以上信息,说明Redis主从配置已经连接成功了,接下来验证一下存取数据。
5.5、验证存取数据
在两个从节点存入数据会报错。
127.0.0.1:6379> set verify-key "Test Verify is Success"(error) READONLY You can't write against a read only replica.因为从节点只可以读取数据不可以写入数据,要想写入数据需要在主节点进行。主节点可以读写数据。登录主节点存入数据:
127.0.0.1:6379> set verify-key "Test Verify is Success"OK127.0.0.1:6379> get verify-key"Test Verify is Success"然后分别在两个从节点读取数据,看看是否可以读取成功。
# 登录 192.168.42.132 节点redis信息127.0.0.1:6379> get verify-key"Test Verify is Success"# 登录 192.168.42.133 节点redis信息127.0.0.1:6379> get verify-key"Test Verify is Success"获取成功,说明主从复制搭建成功。
6、配置 Redis 哨兵
6.1、主节点(node1)配置启动
进入:/root/docker/redis/sentinel目录,下载sentinel.conf文件并上传的该目录,配置哨兵连接。 文件下载地址: https://github.com/redis/redis/blob/5.0.4/sentinel.conf
下载成功后,修改配置文件。执行命令vim /root/docker/redis/sentinel/sentinel.conf
# 哨兵节点的端口号port 26379bind 0.0.0.0logfile "/var/log/redis-sentinel.log"# 关闭保护模式,允许外部访问,Docker环境中必须设置为 noprotected-mode no# 禁止Redis以守护进程方式运行daemonize no# 哨兵工作目录dir /tmpsentinel monitor mymaster 192.168.42.131 6379 2# 解析:# - mymaster:主节点的名称标识# - 49.235.180.130:主节点的IP地址# - 6379:主节点的端口号# - 2:判定主节点故障需要的哨兵投票数(至少2个哨兵认为主节点故障才会进行故障转移)#redis节点密码sentinel auth-pass mymaster 1234qwer# 解析:# - mymaster:对应的主节点名称# - 1234qwer:Redis节点的访问密码# 主观下线时间,单位毫秒sentinel down-after-milliseconds mymaster 10000# 解析:如果10秒内无法连接主节点,则认为主节点主观下线# 同步配置sentinel parallel-syncs mymaster 1# sentinel parallel-syncs:并行同步配置,mymaster:主节点的别名,1:并行同步的从节点数量。#作用和影响:#1. 控制故障转移期间的数据同步行为,当值为1时:从节点逐个同步,当值大于1时:多个从节点并行同步#示例场景:# parallel-syncs = 1 时:主节点故障,slave1被选为新主节点 slave2开始与slave1同步,slave3等待slave2同步完成后再同步,过程较慢但主节点压力小# parallel-syncs = 2 时:主节点故障,slave1被选为新主节点,slave2和slave3同时与slave1同步,过程较快但主节点压力大# 故障转移超时时间sentinel failover-timeout mymaster 600000# 解析:故障转移的超时时间为600秒(10分钟)sentinel deny-scripts-reconfig yes# 解析:禁止通过脚本修改哨兵配置,提高安全性# 对外宣告的IP地址sentinel announce-ip "192.168.42.131"# 对外宣告的端口号sentinel announce-port 26379# 解析:# - 用于在Docker或NAT网络环境中,声明哨兵节点的实际可访问地址# - 确保其他节点可以正确连接到该哨兵节点启动哨兵:
docker run -d -p 26379:26379 --restart=always --privileged=true --name redis-sentinel-v /root/docker/redis/sentinel/sentinel.conf:/etc/redis/sentinel.conf redis:5.0 redis-sentinel /etc/redis/sentinel.conf6.2、从节点(node2)配置启动
下载并修改配置文件。执行命令vim /root/docker/redis/sentinel/sentinel.conf
# 配置文件和主节点启动的哨兵配置文件一致,只需要更改一下对外监听的哨兵地址即可# 对外宣告的IP地址sentinel announce-ip "192.168.42.132"# 对外宣告的端口号sentinel announce-port 26379启动哨兵:
docker run -d -p 26379:26379 --restart=always --privileged=true --name redis-sentinel-v /root/docker/redis/sentinel/sentinel.conf:/etc/redis/sentinel.conf redis:5.0 redis-sentinel /etc/redis/sentinel.conf6.3、从节点(node3)配置启动
下载并修改配置文件。执行命令vim /root/docker/redis/sentinel/sentinel.conf
# 配置文件和主节点启动的哨兵配置文件一致,只需要更改一下对外监听的哨兵地址即可# 对外宣告的IP地址sentinel announce-ip "192.168.42.133"# 对外宣告的端口号sentinel announce-port 26379启动哨兵:
docker run -d -p 26379:26379 --restart=always --privileged=true --name redis-sentinel-v /root/docker/redis/sentinel/sentinel.conf:/etc/redis/sentinel.conf redis:5.0 redis-sentinel /etc/redis/sentinel.conf6.4、验证哨兵集群
首先选择一台服务器进行登录哨兵,可以在主节点或从节点执行以下命令,进入哨兵容器:
docker exec -it redis-sentinel bash连接哨兵客户端:
redis-cli -p 26379查看哨兵信息:
# info sentinel127.0.0.1:26379> info sentinel# Sentinelsentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0sentinel_simulate_failure_flags:0master0:name=mymaster,status=ok,address=192.168.42.131:6379,slaves=2,sentinels=3#输出以上信息配置成功查看哨兵信息,如果发现已经集成成功了,接下来就可以验证哨兵模式了。在哨兵模式下,当master服务器宕机之后,哨兵自动会在从 节点服务器里面投票选举一个master服务器来,这个master服务器也可以进行读写操作。
先把主节点关闭掉:
docker stop redis-master再次查看哨兵信息:
127.0.0.1:26379> info sentinel# Sentinelsentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0sentinel_simulate_failure_flags:0master0:name=mymaster,status=ok,address=192.168.42.132:6379,slaves=2,sentinels=3# 根据最后一行信息,可以看出主节点的IP进行变化了。由192.168.42.131 -> 192.168.42.1327、SpringBoot连接Redis哨兵模式
7.1、引入依赖
在 pom.xml 中添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId></dependency>7.2、配置 application.yml
spring: redis: #密码 password: 1234qwer timeout: 5000 sentinel: # 对应前面sentinel的配置文件信息 master: mymaster # 三个哨兵的ip端口 nodes: 192.168.42.131:26379,192.168.42.132:26379,192.168.42.133:263797.3、编写测试代码
创建 RedisConfig使用Jedis客户端连接
@Configurationpublic class RedisConfig extends CachingConfigurerSupport { @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.redis.password}") private String password; // 新增哨兵配置 @Value("${spring.redis.sentinel.master}") private String masterName; @Value("${spring.redis.sentinel.nodes}") private String sentinelNodes; @Bean public JedisSentinelPool jedisSentinelPool() { // 解析哨兵节点 Set<String> sentinels = new HashSet<>(Arrays.asList(sentinelNodes.split(","))); // 创建哨兵连接池 JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName, sentinels, createPoolConfig(), timeout, password,0); log.info("使用哨兵模式,主节点名称:{} JedisSentinelPool注入成功!!", masterName); return sentinelPool; } private JedisPoolConfig createPoolConfig() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(50); jedisPoolConfig.setMinIdle(5); jedisPoolConfig.setMaxWaitMillis(10000); jedisPoolConfig.setJmxEnabled(true); return jedisPoolConfig; }}创建 JedisTemplate操作Redis类
@Component@ConditionalOnClass(RedisConfig.class)public class JedisTemplate { @Autowired(required = false) private JedisSentinelPool jedisSentinelPool; /** * 保存数据 * @param key * @param value * @param seconds 小于等于0时为不限制时长 */ public final void setStringData(String key, String value, int seconds) { Jedis jedis = null; try { jedis = jedisSentinelPool.getResource(); boolean flag = jedis.exists(key); if (flag) { jedis.del(key); } jedis.set(key, value); if (seconds > 0) { jedis.expire(key, seconds); } } catch (Exception e) { e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } } /** * 根据key获取redis里面的字符串 * @param key * @return */ public String getStringData(String key) { String str = null; Jedis jedis = null; try { jedis = jedisSentinelPool.getResource(); str = jedis.get(key); } catch (Exception e) { e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } return str; } /** * 删除一个key * @param key */ public void deleteKey(String key) { Jedis jedis = null; try { jedis = jedisSentinelPool.getResource(); jedis.del(key); } catch (Exception e) { e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } }}创建RedisTestController测试Web控制器
@RestControllerpublic class RedisTestController { @Autowired private JedisTemplate jedisTemplate; @GetMapping("/redis/test/{key}") public Response getRedisSentinelTest(@PathVariable("key") String key){ String randomNumeric = RandomStringUtils.randomNumeric(12); jedisTemplate.setStringData(key,"哈哈哈" + randomNumeric, 180); String stringData = jedisTemplate.getStringData(key); System.out.println(stringData); return Response.success(stringData); }}7.4、启动项目
2025-02-22 17:40:14.223 INFO-- Trying to find master from available Sentinels...2025-02-22 17:40:14.328 INFO-- Redis master running at 192.168.42.131:6379, starting Sentinel listeners...2025-02-22 17:40:14.345 INFO-- Created JedisPool to master at 192.168.42.131:63792025-02-22 17:40:14.345 INFO-- 使用哨兵模式,主节点名称:mymaster JedisSentinelPool注入成功!!2025-02-22 17:40:48.609 INFO-- Starting ProtocolHandler ["http-nio-9090"]2025-02-22 17:40:48.630 INFO-- Tomcat started on port(s): 9090 (http) with context path ''2025-02-22 17:40:48.642 INFO-- Started App in 2.892 seconds (JVM running for 3.562)2025-02-22 17:41:05.914 INFO-- Initializing Spring DispatcherServlet 'dispatcherServlet'2025-02-22 17:41:05.915 INFO-- Initializing Servlet 'dispatcherServlet'2025-02-22 17:41:05.929 INFO-- Completed initialization in 14 ms调用接口使用PostMan调用接口进行测试:http://localhost:9090/redis/test/1234 ,测试哨兵是否创建成功。
8、总结
到此Redis哨兵集群就搭建完成了。Redis哨兵为我们提供了强大的监控和自动修复功能,这使得Redis服务在面临各种故障时,能够快速自动恢复,减少人工干预。回顾整个搭建过程,搭建并不是特别复杂,主要也就3个步骤,包括:1、配置Redis主从复制。2、设置Redis哨兵节点。3、验证高可用性。最后,希望本文能帮助你了解Redis哨兵模式的搭建流程,并为你提供参考。如果你在搭建过程中遇到问题,欢迎讨论或查阅Redis官方文档,进一步优化配置。
页:
[1]