jetcache 自建Monitor使用Cache时, Kryo出现反序列化问题

cngwdvgl  于 2022-11-06  发布在  其他
关注(0)|答案(6)|浏览(500)

我使用Monitor实现了Cache-Back-Write的功能, 在使用的过程中, 发现又Monitor自循环触发的Event, 很大概率会产生 Kryo 的反序列化错误. 下面是我的错误.

01:43:17.641||[FORK-JOIN-COMMON-POOL-4-5] ERROR c.a.j.AbstractCache:52 - jetcache(TairCache) GET error. key=TppScoreToDBMonitor_MONITOR_MATERIAL_CACHE.
com.alicp.jetcache.support.CacheEncodeException: decode error
        at com.alicp.jetcache.support.AbstractValueDecoder.apply(AbstractValueDecoder.java:46)
        at com.alicp.jetcache.support.AbstractValueDecoder.apply(AbstractValueDecoder.java:11)
        at com.alicp.jetcache.tair.TairCache.do_GET(TairCache.java:63)
        at com.alicp.jetcache.AbstractCache.GET(AbstractCache.java:87)
        at com.alicp.jetcache.Cache.get(Cache.java:40)
        at com.alicp.jetcache.LoadingCache.get(LoadingCache.java:36)
        at com.alicp.jetcache.anno.field.LazyInitCache.get(LazyInitCache.java:106)
        at com.taobao.film.tfmind.module.core.BackWriteCacheCompose.lambda$appendActiveCacheKey$1(BackWriteCacheCompose.java:68)
        at com.alicp.jetcache.Cache.tryLockAndRun(Cache.java:272)
        at com.alicp.jetcache.SimpleProxyCache.tryLockAndRun(SimpleProxyCache.java:78)
        at com.alicp.jetcache.anno.field.LazyInitCache.tryLockAndRun(LazyInitCache.java:232)
        at com.taobao.film.tfmind.module.score.kits.JetCacheKit.retryTryLock(JetCacheKit.java:83)
        at com.taobao.film.tfmind.module.core.BackWriteCacheCompose.tryLock(BackWriteCacheCompose.java:41)
        at com.taobao.film.tfmind.module.core.BackWriteCacheCompose.appendActiveCacheKey(BackWriteCacheCompose.java:67)
        at com.taobao.film.tfmind.module.core.BackWriteCacheMonitor.lambda$afterOperation$0(BackWriteCacheMonitor.java:85)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:627)
        at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
        at java.lang.Thread.run(Thread.java:882)
Caused by: java.lang.IllegalArgumentException: classLoader cannot be null.
        at com.esotericsoftware.kryo.Kryo.setClassLoader(Kryo.java:1028)
        at com.alicp.jetcache.support.KryoValueDecoder.doApply(KryoValueDecoder.java:31)
        at com.alicp.jetcache.support.AbstractValueDecoder.apply(AbstractValueDecoder.java:41)
        at com.alicp.jetcache.support.AbstractValueDecoder.apply(AbstractValueDecoder.java:11)
        at com.alicp.jetcache.tair.TairCache.do_GET(TairCache.java:63)
        at com.alicp.jetcache.AbstractCache.GET(AbstractCache.java:87)
        at com.alicp.jetcache.Cache.get(Cache.java:40)
        at com.alicp.jetcache.LoadingCache.get(LoadingCache.java:36)
        at com.alicp.jetcache.anno.field.LazyInitCache.get(LazyInitCache.java:106)
        at com.taobao.film.tfmind.module.core.BackWriteCacheCompose.lambda$appendActiveCacheKey$1(BackWriteCacheCompose.java:68)
        at com.alicp.jetcache.Cache.tryLockAndRun(Cache.java:272)
        at com.alicp.jetcache.SimpleProxyCache.tryLockAndRun(SimpleProxyCache.java:78)
        at com.alicp.jetcache.anno.field.LazyInitCache.tryLockAndRun(LazyInitCache.java:232)
        at com.taobao.film.tfmind.module.score.kits.JetCacheKit.retryTryLock(JetCacheKit.java:83)
        at com.taobao.film.tfmind.module.core.BackWriteCacheCompose.tryLock(BackWriteCacheCompose.java:41)
        at com.taobao.film.tfmind.module.core.BackWriteCacheCompose.appendActiveCacheKey(BackWriteCacheCompose.java:67)
        at com.taobao.film.tfmind.module.core.BackWriteCacheMonitor.lambda$afterOperation$0(BackWriteCacheMonitor.java:85)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)

经过一番源码check, 发现问题出在:

com.alicp.jetcache.support.KryoValueDecoder.doApply(KryoValueDecoder.java:31)
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);
}

这是获取当前线程的Classloader失败导致的反序列化异常. (JNI自己的线程无法获得Classloader)

由于我无法修改源码, 所以我在Monitor的afterOperation中添加了下面的代码, 问题解决.

public void afterOperation(CacheEvent event) {
    if (!(event instanceof CachePutEvent)) {
        return;
    }

    // 只有在下游监控match的情况下, 才会进入
    if(!isMatch(event)) {
        return;
    }

    // XXX: 这里理论可以不使用线程池, 但为了隔离与ForJoin的线程还是用了.
    // XXX: KryoValueDecoder.java 31. (Thead.getClassLoader 在 JNI 线程中会返回 null, 导致setClassLoader报错)
    BACK_WRITE_CACHE_POOL.execute(() -> {
        if(Thread.currentThread().getContextClassLoader() == null) {
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            Thread.currentThread().setContextClassLoader(cl);
        }
        CachePutEvent e = (CachePutEvent) event;
        backWriteCacheCompose.appendActiveCacheKey(this.monitorActiveMaterialKey, e.getKey().toString());
    });
}

希望作者 @areyouok 在下个版本可以修复这个问题, 如果需要, 我也可以贡献github的代码.

6rvt4ljy

6rvt4ljy1#

怎么判断当前是Jni线程呢?

如果我没有理解错的话,这个错误的原因是没有判空。对于你来说,应该检查一下当前Thread上的ContextClassLoader为何是空。对于JetCache,在这里应该做一个判空兜底。

c3frrgcw

c3frrgcw2#

你好,我在使用的过程中也遇到了相同的问题,场景是Java使用parallelStream调用带有Cached注解的方法,请问贵团队有做修复的打算么,或者有查找原因的意愿么

r1zhe5dt

r1zhe5dt3#

我只能做个兜底,你应该检查一下为何当前线程的context class loader是空,如果可以的话,给它设置一个。

相关commit已经提交: 6c911d0

whitzsjs

whitzsjs4#

这取决于你用什么线程运行 monitor的 afterOperation @areyouok
这个应该是JetCache可以注意到的.

brjng4g3

brjng4g35#

你好,我在使用的过程中也遇到了相同的问题,场景是Java使用parallelStream调用带有Cached注解的方法,请问贵团队有做修复的打算么,或者有查找原因的意愿么

Java的parallelStream 场景, 使用的是ForkJoin的线程(是Java自建的), 所以你知道使用自己的线程池, 就能保证不出这个问题.

uwopmtnx

uwopmtnx6#

现在的这个commit能解决你的问吗

相关问题