当前位置: 代码网 > it编程>编程语言>Java > SpringBoot 3整合Redis三种模式(单机/哨兵/集群)的实战指南

SpringBoot 3整合Redis三种模式(单机/哨兵/集群)的实战指南

2026年09月28日 • Java •我要评论
在实际项目中,redis 的部署模式可能因环境而异:开发环境用单机,测试/生产用哨兵或集群。如果每次切换环境都要改代码,那简直是灾难。本文基于 springboot 3 + lettuce,通过一份配

在实际项目中,redis 的部署模式可能因环境而异:开发环境用单机,测试/生产用哨兵或集群。如果每次切换环境都要改代码,那简直是灾难。本文基于 springboot 3 + lettuce,通过一份配置文件的 mode 字段,动态选择单机、哨兵、集群三种连接模式,代码简洁、扩展性强,可直接用于生产。

一、前言

springboot 3 对 redis 的自动配置做了不少优化,但自动配置只支持单一模式(默认读取 spring.data.redis.*)。当你需要同时支持单机/哨兵/集群并在不同环境灵活切换时,手动配置 redisconnectionfactory 才是最优解。

本文的源码来自真实项目,已在 springboot 3.x 环境验证,具备以下特点:

  • ✅ 一份配置文件,通过 mode 字段切换三种模式
  • ✅ 使用 lettuce 作为底层客户端(springboot 3 默认)
  • ✅ 统一 jackson2jsonredisserializer 序列化,支持复杂对象
  • ✅ 密码、数据库、连接池等公共配置统一管理
  • ✅ 代码结构清晰,便于维护和扩展

二、依赖引入

springboot 3 中,spring-boot-starter-data-redis 默认使用 lettuce,无需额外引入 jedis。

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-data-redis</artifactid>
</dependency>
<!-- lettuce 连接池需要 commons-pool2 -->
<dependency>
    <groupid>org.apache.commons</groupid>
    <artifactid>commons-pool2</artifactid>
</dependency>
<!-- jackson 序列化 -->
<dependency>
    <groupid>com.fasterxml.jackson.core</groupid>
    <artifactid>jackson-databind</artifactid>
</dependency>

注意:如果不引入 commons-pool2,连接池配置(lettuce.pool.*)不会生效。

三、配置文件设计

application.yml 中通过 spring.data.redis.mode 控制模式,值分别为 standalone、sentinel、cluster。

spring:
  data:
    redis:
      # redis 运行模式: standalone(单机), sentinel(哨兵), cluster(集群)
      mode: standalone
      # 单机模式配置
      host: 172.16.18.53
      port: 6379
      # 通用配置
      database: 0
      timeout: 60000
      password: dkdh@13579
      # 连接池配置
      lettuce:
        pool:
          max-active: 8
          max-wait: -1
          max-idle: 8
          min-idle: 0
      # 哨兵模式配置
      sentinel:
        master: mymaster
        nodes: 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381
      # 集群模式配置
      cluster:
        nodes: 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,127.0.0.1:7006
        max-redirects: 3

设计思路:

配置项说明
mode核心开关,决定走哪个分支
host/port仅单机模式使用
sentinel.master/nodes仅哨兵模式使用
cluster.nodes/max-redirects仅集群模式使用
password/database/timeout三种模式通用

这样即使同一套代码,只需要在 application-dev.yml、application-prod.yml 中修改 mode 和对应节点即可,无需改一行 java 代码。

四、核心配置类

完整代码如下(来自真实项目,可直接复制使用):

@configuration
public class redisconfig {

    @value("${spring.data.redis.mode}")
    private string redismode;

    @value("${spring.data.redis.host}")
    private string host;

    @value("${spring.data.redis.port}")
    private int port;

    @value("${spring.data.redis.password}")
    private string password;

    @value("${spring.data.redis.database}")
    private int database;

    @value("${spring.data.redis.sentinel.master}")
    private string sentinelmaster;

    @value("${spring.data.redis.sentinel.nodes}")
    private string sentinelnodes;

    @value("${spring.data.redis.cluster.nodes}")
    private string clusternodes;

    @value("${spring.data.redis.cluster.max-redirects}")
    private int clustermaxredirects;

    /**
     * 创建 redis 连接工厂
     */
    @bean
    public redisconnectionfactory redisconnectionfactory() {
        switch (redismode) {
            case "standalone":
                return createstandaloneconnectionfactory();
            case "sentinel":
                return createsentinelconnectionfactory();
            case "cluster":
                return createclusterconnectionfactory();
            default:
                throw new illegalargumentexception("invalid redis mode configuration: " + redismode);
        }
    }

    /**
     * 创建单机模式连接工厂
     */
    private redisconnectionfactory createstandaloneconnectionfactory() {
        redisstandaloneconfiguration config = new redisstandaloneconfiguration();
        config.sethostname(host);
        config.setport(port);
        config.setpassword(password);
        config.setdatabase(database);
        return new lettuceconnectionfactory(config);
    }

    /**
     * 创建哨兵模式连接工厂
     */
    private redisconnectionfactory createsentinelconnectionfactory() {
        redissentinelconfiguration config = new redissentinelconfiguration()
                .master(sentinelmaster);

        // 配置哨兵节点
        for (string node : sentinelnodes.split(",")) {
            string[] parts = node.split(":");
            config.sentinel(parts[0], integer.parseint(parts[1]));
        }

        config.setpassword(password);
        config.setdatabase(database);
        return new lettuceconnectionfactory(config);
    }

    /**
     * 创建集群模式连接工厂
     */
    private redisconnectionfactory createclusterconnectionfactory() {
        redisclusterconfiguration config = new redisclusterconfiguration();

        // 配置集群节点
        for (string node : clusternodes.split(",")) {
            string[] parts = node.split(":");
            config.clusternode(parts[0], integer.parseint(parts[1]));
        }

        config.setmaxredirects(clustermaxredirects);
        config.setpassword(password);
        return new lettuceconnectionfactory(config);
    }

    /**
     * 创建 redistemplate
     */
    @bean
    public redistemplate<string, object> redistemplate(redisconnectionfactory redisconnectionfactory) {
        redistemplate<string, object> template = new redistemplate<>();
        template.setconnectionfactory(redisconnectionfactory);

        // key 使用 string 序列化
        template.setkeyserializer(new stringredisserializer());
        template.sethashkeyserializer(new stringredisserializer());

        // value 使用 jackson2jsonredisserializer
        jackson2jsonredisserializer<object> jacksonserializer = getjacksonserializer();
        template.setvalueserializer(jacksonserializer);
        template.sethashvalueserializer(jacksonserializer);

        template.afterpropertiesset();
        return template;
    }

    /**
     * 创建并配置 jackson2jsonredisserializer
     */
    private jackson2jsonredisserializer<object> getjacksonserializer() {
        objectmapper objectmapper = new objectmapper();
        // 任何字段(包括私有字段)都序列化
        objectmapper.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
        // 启用默认类型信息,反序列化时能正确识别类型
        objectmapper.activatedefaulttyping(
                laissezfairesubtypevalidator.instance,
                objectmapper.defaulttyping.non_final,
                jsontypeinfo.as.property
        );
        return new jackson2jsonredisserializer<>(objectmapper, object.class);
    }
}

五、关键代码解析

1. 三种模式的配置对象差异

模式配置类特点
单机redisstandaloneconfiguration直连单个 host:port
哨兵redissentinelconfiguration通过 master 名称 + 哨兵节点列表,客户端自动感知主从切换
集群redisclusterconfiguration需要配置所有 master 节点,支持 maxredirects

2. 为什么不用 springboot 自动配置?

springboot 3 的 redisautoconfiguration 在检测到 redisconnectionfactory bean 后会自动退避。手动注册 redisconnectionfactory 后,自动配置不会覆盖,这正是我们实现多模式切换的关键。

3.jackson2jsonredisserializer的坑

objectmapper.activatedefaulttyping(
    laissezfairesubtypevalidator.instance,
    objectmapper.defaulttyping.non_final,
    jsontypeinfo.as.property
);
  • 必须开启 defaulttyping:否则反序列化时会丢失具体类型,变成 linkedhashmap。
  • laissezfairesubtypevalidator:宽松验证器,避免某些类被拒绝序列化。
  • non_final:对所有非 final 类型保存类型信息,覆盖大部分 pojo。
  • jsontypeinfo.as.property:类型信息作为 json 的一个属性(默认 @class),可读性好。

如果你不涉及复杂对象存储,可以考虑用 genericjackson2jsonredisserializer 简化配置。

4. redistemplate 的 key/value 序列化策略

序列化器适用位置原因
stringredisserializerkey、hashkey保证 redis 中 key 可读,便于运维排查
jackson2jsonredisserializervalue、hashvalue支持复杂对象,自动类型识别

六、多环境实践建议

在真实项目中,推荐按环境拆分配置文件:

application.yml               # 公共配置
application-dev.yml           # mode: standalone
application-test.yml          # mode: sentinel
application-prod.yml          # mode: cluster

application-prod.yml 示例:

spring:
  data:
    redis:
      mode: cluster
      password: ${redis_password}
      cluster:
        nodes: 10.0.0.1:7001,10.0.0.2:7002,10.0.0.3:7003
        max-redirects: 3

启动时通过 --spring.profiles.active=prod 切换,代码零改动。

七、常见问题排查

q1: 启动报错noauth authentication required

原因:集群/哨兵模式密码未设置。
解决:确保 config.setpassword(password) 被调用。注意集群模式下密码是设置在 redisclusterconfiguration 上,不是每个节点单独设置。

q2: 哨兵模式连接不上,日志显示连接的是127.0.0.1

原因:哨兵返回的 master 地址是内网 ip,客户端无法访问。
解决:在哨兵配置中设置 sentinel announce-ip 和 announce-port 为可访问的地址。

q3: 存进去的对象取出来变成linkedhashmap

原因:objectmapper 未开启 defaulttyping。
解决:参照本文 getjacksonserializer() 中的 activatedefaulttyping 配置。

q4: 连接池配置不生效

原因:未引入 commons-pool2 依赖。
解决:添加依赖即可,lettuce 会自动使用连接池。

q5: springboot 3 中redissentinelconfiguration.setpassword类型问题

原因:springboot 3 中 setpassword 参数是 redispassword 类型,但可以直接传 string(有重载)。
解决:本文写法 config.setpassword(password) 在 springboot 3.x 是兼容的。

八、扩展:连接池参数调优

lettuce 连接池配置建议:

lettuce:
  pool:
    max-active: 8      # 生产建议 16~32,视并发量而定
    max-wait: -1       # 建议改为 2000ms,避免无限阻塞
    max-idle: 8
    min-idle: 2        # 保持一定数量空闲连接,避免冷启动慢
  shutdown-timeout: 200ms

如果你的业务是高并发场景,建议 max-active 调大,并把 max-wait 设置为合理超时(如 2000ms),防止请求堆积。

九、总结

本文通过一份 springboot 3 源码,实现了 redis 单机 / 哨兵 / 集群三种模式的动态切换,核心要点:

  1. 用 mode 字段做开关,代码分支清晰,零侵入切换环境。
  2. 手动注册 redisconnectionfactory,绕过 springboot 自动配置,灵活可控。
  3. 统一序列化策略:key 用 string,value 用 jackson,兼顾可读性与复杂对象支持。
  4. 配置分离:三种模式的节点信息放在不同配置节,多环境拆 profile 即可。

这套方案已在多个生产项目中稳定运行,希望能帮到正在做 redis 多模式适配的你。

以上就是springboot 3整合redis三种模式(单机/哨兵/集群)的实战指南的详细内容,更多关于springboot 3整合redis三种模式的资料请关注代码网其它相关文章!

赞 (0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com