redis 集群搭建(含SpringBoot 相关配置代码)
Redis常见的几种主要使用方式:
-
Redis 多副本(主从)
port 6666
daemonize yes
requirepass 123
# 服务器配置masterauth作用主要是为了后期sentinel引入后重新选举master并且7000端口redis重新加入主从复制时必备的,否则会出现权限不足
masterauth 123
slaveof 172.31.53.175 6666 (从服务器添加)
logfile ./redislog_louie.log
dir ./
#bind 192.168.250.132 127.0.0.1 # 0.0.0.0
RDB:
save 20 1
# 自动保存策略,20秒内有一个key发生变化就自动保存
dbfilename rdb_louie.rdb
# rdb文件名
stop-writes-on-bgsave-error yes
# 发生错误中断写入,建议开启
rdbcompression yes
# 数据文件压缩,建议开启
rdbchecksum yes
# 开启crc64错误校验,建议开启
AOF:
appendonly yes
# 开启aof
appendfilename aof_louie.aof
# aof 日志文件名
appendfsync everysec
# 每秒记录一次日志,建议everysec
no-appendfsync-on-rewrite yes
# 重写过程中是否向日志文件写入,yes 代表rewrite过程中,不向aof文件中追加信息,rewrite结束后再写入,no 代表rewrite执行的同时,也向aof追加信息
auto-aof-rewrite-percentage 100
# 触发重写文件增长百分比 默认100%
auto-aof-rewrite-min-size 64mb
# 触发重写最小aof文件尺寸
../src/redis-cli -c -p 9001 -a 123 info Replication
优点:特点就是主从实例间数据实时同步,并且提供数据持久化和备份策略,可以实现同时对外提供服务和读写分离策略。
缺点:主节点挂掉,需要手动的晋升一个从节点作为主节点,同时需要需要业务方变更配置,主库的写能力受到单机的限制
Redis Sentinel(哨兵)
进入服务器的redis文件夹下,创建一个文件名为 sentinel-26379.conf 配置文件,文件内容如下
port 26379
daemonize yes
logfile "26379.log"
dir "./"
sentinel monitor mymaster 172.31.53.175 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 15000
sentinel auth-pass mymaster 123
bind 0.0.0.0
其他的集群复制改port 即可
参数介绍:
sentinel monitor <master-name> <ip> <redis-port> <quorum>
告诉sentinel去监听地址为ip:port的一个master,这里的master-name可以自定义,quorum是一个数字,指明当有多少个sentinel认为一个master失效时,master才算真正失效
sentinel auth-pass <master-name> <password>
设置连接master和slave时的密码,注意的是sentinel不能分别为master和slave设置不同的密码,因此master和slave的密码应该设置相同。
sentinel down-after-milliseconds <master-name> <milliseconds>
这个配置项指定了需要多少失效时间,一个master才会被这个sentinel主观地认为是不可用的。 单位是毫秒,默认为30秒
sentinel parallel-syncs <master-name> <numslaves>
这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
sentinel failover-timeout <master-name> <milliseconds>
failover-timeout 可以用在以下这些方面:
1. 同一个sentinel对同一个master两次failover之间的间隔时间。
2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
3.当想要取消一个正在进行的failover所需要的时间。
4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了。
./redis-sentinel sentinel-port.conf 配置文件启动哨兵
哨兵是一个独立的进程
优点:解决上面主从的痛点,提供监控、提醒、自动故障转移三个方面的优化。
监控:通过心跳机制,不断地检查主服务器、从服务器和sentinel节点 是否运作正常。
提醒:当谋个服务器出现问题,Sentinel 可以通过api 像管理员发送通知。
自动故障转移:当主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。主服务器恢复后,就成为了一个从服务器。
Redis Cluster
配置文件内容为如下:其他的配置文件修改一下端口以及log文件、日志文件即可。其中中间部分cluster代表
daemonize yes
port 7000
logfile 7000.log
#masterauth 123
#requirepass 123
dir ./
bind 192.168.250.129 127.0.0.1
cluster-enabled yes
cluster-config-file nodes_7000.conf
cluster-node-timeout 15000
appendonly yes
appendfilename aof-7000.aof
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
工作原理:
Redis 集群是一个提供在多个Redis节点间共享数据的程序集。下图以三个master节点和三个slave节点作为示例。Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽,如图中slots所示。
其内部中也需要配置主从,并且内部也是采用哨兵模式,如果有半数节点发现某个异常节点,共同决定更改异常节点的状态,如果改节点是主节点,则对应的从节点自动顶替为主节点,当原先的主节点上线后,则会变为从节点。
如果集群中的master没有slave节点,则master挂掉后整个集群就会进入fail状态,因为集群的slot映射不完整。如果集群超过半数以上的master挂掉,无论是否有slave,集群都会进入fail状态。
根据官方推荐 集群部署至少要3台以上的master节点。那么接下来就开始部署吧
优点:采用去中心化的思想,没有中心节点的说法。自动实现负载均衡与高可用。(是主从复制和哨兵模式的结合版)
解决了redis-Sentinel 主服务器的写压力 ,将主服务器做成集群,每个主节点都有从节点。
方案对比,方案确定
Redis 多副本(主从):master节点宕机,没有主动的选举机制(主节点写,从节点读) 至少3台服务器
Redis-Sentinel:发现master宕机后能进行自动切换,解决一主多从master 节点宕机,写数据的问题(主备切换) 至少3台服务器
Redis-Cluster:多个主节点,减轻了主节点的压力。 至少3台服务器 (3组master - slave )
所以:这里最佳的高可用方式是官方推荐的Redis-Cluster
部署方式
1.Sentinel节点不应该部署在一台物理机上。
2.部署至少三个且奇数个的Sentinel节点
环境配置:
第一:准备3台服务器,每台服务器运行两个redis
主机说明 |
主机IP |
端口 |
Redis |
172.31.53.175 |
7000 7001 |
Redis |
172.31.53.175 |
7002 7003 |
Redis |
172.31.53.175 |
7004 7005
|
在每一台服务器上我们添加一下配置文件
分别为:redis-7000.conf redis-7001.conf redis-7002.conf redis-7003.conf redis-7004.conf redis-7005.conf
配置文件内容为如下:其他的配置文件修改一下端口以及log文件、日志文件即可。其中中间部分cluster代表集群设置。
daemonize yes
#masterauth 123
#requirepass 123
port 7000
logfile 7000.log
dir ./
bind 192.168.250.129 127.0.0.1
cluster-enabled yes
cluster-config-file nodes_7000.conf
cluster-node-timeout 15000
appendonly yes
appendfilename aof-7000.aof
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
上面文件都配置好后,即可启动
启动后我们就可以创建集群
注意:
在redis5.0后 创建集群统一使用redis-cli,
./src/redis-cli --cluster create 172.31.53.175:7000 172.31.53.175:7001 172.31.53.175:7002 172.31.53.175:7003 172.31.53.175:7004 172.31.53.175:7005 --cluster-replicas 1
之前的版本使用redis-trib.rb, hostname -i
./redis-trib.rb create --replicas 1 172.31.53.175:7000 172.31.53.175:7001 172.31.53.175:7002 172.31.53.175:7003 172.31.53.175:7004 172.31.53.175:7005
具体的可以参照redis官方网站查看
https://redis.io/topics/cluster-tutorial
创建集群命令:其中 cluster-replicas 1 代表 一个master后有几个slave,1代表为1个slave节点
验证一下集群是否创建成功
登录redis客户端 ./src/redis-cli -c -p 7000, -c 参数代表连接到集群中
我们先看一下主从配对情况,根据node ID匹配可以得出配比。如下:红色圈出来的为标注,大家可以看看是否有对应的ID,这样我们就区分了主从节点的关系了。
验证数据匹配是否采用哈希槽的方式。大家可自行测试一下。发现我在7000客户端设置的数据,被分配到7002上了。
如果是使用redis-trib.rb工具构建集群,集群构建完成前不要配置密码,集群构建完毕再通过config set + config rewrite命令逐个机器设置密码
客户端实现
Yml:
spring:
redis:
timeout: 60000ms
cluster:
max-redirects: 3
nodes:
- 47.105.102.123:7001
- 47.105.102.123:7002
- 47.105.102.123:7003
- 47.105.102.123:7004
- 47.105.102.123:7005
- 47.105.102.123:7000
lettuce:
pool:
max-active: 1000
max-idle: 10
min-idle: 5
max-wait: -1
@Configuration
public class RedisConfiguration {
@Autowired
private RedisProperties redisProperties;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(redisProperties.getCluster().getNodes());
redisClusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
redisClusterConfiguration.setPassword(redisProperties.getPassword());
//支持自适应集群拓扑刷新和静态刷新源
ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
.enablePeriodicRefresh()
.enableAllAdaptiveRefreshTriggers()
.refreshPeriod(Duration.ofSeconds(redisProperties.getTimeout().getSeconds()))
.build();
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
.topologyRefreshOptions(clusterTopologyRefreshOptions).build();
//从优先,读写分离,读从可能存在不一致,最终一致性CP
LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.SLAVE_PREFERRED)
.clientOptions(clusterClientOptions).build();
return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
备注:
<!-- lettuce客户端需要使用到 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
哨兵模式:客户端
spring:
redis:
timeout: 60000ms
lettuce:
pool:
max-active: 1000
max-idle: 10
min-idle: 5
max-wait: -1
sentinel:
master: mymaster
nodes: 47.105.102.123:29000,47.105.102.123:29001,47.105.102.123:29002
password: 123server:
port: 9999
@Configuration
public class RedisConfiguration {
@Autowired
private RedisProperties redisProperties;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
List<RedisNode> nodes = redisProperties.getSentinel().getNodes().stream().map(e -> new RedisNode(e.split(":")[0], Integer.valueOf(e.split(":")[1])))
.collect(Collectors.toList());
RedisSentinelConfiguration redisClusterConfiguration = new RedisSentinelConfiguration();
redisClusterConfiguration.setSentinels(new HashSet<>(nodes));
redisClusterConfiguration.setPassword(redisProperties.getPassword());
redisClusterConfiguration.setMaster(redisProperties.getSentinel().getMaster());
//从优先,读写分离,读从可能存在不一致,最终一致性CP
LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.SLAVE_PREFERRED)
.build();
return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
声明:部分图片及叙述借鉴自其他博友,对此表示感谢!!!