一、背景

最近项目中 3主3从 redis集群出现单节点宕机,造成master迁移,但是发现应用无法正常连接redis,使用的是Lettuce连接方式。

二、原因分析

分析了代码,发现默认Lettuce是不会刷新拓扑io.lettuce.core.cluster.models.partitions.Partitions#slotCache,最终造成槽点查找节点依旧找到老的节点,自然访问不了了。

槽点计算

io.lettuce.core.cluster.SlotHash#getSlot(java.lang.String)
io.lettuce.core.cluster.SlotHash#getSlot(byte[])

/*** Calculate the slot from the given key.** @param key the key* @return slot*/public static int getSlot(ByteBuffer key) {int limit = key.limit();int position = key.position();int start = indexOf(key, SUBKEY_START);if (start != -1) {int end = indexOf(key, start + 1, SUBKEY_END);if (end != -1 && end != start + 1) {key.position(start + 1).limit(end);}}try {if (key.hasArray()) {return CRC16.crc16(key.array(), key.position(), key.limit() - key.position()) % SLOT_COUNT;}return CRC16.crc16(key) % SLOT_COUNT;} finally {key.position(position).limit(limit);}}

根据槽点查找节点

redis集群总共16384个槽点。
返回的节点对象类似:
RedisClusterNodeSnapshot [uri=RedisURI [host=‘127.0.0.1’, port=7004], nodeId=‘93e0fa29f8639a2e11015736532a0d186f0ab8e9’, connected=true, slaveOf=‘null’, pingSentTimestamp=0, pongReceivedTimestamp=1569735410781, configEpoch=9, flags=[MASTER], slot count=5461]

    /*** Retrieve a {@link RedisClusterNode} by its slot number. This method does not distinguish between masters and slaves.** @param slot the slot* @return RedisClusterNode or {@literal null}*/public RedisClusterNode getPartitionBySlot(int slot) {return slotCache[slot];}

三、修改

1、分析步骤

是否启动拓扑刷新的逻辑源码

private void activateTopologyRefreshIfNeeded() {if (getOptions() instanceof ClusterClientOptions) {ClusterClientOptions options = (ClusterClientOptions) getOptions();ClusterTopologyRefreshOptions topologyRefreshOptions = options.getTopologyRefreshOptions();if (!topologyRefreshOptions.isPeriodicRefreshEnabled() || clusterTopologyRefreshActivated.get()) {return;}if (clusterTopologyRefreshActivated.compareAndSet(false, true)) {ScheduledFuture<?> scheduledFuture = genericWorkerPool.scheduleAtFixedRate(clusterTopologyRefreshScheduler,options.getRefreshPeriod().toNanos(), options.getRefreshPeriod().toNanos(), TimeUnit.NANOSECONDS);clusterTopologyRefreshFuture.set(scheduledFuture);}}}

刷新拓扑的逻辑源码

/*** Update the partition cache. Updates are necessary after the partition details have changed.*/public void updateCache() {synchronized (partitions) {if (partitions.isEmpty()) {this.slotCache = EMPTY;this.nodeReadView = Collections.emptyList();return;}RedisClusterNode[] slotCache = new RedisClusterNode[SlotHash.SLOT_COUNT];List<RedisClusterNode> readView = new ArrayList<>(partitions.size());for (RedisClusterNode partition : partitions) {readView.add(partition);for (Integer integer : partition.getSlots()) {slotCache[integer.intValue()] = partition;}}this.slotCache = slotCache;this.nodeReadView = Collections.unmodifiableCollection(readView);}}

spring boot redisConnectionFactory生成源码,里面预留了钩子,可以注入自定的逻辑。

 @Bean@ConditionalOnMissingBean(RedisConnectionFactory.class)public LettuceConnectionFactory redisConnectionFactory(ClientResources clientResources) throws UnknownHostException {LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(clientResources, this.properties.getLettuce().getPool());return createLettuceConnectionFactory(clientConfig);}// 最终执行到这private void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {// customizer 补充逻辑,刷新拓扑的设置就通过这里执行for (LettuceClientConfigurationBuilderCustomizer customizer : this.builderCustomizers) {customizer.customize(builder);}}

所以只要在预留的LettuceClientConfigurationBuilderCustomizer中追加刷新拓扑逻辑即可。

2、修改步骤

新增MyLettuceClientConfigurationBuilderCustomizer bean接口

package com.migu.clipcloud.core.cache.redis;import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.stereotype.Component;import java.time.Duration;/*** @Auther: csp* @Description: 处理Lettuce 集群拓扑刷新* @Date: Created in 2019/9/28 下午12:02* @Modified By:*/
@Component
public class MyLettuceClientConfigurationBuilderCustomizer implements LettuceClientConfigurationBuilderCustomizer {/*** Customize the {@link LettuceClientConfigurationBuilder}.** @param clientConfigurationBuilder the builder to customize*/@Override public void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigurationBuilder) {/*ClusterTopologyRefreshOptions配置用于开启自适应刷新和定时刷新。如自适应刷新不开启,Redis集群变更时将会导致连接异常!*/ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()//开启自适应刷新
//                .enableAdaptiveRefreshTrigger(ClusterTopologyRefreshOptions.RefreshTrigger.MOVED_REDIRECT, ClusterTopologyRefreshOptions.RefreshTrigger.PERSISTENT_RECONNECTS).enableAllAdaptiveRefreshTriggers().adaptiveRefreshTriggersTimeout(Duration.ofSeconds(10))//开启定时刷新,时间间隔根据实际情况修改.enablePeriodicRefresh(Duration.ofSeconds(15)).build();clientConfigurationBuilder.clientOptions(ClusterClientOptions.builder().topologyRefreshOptions(topologyRefreshOptions).build());}
}

至此,可以实现拓扑的自动刷新

微信扫一扫关注该公众号

spring redis cluster Lettuce 拓扑刷新相关推荐

  1. Redis Client Lettuce 5 GA发布

    经过13个月的开发阶段和208张已解决的故障单,我很高兴宣布Lettuce 5.0全面上市. 这是一个主要发行版,带有一些重大更改,新的有趣功能以及Java 9兼容性. 从Maven Central获 ...

  2. Redis Cluster深入与实践(续)

    前文回顾 上一篇文章基于redis的分布式锁实现写了基于redis实现的分布式锁.分布式环境下,不会还使用单点的redis,做到高可用和容灾,起码也是redis主从.redis的单线程工作,一台物理机 ...

  3. redis cluster中添加删除重分配节点例子

    redis cluster中添加删除重分配节点例子 作者:用户 来源:互联网 时间:2016-05-05 10:22:27 摘要: 本文讲的是redis cluster中添加删除重分配节点例子, re ...

  4. Spring Boot整合Redis缓存(Lettuce)

    spring-boot-demo-cache-redis 此 demo 主要演示了 Spring Boot 如何整合 redis,操作redis中的数据,并使用redis缓存数据.连接池使用 Lett ...

  5. Spring Boot Redis Cluster 实战干货

    转载自  Spring Boot Redis Cluster 实战干货 添加配置信息 spring.redis:database: 0 # Redis数据库索引(默认为0)#host: 192.168 ...

  6. 【故障演练】 Redis Cluster集群,当master宕机,主从切换,客户端报错 timed out

    大家好,我是Tom哥 性能不够,缓存来凑 一个高并发系统肯定少不了缓存的身影,为了保证缓存服务的高可用,我们通常采用 Redis Cluster 集群模式. 描述: 集群部署采用了 3主3从 拓扑结构 ...

  7. 高性能分布式缓存redis(持久化原理 安全策略 过期删除内存淘汰策略 性能压测 高可用 Redis Cluster)

    redis redis(持久化原理 安全策略 过期删除&内存淘汰策略 性能压测 高可用 Redis Cluster) 1. 持久化原理 1.1 持久化流程(落盘) 1.2 RDB详解 1.2. ...

  8. redis cluster 集群 HA 原理和实操(史上最全、面试必备)

    文章很长,建议收藏起来慢慢读!疯狂创客圈总目录 语雀版 | 总目录 码云版| 总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 经典图书:<Java高并发核心编程(卷1)> 面试必备 ...

  9. 响应式久草编程基础教程:久草Spring Boot 与 Lettuce 在线整合

    本文主要介绍响应式编程访问 Redis,以及 Spring Boot 与 Lettuce 的整合使用. Lettuce 是可扩展性线程安全的 Redis 客户端,用于同步.异步和响应式使用.如果多个线 ...

最新文章

  1. SAP S/4HANA现金管理之变
  2. python循环输入若干学生信息保存到字典、并按学号排序,Python实现按学生年龄排序的实际问题详解...
  3. 浅谈ajax中get与post的区别,以及ajax中的乱码问题的解决方法
  4. 今天开始学opnet14.5
  5. iDesktop点数据集构建DEM时三种插值方式的选择
  6. Windows学习总结(15)——Notepad++ 快捷键大全
  7. react native在static中使用this方法
  8. Codeforces Round #666 (Div. 2)D. Stoned Game(博弈问题)
  9. Linux系统U盘怎么格式化,u盘怎么格式化各系统教程
  10. IP变更导致fdfs文件上传服务不可用解决流程
  11. 在html5水平边距属性hspace,响应式网页设计(html5+css3+cms)教学课件作者李文奎第2章html基础.pptx...
  12. java使用itext导出pdf,图片、表格、背景图
  13. 高效率完成一次接入80个手游渠道SDK——游戏接入SDK服务端篇
  14. qq邮箱发件转发php,phpmailer 利用qq邮箱转发邮件的问题
  15. ASUS C302C Chromebook Windows声卡驱动
  16. VC++调节笔记本屏幕亮度(附源码)
  17. 摄像机(Camera)
  18. Java图片处理 - 创建工具类
  19. java代码json_JSON 解析(java代码)
  20. dz plugin.php,DZ支付积分充值插件 Discuz码支付免签约即时到账插件 Discuz手机支付插件...

热门文章

  1. Android二维码扫描之ZXing快速项目集成
  2. 贝克曼库尔特Beckman Coulter全自动血液生化分析仪AU5800双工通讯开发
  3. 学术论文翻译网址总结
  4. 消失的喀什老城 消失的大半新疆94
  5. 贵州二级计算机考试题库,2018贵州事业单位考试题库:事业单位模拟试卷试题及答案解析六...
  6. Oracle Applications DBA 基础(二)
  7. 开源源代码收集下载网站汇总
  8. Photoshop入门与进阶实例:4.2 封尘的杂志
  9. 操作系统OS-Lab1-100位大数除法NASM实现
  10. python 人形自动标注_自动设置人形生物