文章目录

  • 前言
  • Redis哨兵java实现
  • 一、添加依赖
  • 二、改好RedisConfig
  • 三、修改nacos中Redis的配置
  • 四、其他
  • 1.踩到的坑如下:
  • Redis主从复制与哨兵模式搭建
  • 先装好Redis
  • 一、主从复制
  • 1.先在主机192.168.2.237的/usr/local目录下新建redisfile文件,并将之前安装好的redis文件复制到该目录下。然后redis目录下创建redis的配置文件redis-6379.conf
  • 2. 其次在从机192.168.2.37(我这里将redis的两台从机都部署在同一台虚拟机的不同端口)的/usr/local目录新建redisfile文件夹和redisfile2文件夹,并将之前安装好的redis文件分别复制到以上两个文件夹目录下。以下在各自的redis目录下创建创建redis的配置文件步骤一样,端口及地址做下改变即可。我在创建的两个redis配置文件分别命名为redis-6340.conf和redis-6341,其中redis-6341.conf配置如下:
  • 二、哨兵模式
  • 1. 先在主机192.168.2.237的redis目录下创建sentinel的配置文件sentinel-26379.conf
  • 2.其次在从机192.168.2.37各自的redis目录下创建sentinel的配置文件,与上步骤一样,端口及地址做下改变即可。我在创建的两个sentinel配置文件分别命名为sentinel-26340.conf和sentinel-26341,其中sentinel-26340.conf配置如下:
  • 三、运行
  • 1.先运行redis
  • 1.1 先运行192.168.2.237的主机
  • 1.2 再运行192.168.2.37的两台从机
  • 2.再运行sentinel
  • 2.1 先运行192.168.2.237的主机
  • 2.2 再运行192.168.2.37的两台从机
  • 3.试试故障转移
  • 四、其他命令



前言




Redis哨兵java实现

一、添加依赖

因我的构建cloud项目,这里直接把可能会用的依赖都放到一起

<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>




二、改好RedisConfig

前提是已经建好Redis的springboot项目,按网上的操作就行。我的是在这基础上操作

package com.ams.common.redis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.List;

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {

    private final RedisProperties properties;

    public RedisConfig(RedisProperties properties){
        this.properties = properties;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(getConnectionFactory());

        // 用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer); // key
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域(field,get,set),访问修饰符(public,private,protected)
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); //value

        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public RedisConnectionFactory getConnectionFactory() {
        //哨兵模式
        RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
        configuration.setMaster(properties.getSentinel().getMaster());
        configuration.setPassword(properties.getPassword());
        configuration.setDatabase(properties.getDatabase());
        List<String> nodes = properties.getSentinel().getNodes();
        nodes.forEach(node -> {
            String[] str = node.split(":");
            RedisNode redisServer = new RedisServer(str[0], Integer.parseInt(str[1]));
            configuration.sentinel(redisServer);
        });
        LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration, getPool());
        //使用前先校验连接,这个最好是要配置:不然会带来connection reset by peer
        factory.setValidateConnection(true);
        return factory;
    }

    @Bean
    public LettuceClientConfiguration getPool() {
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        //redis客户端配置:超时时间默认
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder
                builder = LettucePoolingClientConfiguration.builder().
                commandTimeout(Duration.ofMillis(60000));
        //连接池配置
        RedisProperties.Pool pool = properties.getLettuce().getPool();
        genericObjectPoolConfig.setMaxIdle(pool.getMaxIdle());
        genericObjectPoolConfig.setMinIdle(pool.getMinIdle());
        genericObjectPoolConfig.setMaxTotal(pool.getMaxActive());
        genericObjectPoolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
        builder.shutdownTimeout(Duration.ofMillis(4000));
        builder.poolConfig(genericObjectPoolConfig);
        return builder.build();
    }




三、修改nacos中Redis的配置

redis:
  sentinel:
    master: mymaster
    nodes: 192.168.2.237:26379,192.168.2.37:26340,192.168.2.37:26341
  password: 123456
  timeout: 1000
  database: 3
  lettuce:
    pool:
      max-active: 8
      max-wait: 1000
      max-idle: 8
      min-idle: 0




四、其他

1.踩到的坑如下:

java一直无法连接sentinel,关闭防火墙也不行
解决方法:因为之前redis配置的防火墙打开,开放端口,所以这里也不能关闭防火墙,而是要开放sentinel的几个端口。






Redis主从复制与哨兵模式搭建

先装好Redis

网上有很多教程,这里不做赘述,我的最简单安装命令如下:

cd /usr/local

wget https://download.redis.io/releases/redis-6.2.6.tar.gz

tar -zxvf redis-6.2.6.tar.gz

mv redis-6.2.6 redis

cd /usr/local/redis

yum install gcc-c++

y

gcc -v 

make

make install

备注: 提下我踩过的坑:

caused by:io.lettuce.core.redisException:connot retrieve initial cluster partitions from initial URIs

原因:因为端口未开放
解决:以开放6379端口为例

#查看开放的端口
$ firewall-cmd --list-ports
#查询6379端口是否开放
$ firewall-cmd --query-port=6379/tcp
firewall-cmd --add-port=6379/tcp --permanent
#重新加载生效
firewall-cmd --reload

一、主从复制

1.先在主机192.168.2.237的/usr/local目录下新建redisfile文件,并将之前安装好的redis文件复制到该目录下。然后redis目录下创建redis的配置文件redis-6379.conf

daemonize yes  #守护线程
protected-mode no
port 6379		#端口
logfile "6379.log"	#日志文件
dir "/usr/local"	#持久化文件目录路径
requirepass "123456"	#需要密码
masterauth "123456"		#主机密码
#绑定虚拟机地址及本地映射地址
bind 192.168.2.237 127.0.0.1 
appendonly yes		#开启持久化aof模式
appendfilename "aof-6379.aof"	#aof文件夹
appendfsync everysec		#每秒更新
no-appendfsync-on-rewrite yes	#同步数据时不重写
auto-aof-rewrite-percentage 100	 #增加倍数达到100%重写
auto-aof-rewrite-min-size 64mb	#重写最低文件大小为64mb

2. 其次在从机192.168.2.37(我这里将redis的两台从机都部署在同一台虚拟机的不同端口)的/usr/local目录新建redisfile文件夹和redisfile2文件夹,并将之前安装好的redis文件分别复制到以上两个文件夹目录下。以下在各自的redis目录下创建创建redis的配置文件步骤一样,端口及地址做下改变即可。我在创建的两个redis配置文件分别命名为redis-6340.conf和redis-6341,其中redis-6341.conf配置如下:

port 6340
daemonize yes
logfile "6340.log"
dir "/usr/local"
requirepass "123456"
masterauth "123456"
bind 192.168.2.37 127.0.0.1
appendonly yes
appendfilename "aof-6340.aof"
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb




二、哨兵模式

1. 先在主机192.168.2.237的redis目录下创建sentinel的配置文件sentinel-26379.conf

port 26379	#端口
daemonize yes	#守护线程
logfile "26379.log"	#日志
dir "/usr/local"	#持久化目录
#sentinel监听主机为master及其地址和端口
#且当有2个sentinel认为master失效后才算真正失效
sentinel monitor mymaster 192.168.2.237 26379 2
#当达到15000毫秒(默认30s),master失效才被sentinel认为失效
sentinel failover-timeout mymaster 15000
#连接master和slave的密码,且master和slave的密码须一致
sentinel auth-pass mymaster 123456
#发生failover时主备切换有1个slave同时对新的master进行同步
sentinel parallel-syncs mymaster 1
#该sentinel绑定的地址及端口
bind 192.168.2.237 127.0.0.1

备注: 以上master若已改名,则应全部替换为新的名字,如mymaster

2.其次在从机192.168.2.37各自的redis目录下创建sentinel的配置文件,与上步骤一样,端口及地址做下改变即可。我在创建的两个sentinel配置文件分别命名为sentinel-26340.conf和sentinel-26341,其中sentinel-26340.conf配置如下:

port 26340	#端口
daemonize yes	#守护线程
logfile "26340.log"	#日志
dir "/usr/local"	#持久化目录
#sentinel监听主机为master及其地址和端口
#且当有2个sentinel认为master失效后才算真正失效
sentinel monitor mymaster 192.168.2.37 26340 2
#当达到15000毫秒(默认30s),master失效才被sentinel认为失效
sentinel failover-timeout mymaster 15000
#连接master和slave的密码,且master和slave的密码须一致
sentinel auth-pass mymaster 123456
#发生failover时主备切换有1个slave同时对新的master进行同步
sentinel parallel-syncs mymaster 1
#该sentinel绑定的地址及端口
bind 192.168.2.37 127.0.0.1

三、运行

1.先运行redis

1.1 先运行192.168.2.237的主机

我直接在usr/local目录下运行:

./redisfile/redis/src/redis-server ./redisfile/redis/redis-6379.conf

1.2 再运行192.168.2.37的两台从机

这里直接在/usr/local目录下编写一个.sh脚本同时启动两个redis服务,节省后面每次启动的时间,脚本start-all.sh
如下:

./redisfile/redis/src/redis-server ./redisfile/redis/redis-6340.conf

./redisfile2/redis/src/redis-server ./redisfile2/redis/redis-6341.conf

编写好后要赋予其读写执行的权限,最后启动

chmod +x start-all.sh
sh start-all.sh

2.再运行sentinel

2.1 先运行192.168.2.237的主机

我直接在usr/local目录下运行:

./redisfile/redis/src/redis-sentinel ./redisfile/redis/sentinel -26379.conf

2.2 再运行192.168.2.37的两台从机

这里直接在/usr/local目录下编写一个.sh脚本同时启动两个sentinel 服务,节省后面每次启动的时间,脚本start-all-sentinel.sh
如下:

./redisfile/redis/src/redis-sentinel ./redisfile/redis/sentinel-26340.conf

./redisfile2/redis/src/redis-sentinel ./redisfile2/redis/sentinel-26341.conf

编写好后要赋予其读写执行的权限,最后启动

chmod +x start-all-sentinel.sh
sh start-all-sentinel.sh

待redis和sentinel都运行后,sentinel的配置文件也发生了改变,其中sentinel-6379.conf如下:

sentinel known-slave mymaster 192.168.2.37 6340
sentinel known-slave mymaster 192.168.2.37 6341
sentinel known-sentinel mymaster 192.168.2.37 26340 75f0266605bfa79a173076e6394d1c5f0032ebd6
sentinel known-sentinel mymaster 192.168.2.37 26341 03e6af6441ffac5c9602c693974e299136ac4632

3.试试故障转移

现在主机是6379,我们把6379的redis服务停止,同时停止26341的sentinel服务,看sentinel是否能够选举出新的主机并切换过去。

## 先在主机192.168.2.237找出6379redis的运行进程
ps -ef|grep redis
## 再杀死进程
kill -9 76529

同上从机192.168.2.37找出26341sentinel的运行进程并杀死
过15秒后看sentinel-26341.conf的主机master已经由6379变成6340了

port 26379
daemonize yes
logfile "26379.log"
dir "/usr/local"
sentinel myid 72ea9a3eb747027d16a2911abe372e7f5a24b620
sentinel monitor mymaster 192.168.2.37 26341 2




四、其他命令

// 登录客户端后查看redis主从等信息
 info replication




备注:踩坑如下,如有知道的留言给大家避坑,感谢感谢!:
master可读可写,另外一台salve(经三次测试每次都是下一次宕机后不会成为主机的从机)竟然也是可读可写,但是其中的数据并不会同步到master和另一台slave,包括主机切换后也不会。








随心所往,看见未来。Follow your heart,see light!