您的位置:首页 > 数据库 > Redis

spring-data-redis使用jdk序列化时increment的异常

2017-10-11 11:11 676 查看
no set 直接使用increment后

get时抛出异常

org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed
to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.EOFException
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:42)
at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:274)
at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:52)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:185)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:153)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:86)
at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:43)
at com.duotin.redis.RedisClient.get(RedisClient.java:137)
at com.duotin.RedisClientTest.testIncr(RedisClientTest.java:73)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?;
nested exception is java.io.EOFException
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:61)
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:1)
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:40)
... 34 more
Caused by: java.io.EOFException
at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2325)
at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2794)
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:801)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
at org.springframework.core.serializer.DefaultDeserializer.deserialize(DefaultDeserializer.java:38)
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:58)

... 36 more

在spring中redis的string 类型,当值采用JdkSerializationRedisSerializer序列化操作后,使用get获取失败。这是redis的一个bug.有两种解决办法。第一,采用StringRedisSerializer序列化其值。第二,采用boundValueOps(key).get(0,-1)获取计数key的值
http://blog.csdn.net/wozaiangongke/article/details/50831580
最近在开发中,使用Redis来实现数据点击量的统计存储功能。为什么使用Redis?点击量之类的功能,需要频繁触发更新操作,而且高并发访问时,还需要考虑操作冲突导致数据不一致的问题。而Redis是内存型存储,相比关系型数据库,操作更快,避免了频繁的文件写操作。更重要的是,Redis中有个INCR和INCRBY命令,都可以实现值递增的原子性操作,方便了解决了高并发时的冲突问题。

    Redis手册中的命令说明很详尽,还有Redis中文命令参考的网站可供使用,在此感谢无私的翻译人员。如下:

Redis中文参考手册

    sdr中针对redis的命令,一一提供了对应的方法可供使用,结合redis命令参考sdr的api,很容易上手。但唯一不足地是,sdr提供的api太简略了,只提供了函数和参数,参数的说明、返回值的说明、异常情况的说明统统没有,这个只能自己在实践的过程中用代码来认知了。

    sdr中提供了一个ValueOperations的接口来针对Redis中的Key-Value的命令操作。通过粗略追踪源码的手段,可以大概了解到,sdr框架操作Redis的实质,是和Redis服务通信,告知Redis服务执行指定的命令。ValueOperations中有increment方法,本质上是向Redis服务发送的INCRBY命令。当然要实现点击量递增的功能,需要使用ValueOperations的increment方法。

    Redis采用Key-Value的格式实现了多种结构的数据,例如List、Set等。但Redis中的Key和Value存储的都是字符串,而Java是面向对象的语言,大部分情况下操作数据也都是对象,那么Java与Redis的结合手段自然是序列化了。sdr提供了丰富的序列化策略,所以在配置sdr时,可以显示地指定Key和Value的序列化器,如下代码所示。这部分不了解的同志可以搜索相关的内容去了解,这里不再做过多说明。

[xml] view
plain copy

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">         <property name="connectionFactory" ref="jedisConnectionFactory" />         <property name="keySerializer">             <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />         </property>     </bean>  

    如果没有显示指定Key或Value的序列化器,默认采用JdkSerializationRedisSerializer。StringRedisSerializer将数据存储为正常的字符串,而且JdkSerializationRedisSerializer则是将数据存储为一串序列字符串,还需通过反序列化才能得到正常的字符串。

    当在使用ValueOperations之类的接口时,值得注意的是,接口是使用泛型的,如ValueOperations<K,V>,起初实现点击量递增,自然选用了ValueOperations<String,Long>的方式,点击量嘛,当然是整数喽。可就在调用increment方法时出现问题了,提示“ERR value is not an integer or out of range”。Redis手册中已经给出了很明确的说明,如下:



    在递增之前,由于使用ValueOperations的set方法为点击量设置了初始值,Key的序列化器采用的是JdkSerializationRedisSerializer,其将初始值变成了序列化字符串存入了Redis,而Redis执行INCRBY命令时是无法识别序列化字符串为整数的,所以会提示错误。

    有人提出一个很奇怪的现象,如果不使用set方法设置点击量的初始值,直接调用increment方法,Redis中存入的是正常的数字字符串,没有被序列化!这个现象仔细想想,跟sdr没有半毛钱关系的,更扯不到序列化。看上图中的一句说明,“如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作”。在点击量的Key不存在时,直接调用increment方法,递增量的初始值是由Redis生成的,根本没有走sdr的序列化策略,又何来序列化。

    那说了这么多,点击量的递增到底如何来解决呢?我得出的结论是,在使用ValueOperations的increment功能时,设置Value的序列化器为StringRedisSerializer,这样存入Redis的值即为可识别的数字字符串了。可以在配置文件中配置,也可以在代码中动态的指定Value的序列化器,下面贴出配置文件中的配置方式:

[xml] view
plain copy

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"           p:connectionFactory-ref="jedisConnectionFactory">         <property name="keySerializer">             <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />         </property>         <property name="valueSerializer">             <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />         </property>     </bean>  

参考链接:
http://docs.spring.io/spring-data/redis/docs/1.4.0.M1/reference http://shift-alt-ctrl.iteye.com/blog/1887370 http://blog.csdn.net/jackgaogaogao/article/details/50390274
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: