jetcache
jetcache copied to clipboard
com.alicp.jetcache.AbstractCache: jetcache(RedisLettuceCache) GET error. key=[XXX]
环境:
- JDK 8 U331
- Spring Boot 2.7.2
- JetCache 2.6.7
- RedisClient :Lettuce
问题:
首先该问题在单体应用或者一个独立服务中,没有发现。
如果是用微服务,两个服务同时使用同一个 Redis。第一个服务没有任何问题,第二个服务中,如果缓存中使用了和第一个服务相同的Key的缓存,那么就会出现这个问题。
com.alicp.jetcache.AbstractCache: jetcache(RedisLettuceCache) GET error. key=[XXX]
额外说明:
这个问题在 JetCache 2.6.7 版本下存在。同样的环境,清空缓存,使用 2.6.6 就没有这个问题。
尝试解决:
进行了代码跟踪,尝试看看能不能解决这个问题。奈何能力和精力有限,未能解决。但是通过跟踪代码发现,主要问题出现在以下代码中:
RedisLettuceCache
@Override
protected CacheGetResult<V> do_GET(K key) {
try {
byte[] newKey = buildKey(key);
RedisFuture<byte[]> future = stringAsyncCommands.get(newKey);
CacheGetResult<V> result = new CacheGetResult<>(future.handle((valueBytes, ex) -> {
if (ex != null) {
JetCacheExecutor.defaultExecutor().execute(() -> logError("GET", key, ex));
return new ResultData(ex);
} else {
try {
if (valueBytes != null) {
CacheValueHolder<V> holder = (CacheValueHolder<V>) valueDecoder.apply(valueBytes);
if (System.currentTimeMillis() >= holder.getExpireTime()) {
return new ResultData(CacheResultCode.EXPIRED, null, null);
} else {
return new ResultData(CacheResultCode.SUCCESS, null, holder);
}
} else {
return new ResultData(CacheResultCode.NOT_EXISTS, null, null);
}
} catch (Exception exception) {
logError("GET", key, ex);
return new ResultData(ex);
}
}
}));
setTimeout(result);
return result;
} catch (Exception ex) {
logError("GET", key, ex);
return new CacheGetResult(ex);
}
}
问题主要出在:valueDecoder.apply(valueBytes)。出问题时,valueBytes是负值,代码 AbstractValueDecoder 中,无法根据这个值转换后的identityNumber,找到对应的Decoder,导致反序列化失败。
DecoderMap 中是存有 KryoValueDecoder
和 SpringJavaValueDecoder
两个反序列化器的。不知道为什么生成的identityNumber 会变,导致找不到Decoder
@Override
public Object apply(byte[] buffer) {
try {
if (useIdentityNumber) {
DecoderMap.registerBuildInDecoder();
int identityNumber = parseHeader(buffer);
AbstractValueDecoder decoder = DecoderMap.getDecoder(identityNumber);
Objects.requireNonNull(decoder, "no decoder for identity number:" + identityNumber);
return decoder.doApply(buffer);
} else {
return doApply(buffer);
}
} catch (Exception e) {
throw new CacheEncodeException("decode error", e);
}
}
2.6.7和2.6.6只有一个区别,就是2.6.7把这个异常打出来了,你以前用2.6.6应该也有问题,只不过没发现。 应该是你的value序列化遇到了问题,可以看看有没有堆栈。
哦,好的。谢谢大佬指点,我再好好跟一下看看。
这两天一直在跟踪这个问题。
大多数情况,确实是因为远程存储的数据反序列化的问题导致Get抛出错误。
但是还存在 valueDecoder 找不到,导致的抛空。(注:下图是出现问题的cache,是通过 @CreateCache 创建)
创建代码如下:
@CreateCache(name = ProtectConstants.CACHE_NAME_TOKEN_SECURE_KEY, cacheType = CacheType.BOTH)
protected Cache<String, SecretKey> cache;
@areyouok
你这前面4个字节应该是一个升级了kryo5,同时jetcache版本小于2.6.7的程序写进去的。
kryo4和kryo5完全不兼容,并且kryo5改成了little endian,jetcache<=2.6.6使用kryo Output.writeInt()来写前面4个字节,会导致kryo4和kryo5写入的结果不一样,但是decoder的时候只能按一种方式解。所以jetcache 2.6.7改成了自己按big endian的方式来写着4个字节。
你这前面4个字节应该是一个升级了kryo5,同时jetcache版本小于2.6.7的程序写进去的。
kryo4和kryo5完全不兼容,并且kryo5改成了little endian,jetcache<=2.6.6使用kryo Output.writeInt()来写前面4个字节,会导致kryo4和kryo5写入的结果不一样,但是decoder的时候只能按一种方式解。所以jetcache 2.6.7改成了自己按big endian的方式来写着4个字节。
我用的2.7.3,缓存是新建的,并且只有一个服务使用这个缓存还是有这样的问题
scoreLiveCache = cacheManager.getOrCreateCache(
QuickConfig.newBuilder("scoreLiveCache.")
.cacheType(CacheType.REMOTE)
.build()