使用Jetcache过程的bug之Buffer underflow

业务场景

- 使用Jetcache+springCloud相关组件

- 线上A服务缓存用户信息到jetcache中,但是其他服务读取不到缓存,本地可以正常获取。

猜测

- 怀疑是key错误,但是代码没更改过,排除

- 缓存失败,通过第三方查看缓存工具,确认有改缓存,排除

- Jetcache配置问题,最近没有更改过配置,排除

最后排查好久,连pom引入的依赖都一个一个排除,还是没有任何结果,简直是崩溃。后面破罐子破摔将其他服务重新打包更新,突然发现可以访问到缓存。

跟踪

- 有了希望,就跟踪Jetcache的Get方法。

1.终于发现了JetCache在get时抛异常时,会直接返回null,而抛出的异常是【Buffer underflow】,有点疑问继续跟踪下去。

default V get(K key) throws CacheInvokeException {
    CacheGetResult result = GET(key);
    if (result.isSuccess()) {
        return result.getValue();
    } else {
        return null;
    }
}

2.在RedisCache实现类,取值解码。valueDecoder是一个值解码器,现在配置是KyroValueDecoder

CacheValueHolder holder = (CacheValueHolder) valueDecoder.apply(bytes);

3.跟踪到【KyroValueDecoder】的doApply方法。

@Override
public Object doApply(byte[] buffer) {
    ByteArrayInputStream in;
    if (useIdentityNumber) {
        in = new ByteArrayInputStream(buffer, 4, buffer.length - 4);
    } else {
        in = new ByteArrayInputStream(buffer);
    }
    Input input = new Input(in);
    Kryo kryo = (Kryo) KryoValueEncoder.kryoThreadLocal.get()[0];
    kryo.setClassLoader(Thread.currentThread().getContextClassLoader());
    return kryo.readClassAndObject(input);
}

4.会返回解析数据类型的【readClassAndObject】,最后跟踪到Input的require(1)方法报错了。

protected int require (int required) throws KryoException

 

错误原因

发现是因为A和B服务Core包更新不同步,导致A服务存的某个实体字段和B服务取的字段类型不一致,存是String类型,取是Int类型。

 

原理

最后发现是在解析数据时,会先去Buffer请求分配空间,如果已经被读完就会报 【buffer underflow】异常。发现是Int类型是4字节,32位。String是根据当前值定义长度的。比如当前存的是1,int是4字节,32位。去读取String类型,1字节,8位。就会出现这种缓存区下溢的情况了。下面是具体代码。

主要报错方法

/** Fills the buffer with more bytes. Can be overridden to fill the bytes from a source other than the InputStream.
 * @return -1 if there are no more bytes. */
protected int fill (byte[] buffer, int offset, int count) throws KryoException {
   if (inputStream == null) return -1;
   try {
      return inputStream.read(buffer, offset, count);
   } catch (IOException ex) {
      throw new KryoException(ex);
   }
}

Buffer underflow 定义

- 在计算中,缓冲区欠载或缓冲器下溢是当用于在两个设备或进程之间通信的缓冲器以低于从其读取数据的速度提供数据时发生的状态。这需要从缓冲区读取的程序或设备在缓冲区重新填充时暂停其处理。

心得体会

- 出现不明白的问题,跟踪下源码,多考虑下细节问题即可

你可能感兴趣的:(java服务端开发,bug)