2018-09-12 16:16:44 WARN [New I/O worker #1] SerializerFactory.java:652 getDeserializer - Hessian/Burlap: 'com.xxx.xxxBolt$1' is an unknown class in sun.misc.Launcher$AppClassLoader@a2c6f70
今天下午4点过商品组同事反馈,线上他们那边有个dubbo服务A(provider)打印了很多WARN日志让看看。
这个日志是在dubbo的hessian-lite模块下SerializerFactory类,Deserializer getDeserializer(String type)方法中打印的:
看名称大概是获取反序列化类。在服务A中接口方法是能正常调用的,不影响业务。但是该方法调用很频繁,大量这样的WARN日志显然不好。
其实这个问题之前遇到过,当时是dubbo的provider方接口有升级,方法入参vo有变动,consumer端依赖了provider放的api interface包,没有及时重新构建发布。
不过看这次日志打印很奇怪,'com.xxx.XxxBolt$1' is an unknown class,XxxBolt是我们这边一个storm拓扑(比如拓扑T)中的一个bolt名称,在该bolt中调用了服务A的一个接口;
服务A依赖了包B,B下面有个枚举类C;
该接口方法签名为 Map<枚举类C, Object> 方法名(Long xxId, Set<枚举类C> xxSet);
检查XxxBolt中调接口的代码,
调用如下:
Long xxId = ...; Set<类B, Object> xxSet = new HashSet{{ add(枚举C.枚举1,xxx); add(枚举C.枚举2,xxx); ... }} Map<枚举类C, Object> result = 方法名(Long xxId, Set<枚举类C> xxSet);
看代码中并没有把XxxBolt作为参数传递,而且服务A没有依赖storm拓扑的项目,怎么日志里会打印出XxxBolt的类名呢?
这个问题先放着,我们先回想了一下:服务A上周有过改动构建发布,storm的拓扑T的XxxBolt也有改动并发布,不过可以确定的是这段调用代码,包括请求参数,是没有改动的;
刚才提到,服务A依赖了包B,包B也有改动;那么拓扑T是否是在服务A、包C构建之后构建发布的呢?(虽然记忆中是按的这个顺序)
在jenkins上查看构建记录,包B是9月5号上午11点构建的,服务A是下午4点构建发布的,我们这边的拓扑T是下午5点以后构建发布的;
在storm UI上去看拓扑的Uptime,已启动了5天多10多个小时,推算下时间,也证明拓扑T是构建后重启的;
综上2点,说明拓扑T的依赖(A和B)都是最新的,应该不存在provider和consumer枚举C类不一致的问题。
跟同事沟通确定这点后都有些疑惑。尝试把拓扑T构建打包后的jar下到本地,解压出来看class文件,发下XxxBolt类所在包下,不仅有XxxBolt.class,还多了一个XxxBolt$1.class;
再回头看日志'com.xxx.XxxBolt$1' is an unknown class,这时才注意到日志里说找不到类的名称,XxxBolt后面多了$1,之前看漏了- -!
想起java编译生成class文件,如果定义了内部类会产生xxx$xxx的class文件;
可记得XxxBolt类里面并没有定义内部类啊,翻看一遍代码确实没有。
用反编译工具打开XxxBolt$1.class:
import com.xxx.枚举B; import java.util.HashSet; class 1 extends HashSet<枚举B> { }
类名是1,这样XxxBolt$1名称解释得通,但它是哪里产生的呢?
刚才枚举B已确定是最新的,于是我们把目光锁定在了这个HashSet的构造上
Set<类B, Object> xxSet = new HashSet{{ add(枚举C.枚举1,xxx); add(枚举C.枚举2,xxx); ... }}
这里使用了快捷的初始化数据语法,即在{{}}里用add的方式;
尝试把XxxBolt代码里的某个Map xxx = new HashMap也用这种写法,重新编译发现生成了XxxBolt$2.class,反编译打开是class 2 extends HashMap<>...
那么问题找到了,拉分支把这段代码修改下:
Set<类B, Object> xxSet = new HashSet<类B, Object>(); xxSet.add(枚举C.枚举1, xxx); xxSet.add(枚举C.枚举2, xxx); ...
用普通的初始化方式,先new好,在用对象调add方法。
重新编译就只有XxxBolt.class了。
依次合并代码至主干后,重新构建发布并重启拓扑T,再观察服务A中已没有这个警告日志了。
问题解决:)
-------------------------------------------------------------------------------------------------------------------------------------------------
总结:
dubbo服务provider和consumer中入参要保持一致,入参vo有类型或结构变动,consumer端需要跟着provider构建,依赖最新的包;
HashSet、HashMap用快捷初始化语法时,编译会产生内部类,以Xxx$1这种命名方式,在dubbo接口方法参数中如果用Set、Map类型,则需要注意用普通的方式初始化;
-------------------------------------------------------------------------------------------------------------------------------------------------
参考:
【解决】hessian 客户端报警告:Hessian/Burlap: xxx is an unknown class in sun.misc.Launcher…… https://blog.csdn.net/fangzhangsc2006/article/details/7975978
警告: Hessian/Burlap: 'com.github.pagehelper.Page' is an unknown class in WebappClassLoader https://blog.csdn.net/qq_38765404/article/details/78108780