解决使用fst反序列化造成空指针异常问题

由于最近在做商城秒杀这块的业务,因此使用到了redis中提供的incrBy 自增方法和decrBy自减方法。但是今天在项目中使用发现当我们使用到fst或者jdk的序列化与反序列化时,如果我们使用上面两个api 往redis 里面存数据是没有任何问题的。但是如果直接调用redis api中的get方法去取值的时候,系统抛出空指针异常了。下面我们来看代码测试

public static void main(String[] args) {
    try {
        RedisPlugin redisPlugin = new RedisPlugin("MAIN", "127.0.0.1", 6379, "123456");
        FstSerializer fstSerializer = new FstSerializer();
        redisPlugin.setSerializer(fstSerializer);
        redisPlugin.start();
        Redis.use("MAIN").incrBy("test1223", 1);
        byte[] bytes = Redis.use("MAIN").getJedis().get(fstSerializer.keyToBytes("test1223"));
        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);

        System.out.println("fastJson解析" + fastJsonRedisSerializer.deserialize(bytes)); // 使用fastJson反序列化
        System.out.println("fst解析" + fstSerializer.valueFromBytes(bytes));// 使用fst反序列化
        System.out.println("JdkSerializer解析" + new JdkSerializer().valueFromBytes(bytes));// 使用Jdk反序列化

    } catch (Exception e) {
        e.printStackTrace();
    }
}

下面是运行结果

1591924203(1).png

可以看到只有使用fastJson可以反序列化成功。最后我经过阅读源码发现,当我们调用incrBy 自增方法和decrBy自减方法这两个api 时,存储的value不会经过fst进行序列化,而是直接将数值存储到redis。而当我们调用get 方法取值的时候首先取出来的是byte数组,然后在经过fst进行反序列化。而这个时候由于value 没有经过fst进行序列化存储,所以使用fst无法经byte数组解析成Integer类型,因此导致了空指针异常。这可能是fst框架存在的一个小问题。在jfinal 的官方文档中发现波总另外写了一个方法getCounter方法去获取redis中计数器的值。在这里我在分享一个方法,欢迎大家在下方留言

创建一个类FstJsonSerializer,此类继承FstSerializer

public class FstJsonSerializer extends FstSerializer {

    // 使用FastJsonRedisSerializer 代理
    private final FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);

    /**
     * 重写valueFromBytes
     * @param bytes
     * @return
     */
    @Override
    public Object valueFromBytes(byte[] bytes) {
        try {
            return super.valueFromBytes(bytes);
        } catch (Exception e) {
            try {
                LogKit.warn(e.getMessage(), e);
                return fastJsonRedisSerializer.deserialize(bytes);
            } catch (Exception exception) {
                throw new RuntimeException(exception);
            }
        }
    }
}

解决的思路就是如果Fst反序列化异常,就是用FastJson反序列化,如果还不能成功的话则抛出异常。通过使用以上代码,我们就可以调用get方法直接获取redis中计数器的值了。

评论区

zzutligang

2020-06-15 09:07

文档里有红色标注:
注意:使用 incr、incrBy、decr、decrBy 方法操作的计数器,需要使用 getCounter(key) 进行读取而不能使用 get(key),否则会抛反序列化异常。同理:incrBy(key, value) 操作不能使用 set(key, value) 。