热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

解决springdataredis的那些坑

这篇文章主要介绍了springdataredis的那些坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完

解决spring data redis的那些坑

spring data redis的那些坑

spring 的IOC很少有bug,AOPbug开始多起来,到了它的一些“玩具”一样的组件,bug无处不在。而且跟一般的开源框架不同,在github上你报告issue,会被“这不是一个bug”强行关闭。开一博文记录,给遇到同样问题而苦恼的人歇歇脚。

1. 使用lua脚本,返回类型解析错误

背景:一般来讲,就算脚本里没有return语句,redis也是会返回执行结果,看起来就像:{“Ok” = “ok”},或者{“ok”:”ok”}。然而对于一些操作redis没有返回,或者return语句后面返回一个值,spring包了的那一层壳就会出问题。影响的包:spring封装了jedis的所有版本,包括:spring-data-redis 2.0以下的所有版本,以及使用了jedis的2.0以上版本:

        
            org.springframework.boot
            spring-boot-starter-data-redis
            2.0.0.RELEASE
            
                
                    io.lettuce
                    lettuce-core
                
            
        

这种情况下就会遇到

XXX cannot be cast to XXX

原因:DefaultScriptExecutor.java类中:

    public  T execute(final RedisScript script, final RedisSerializer<&#63;> argsSerializer,
            final RedisSerializer resultSerializer, final List keys, final Object... args) {
        return template.execute((RedisCallback) connection -> {
            final ReturnType returnType = ReturnType.fromJavaType(script.getResultType()); // return type is wrong.
            final byte[][] keysAndArgs = keysAndArgs(argsSerializer, keys, args);
            final int keySize = keys != null &#63; keys.size() : 0;
            if (connection.isPipelined() || connection.isQueueing()) {
                // We could script load first and then do evalsha to ensure sha is present,
                // but this adds a sha1 to exec/closePipeline results. Instead, just eval
                connection.eval(scriptBytes(script), returnType, keySize, keysAndArgs);
                return null;
            }
            return eval(connection, script, returnType, keySize, keysAndArgs, resultSerializer);
        });
    }

而作为消费者,一般会将返回值设置为Object,因为同一个脚本里有若干的逻辑,不同情况下返回值可能是布尔型,字符串型,Number型等。

    ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("META-INF/scripts/redis.lua"));
    DefaultRedisScript redisScript = new DefaultRedisScript();
    redisScript.setScriptSource(scriptSource);
    redisScript.setResultType(Object.class);

而DefaultScriptExecutor的execute方法,会把Object类型解析为List类型,进而设置returnType为Multi。

public Object convert(Object result) {
        if (result instanceof String) {
            // evalsha converts byte[] to String. Convert back for consistency
            return SafeEncoder.encode((String) result);
        }
        if (returnType == ReturnType.STATUS) {
            return JedisConverters.toString((byte[]) result);
        }
        if (returnType == ReturnType.BOOLEAN) {
            // Lua false comes back as a null bulk reply
            if (result == null) {
                return Boolean.FALSE;
            }
            return ((Long) result == 1);
        }
        if (returnType == ReturnType.MULTI) {
            List resultList = (List) result;
            List cOnvertedResults= new ArrayList<>();
            for (Object res : resultList) {
                if (res instanceof String) {
                    // evalsha converts byte[] to String. Convert back for
                    // consistency
                    convertedResults.add(SafeEncoder.encode((String) res));
                } else {
                    convertedResults.add(res);
                }
            }
            return convertedResults;
        }
        return result;
    }

会因为result(原本只是一个Object),被解析为List,转换出了问题。此外,这里居然没有设置null的转换,难道null就不是List了。。。好在spring redis基于lettuce的实现不存在这个问题。

2. spring redis基于lettuce配置Client必须显示调用

从官方的reference看,spring的lettuce的配置只需要简单使用一个包含host、port、database、password等链接必须信息构造的RedisStandaloneConfiguration对象作为参数传递给LettuceConnectionFactory 的构造函数,同理连接池,然而实际使用中发现,ConnectionFactory用于建立连接的是从它的client属性获取的服务器地址等,因此必须调用afterPropertiesSet方法。

现在client信息有了,可以连接,但是连接池又未开启,尽管已经在构造器参数中指定过。受限于时间,还没有调这个点。

    LettucePoolingClientConfiguration poolingClientCOnfiguration= LettucePoolingClientConfiguration.builder()
        .poolConfig(new GenericObjectPoolConfig())
        .build();

    RedisStandaloneConfiguration redisStandalOneConfiguration= new RedisStandaloneConfiguration(
        redisProperty.getHost(),redisProperty.getPort()
    );
    redisStandaloneConfiguration.setDatabase(redisProperty.getDatabase());

    LettuceConnectionFactory cf = new LettuceConnectionFactory(redisStandaloneConfiguration, poolingClientConfiguration);
    cf.afterPropertiesSet(); // must
    StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
    stringRedisTemplate.setConnectionFactory(cf);
    setSerializer(stringRedisTemplate);

spring data redis 的优缺点

spring-data-redis是由spring的 cache api 整合 redis 而来,它的命名规则由spring cache 的规则来定义key和对key的管理,进一步弱化redis的API。

事实上redis提供的功能已经足够强大,并且可以直接使用,同时支持灵活的分库。

spring 的 cache 功能主要由 @Cacheable @CacheEvict @CachePut 实现

  • @Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
  • @CachePut 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
  • @CachEvict 主要针对方法配置,能够根据一定的条件对缓存进行清空

默认情况下Spring使用CacheManagerBean 来实现,其实现有3种:EHCache,Redis,ConcurrentHashMap,默认的ConcurrentHashMap 是没有过期的。

Redis 的使用也是要自己手动调 expire ,所以暂时使用原生的 jedis ,直接调用 redis 的api

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程笔记。


推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • Java程序设计第4周学习总结及注释应用的开发笔记
    本文由编程笔记#小编为大家整理,主要介绍了201521123087《Java程序设计》第4周学习总结相关的知识,包括注释的应用和使用类的注释与方法的注释进行注释的方法,并在Eclipse中查看。摘要内容大约为150字,提供了一定的参考价值。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • centos安装Mysql的方法及步骤详解
    本文介绍了centos安装Mysql的两种方式:rpm方式和绿色方式安装,详细介绍了安装所需的软件包以及安装过程中的注意事项,包括检查是否安装成功的方法。通过本文,读者可以了解到在centos系统上如何正确安装Mysql。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 本文介绍了关系型数据库和NoSQL数据库的概念和特点,列举了主流的关系型数据库和NoSQL数据库,同时描述了它们在新闻、电商抢购信息和微博热点信息等场景中的应用。此外,还提供了MySQL配置文件的相关内容。 ... [详细]
author-avatar
qtl4431541
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有