RedissonClient 做Map的累加处理的一些特性的补正

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的KeyValue的类型即可。

测试代码

@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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值