背景
项目中通过远程调用服务框架调用了许多其它的服务,其中有一个服务wx/subscribe/contract/CircleService 需要升级,其升级不是版本上的升级,而是整个服务重新取了一个名字,使用的也是全新的jar包wuxian/social/contract/SocialService,但是调用的方法没有改变,因此在升级时,只是在调用服务service类中修改了调用地址,和调用返回实体(由CircleService改为SocialService),该service中返回该调用实体使用的是static方法.修改完毕过后本地运行一切正常,于是将修改service的class文件以及对应的jar包上到沙箱环境,并将沙箱环境中原来的CircleService jar包删除,然后重启,但是报错了.代码修改如下:
将
private static volatile CircleService subCircleService = null;
subCircleService = ProxyFactory.create(CircleService .class,"tcp://circle/CircleServiceImpl" );
public static CircleService getSubCircleService() {
return subCircleService;
}
修改为
private static volatile SocialService subCircleService = null;
subCircleService = ProxyFactory.create(SocialService.class,"tcp://social/SocialServiceImpl" );
public static SocialService getSubCircleService() {
return subCircleService;
}
其它地方的调用都是用类名+方法名(SCFService.getSubCircleService())的方式访问,故此,在修改时只改动了这个文件,其它文件都没有修改,增量上线的时候也只上线该文件即可.
报错现象
重启服务器的时候无法启动服务,一直报如下错误
错误内容为找不到wx/subscribe/contract/CircleService这个jar包提供的方法,很奇怪为什么会提示这个错误,刚开始怀疑是上线的class文件有问题,沙箱上部署class文件没有成功,还是原来的文件,所以又部署了一次,但是还是报同样的错误.
解决过程
将部署到沙箱上的文件下载下来,进行反编译,看引用中是否存在CircleService相关的jar包应用,结果反编译后,发现报错的文件中都没有CircleService相关的jar包引用,于是在cmd窗口中使用javap编译class文件
javap -v xx.class > D:/result.txt
使用该命令后,在result.txt中的常量池中,惊人的发现居然还有引用CircleService相关jar包的行
故此解开了为何代码中为何会报上述错误的问题,于是将虽然代码没有改动,但是重新编译过后的class文件上到沙箱,重启,结果果然没有报错了,于是上线.但是上线过后才发现,其它类在访问时候才会调用该方法的类依旧会报上述错误,于是重新检查了一遍所有引用过上述修改过的方法的类,将重新编译过后的文件都上线,才解决了报错的问题,这就是增量上线class文件引发的一起血案.
思考
我们观察在在上述报错中,报错内容为NoSuchMethodError,而不是ClassNotFound,这是为什么呢?在其它代码引用上述修改的方法时,都是通过SCFService.getSubCircleService()的方式调用,而没有写返回类,在调用上述方法时,老的class文件常量池中,getSubCircleService()返回的是CircleService方法,而新上线的SCFService中该方法的返回值已经修改为SocialService,故此会先报NoSuchMethodError错误.
这种错误类似于我们在常量类中定义
public static final CONST = 100;
后,要修改常量CONST值为1000后,只增量上线了该常量class文件,而其它引用该常量的类的文件都更新上线所引发的CONST依旧为100的问题.