redis的hgetCounter获取不到值

  1. 1、设置源码:

public Long hincrBy(Object key, Object field, long value) {
   Jedis jedis = getJedis();
   try {
      return jedis.hincrBy(keyToBytes(key), fieldToBytes(field), value);
   }
   finally {close(jedis);}
}

设置时,field使用的是:fieldToBytes(field);


  1. 2、获取源码:

public Long hgetCounter(Object key, Object field) {
   Jedis jedis = getJedis();
   try {
      String ret = jedis.hget(keyNamingPolicy.getKeyName(key), keyNamingPolicy.getKeyName(field));
      return ret != null ? Long.parseLong(ret) : null;
   }
   finally {close(jedis);}
}

获取时,field使用的是keyNamingPolicy.getKeyName(field);

由于field字段错误,导致拿不到值;


  1. 3、问题分析,查看FST源码得知:

fieldToBytes(field)使用了valueToBytes(field);

valueToBytes(field)源码:

public byte[] valueToBytes(Object value) {
   FSTObjectOutput fstOut = null;
   try {
      ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
      fstOut = new FSTObjectOutput(bytesOut);
      fstOut.writeObject(value);
      fstOut.flush();
      return bytesOut.toByteArray();
   }
   catch (Exception e) {
      throw new RuntimeException(e);
   }
   finally {
      if(fstOut != null)
         try {fstOut.close();} catch (IOException e) {LogKit.error(e.getMessage(), e);}
   }
}

查看keyNamingPolicy.getKeyName(field):

public String getKeyName(Object key) {
   return key.toString();
}

查看Jedis.hget()源码:

public String hget(final String key, final String field) {
  checkIsInMultiOrPipeline();
  client.hget(key, field);
  return client.getBulkReply();
}

查看Client.hget()源码:

public void hget(final String key, final String field) {
  hget(SafeEncoder.encode(key), SafeEncoder.encode(field));
}

综上,得知field字段由于设置和获取不一致,导致获取不到值


  1. 4、总结:

设置field时用的是FstSerializer.valueToBytes(field);

获取field时用的是SafeEncoder.encode(field);


  1. 5、临时解决办法(不设置ISerializer和IKeyNamingPolicy的前提下):

private static Long hgetCounter(Object key, Object field) {
    Jedis jedis = cache.getJedis();
    try {
        byte[] hget = jedis.hget(FstSerializer.me.keyToBytes(key.toString()), FstSerializer.me.fieldToBytes(field.toString()));
        if (ArrayUtil.isEmpty(hget)) {
            return null;
        }
        String encode = SafeEncoder.encode(hget);
        return encode != null ? Long.parseLong(encode) : null;
    } finally {
        cache.close(jedis);
    }
}


@JFinal 望尽快修复


-----------------------------------------------------2020-07-07.分割线--------------------------------------------------------


现有hgetCounter:

image.png

@JFinal 波总可以试试

评论区

JFinal

2020-07-10 21:14

刚刚已经改进了这里:
https://gitee.com/jfinal/jfinal/commit/900b4ac402bcdc1e2fb3c9bec08a9c9e6493689f

用下面的方法用上最新版本:
https://jfinal.com/share/2093

然后尽快给我反馈,这样可以确保线上的版本是没问题的

除了你反馈的 hgetCounter 以外,顺便改进了 getCounter 这个方法,希望你也帮忙试一试

錢勢惘導

2020-07-13 18:51

@JFinal 改成这样不就和hget一样了吗?会报错的,FstSerializer.valueFromBytes(byte[] bytes)会报错的

錢勢惘導

2020-07-13 19:05

@JFinal hincrBy的时候value没有使用FstSerializer.valueToBytes(byte[] bytes),拿的时候使用FstSerializer.valueFromBytes(byte[] bytes),会报错的,incrBy同理

JFinal

2020-07-14 17:24

@錢勢惘導 原先是与 hget 不一样,现在按你的需求改掉了,你反而又不希望这么来用,不知道你的需求到底是什么

錢勢惘導

2020-07-25 16:41

@JFinal 我写的临时解决办法用的是jedis的hget,不是用的cache的hget。您看下cache的hincrBy和hgetCounter的源码,您修改后hincrBy的时候value没有使用FstSerializer.valueToBytes(byte[] bytes),而hgetCounter拿的时候value使用了FstSerializer.valueFromBytes(byte[] bytes),存取都不统一,肯定会有问题的

JFinal

2020-07-25 17:54

@錢勢惘導 我看了一下,改成 byte[] 模式以后没有满足你的需求,由于你的回复太晚了,为了保险起见改回原来的老版本了,因为昨天要发布新版本 4.9.01

其实 jfinal 的 redis plugin 是可以通过继承扩展的,大致如下:
public class MyCache extends Cache {
public Long hgetCounter(Object key, Object field) {
// 在此覆盖掉父类中的 hgetCounter 方法,实现自己想要的功能
}

然后配置一下:
Redis.addCache(new MyCache());

在使用的时候就能用上自己的实现类了,建议你通过这种方式来扩展使用

錢勢惘導

2020-07-27 11:45

@JFinal 好的,get到新知识了

錢勢惘導

2020-07-27 11:56

@JFinal
public Long hgetCounter(Object key, Object field) {
Jedis jedis = getJedis();
try {
byte[] hget = jedis.hget(keyToBytes(keyNamingPolicy.getKeyName(key)), fieldToBytes(keyNamingPolicy.getKeyName(field)));
if (ArrayUtil.isEmpty(hget)) {
return null;
}
String ret = SafeEncoder.encode(hget);
return ret != null ? Long.parseLong(ret) : null;
} finally {
close(jedis);
}
}

錢勢惘導

2020-07-27 11:57

@JFinal hgetCounter应该改成这样才行,getCounter是没有问题的

JFinal

2020-07-27 14:25

@錢勢惘導 jfinal 几乎所有地方都留有扩展方式,当需求得不到满足时,优先扩展例如 Db 中的所有方法也是可以扩展的,能过继承 DbPro 然后配置 me.setDbProFactory(...);

錢勢惘導

2020-07-27 14:47

@JFinal 新加了测试结果,波总可以试试看hgetCounter是否能获取到值

JFinal

2020-07-28 22:45

@錢勢惘導 这个问题我加入了备忘录,你的这段代码在你那工作是没问题, 我是担心换个场景可能有问题

因为我现在不确定你 hgetCounter 出来的值是否是通过 hincrBy(...) 方法设置的,因为 hgetCounter 是针对 hincrBy(...) 来做的

我担心你 hgetCounter 获取的是 redis 控制台命令 hincrby 设置的,也担心是 hset(...) 方法设置的

所以,这些我都要完全确定好才能改代码

錢勢惘導

2020-08-04 09:11

热门反馈

扫码入社