当前位置: 首页 > news >正文

redis主从哨兵模式+Lua报错-READONLY You can‘t write against a read

背景

项目试用SpringBoot+redisTemplate执行redis的lua脚本,实现令牌桶;redis结构使用的是1主2从3哨兵模式+读写分离;

问题分析

READONLY You can't write against a read报这个错的含义在从节点执行了写操作,也就是说我执行Lua脚本是在从节点上执行的,那么问题来了,为什么的我LUA脚本会在从节点执行呢?
我们都知道,redis的主从哨兵模式,再加上配置读写分离,会将读操作优先分配到从节点,也就是说它认为LUA脚本是读操作,看一下具体的报错信息:
在这里插入图片描述
1、我们从这一行开始看起,找找是哪一步给我分配到了从节点

at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:77)

之后一步一步点击调用,到下面这一步,调用了get方法获取前面的返回值,那就继续看前面是怎么调用的,点击RedisScriptingAsyncCommands::evalsha方法继续向下寻找
在这里插入图片描述
2、再继续
在这里插入图片描述
3、再继续,可以看到,命令类型给了一个 EVALSHA
在这里插入图片描述
4、回到第二部看 dispatch 方法的调用,找到处理链接的地方
在这里插入图片描述
5、可以看到write方法中,有一个步骤判断操作类型是读还是写
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
6、经过查找,CommandType类中包含 EVALSHA 类型,所以判断为ReadOlny,由此可以验证,确实是使用了从节点执行lua脚本。

解决问题

既然它使用的是从节点,那我就想办法让他在执行脚本的时候,强制选择主节点就能解决问题了呀。
修改redisTemplate注入配置

@Configuration
public class MyRedisConfig {
    @Value("${spring.redis.sentinel.master}")
    private String masterName;

    @Value("${spring.redis.sentinel.nodes}")
    private String sentinelNodes;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.database}")
    private Integer database;

    @Bean(value = "qpsRedisTemplate")
    public RedisTemplate qpsRedisTemplate() {
        List<String> sentinels = Arrays.asList(sentinelNodes.split(","));
        RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
        sentinelConfig.master(masterName);
        Set<RedisNode> sentinelNodes = new HashSet<>();
        for (String sentinel : sentinels) {
            String[] split = sentinel.split(":");
            sentinelNodes.add(new RedisNode(split[0],Integer.parseInt(split[1])));
        }
        sentinelConfig.setSentinels(sentinelNodes);
        sentinelConfig.setDatabase(database);
        sentinelConfig.setPassword(password);
        LettuceConnectionFactory factory = new LettuceConnectionFactory(sentinelConfig);
        factory.afterPropertiesSet();
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }
    //读写分离配置
    @Bean
    public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
        return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
    }
}

相关文章:

  • Java之异常体系
  • Java Set实现类面试题
  • IP----访问服务器流程
  • 解密 Token:大模型如何解析中英文文本
  • 基于 Python 和 Django 的文本情感分析系统设计与实现
  • 音视频容器格式
  • 智能证件照处理器(深度学习)
  • IRI 2016 模型在线版 MATLAB
  • JMeter
  • Java IO 流:从入门到实践
  • 基于javaweb的SpringBoot社区维修平台设计和实现(源码+文档+部署讲解)
  • 如何用Python开发一款可以标注课标单词的工具
  • 【Windows 同时安装 MySQL5 和 MySQL8 - 详细图文教程】
  • 全面汇总windows进程通信(二)
  • 【DeepSeek与鸿蒙HarmonyOS:开启应用开发新次元】
  • 机械行业金属材料重量计算器
  • CSS实现一张简易的贺卡
  • MySQL基本查询——表的增删查改
  • python-leetcode-N 皇后
  • 【Python爬虫(61)】Python金融数据挖掘之旅:从爬取到预测
  • 哪些网站教你做美食的/网络营销方式有几种
  • 网站免费推广大全/seo运营培训
  • 网站建设 好的公司/品牌营销策划与管理
  • 张家界做网站找谁/公司网络推广方案
  • 温州网站建设/百度推广费
  • 保定哪个公司做网站好/网络软文案例