RedissonClient 做Map的累加处理的一些特性的补正
之前的文章RedissonClient 和 RedisTemplate 做Map的累加处理的一些特性 在用Redisson
做 Map的累加处理时是存在一些问题的。问题表现为,累加操作本身没问题,根据Key去读取累加值也没问题,但无法像常规的Map方式取遍历读取。因此重新水一篇来解决掉这个问题。
问题重现
测试代码
@Test
public void testIncrementWithRedisson2_3(){
String key = "testIncrementWithRedisson2_3";
String hashKey = "A1";
IntStream.range(1,101).forEach(r->{
System.out.println(incrementWithRedisson2(key,hashKey));
});
//强制指定本Map的Codec 为LongCodec
RMap<String,Long> accumulatorMap = redissonClient.getMap(key,LongCodec.INSTANCE);
//下面这行代码不会抛出异常
Long value = accumulatorMap.get(hashKey);
System.out.println(String.format("累加的结果%d",value));
//下面这些代码都会抛出异常 java.lang.NumberFormatException: For input string: "A1"
accumulatorMap.readAllKeySet();
// accumulatorMap.keySet().forEach(k->{
// System.out.println(k+"-"+accumulatorMap.get(k));
// });
// accumulatorMap.forEach((mapKey,mapValue)->{
// System.out.println(mapKey+"-"+mapValue);
// });
// Map<String,Long> allMap = accumulatorMap.readAllMap();
}
private long incrementWithRedisson2(String key,String hashKey){
//强制指定本Map的Codec 为LongCodec
RMap<String,Long> accumulatorMap = redissonClient.getMap(key, LongCodec.INSTANCE);
return accumulatorMap.addAndGet(hashKey,1L);
}
测试结果
...
累加的结果200
reply: ReplayingDecoderByteBuf(ridx=12, widx=12), command: (HKEYS), promise: java.util.concurrent.CompletableFuture@6139b414[Not completed, 1 dependents], params: [testIncrementWithRedisson2_3]
java.lang.NumberFormatException: For input string: "A1"
分析
原因是因为LongCodec
这个编码器继承了StringCodec
, 但只覆盖了Decoder
方法,因此写入的时候可以正常将字符串正确编码写入Redis,但读取的时候,字符串就无法反向解码为Long
值了
LongCodec
的相关代码
private final Decoder<Object> decoder = new Decoder<Object>() {
@Override
public Object decode(ByteBuf buf, State state) throws IOException {
String str = (String) LongCodec.super.getValueDecoder().decode(buf, state);
return Long.valueOf(str);
}
};
StringCodec
代码
private final Encoder encoder = new Encoder() {
@Override
public ByteBuf encode(Object in) throws IOException {
ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
out.writeCharSequence(in.toString(), charset);
return out;
}
};
private final Decoder<Object> decoder = new Decoder<Object>() {
@Override
public Object decode(ByteBuf buf, State state) {
String str = buf.toString(charset);
buf.readerIndex(buf.readableBytes());
return str;
}
};
但是在这里仍有一个疑问,上面的测试代码中的Long value = accumulatorMap.get(hashKey);
为啥不会抛出异常呢?
原因是org.redisson.RedissonMap#get
方法对于参数Key
的并不是调用Decoder
进行解码处理,而是调用Encoder
进行编码处理,所以不会抛出异常。
public RFuture<V> getOperationAsync(K key) {
String name = getRawName(key);
return commandExecutor.readAsync(name, codec, RedisCommands.HGET, name, encodeMapKey(key));
}
其中encodeMapKey(key)
就是调用Encoder
对参数Key
进行编码处理。
问题解决方案
使用TypedJsonJacksonCodec
这个编码器就可以了。构建一个TypedJsonJacksonCodec
编码器,指定Map的Key
和Value
的类型即可。
测试代码
@Test
public void testIncrementWithRedisson2_4(){
String key = "testIncrementWithRedisson2_4";
String hashKey = "A1";
List<CompletableFuture> cfList = IntStream.range(1,201).mapToObj(r->{
return CompletableFuture.runAsync(()->
incrementWithRedisson3(key,hashKey)
,testExecutorService)
.exceptionally(ex->{
ex.printStackTrace();
return null;
});
}).collect(Collectors.toList());
// 等待所有线程结束
CompletableFuture.allOf(cfList.toArray(new CompletableFuture[cfList.size()])).whenComplete((k,e)->{
//所有线程都结束后,取出累加的值
//强制指定本Map的Codec 为jacksonCodec
TypedJsonJacksonCodec jacksonCodec = new TypedJsonJacksonCodec(
new TypeReference<String>() {},
new TypeReference<Long>() {}
);
RMap<String,Long> accumulatorMap = redissonClient.getMap(key,jacksonCodec);
Long value = accumulatorMap.get(hashKey);
System.out.println(String.format("累加的结果%d",value));
accumulatorMap.forEach((mapKey,mapValue)->{
System.out.println(mapKey+"-"+mapValue);
});
}).join();
}
private long incrementWithRedisson3(String key,String hashKey){
//强制指定本Map的Codec 为TypedJsonJacksonCodec
TypedJsonJacksonCodec jacksonCodec = new TypedJsonJacksonCodec(
new TypeReference<String>() {},
new TypeReference<Long>() {}
);
RMap<String,Long> accumulatorMap = redissonClient.getMap(key, jacksonCodec);
return accumulatorMap.addAndGet(hashKey,1L);
}
测试结果
redis的值:
控制台输出:
累加的结果200
A1-200