关于dubbo的validation问题分析

一、背景

日常真线错误日志排查中,日志经常出现,warn级别的错误日志,如下 frozen class cannot edit,但是不影响业务,太多的warn日志,影响真线的其他问题排查,因此需要进一步分析排查原因;


图1

问题:
为什么dubbo的consumer端没有配置validation=true 默认是false,会有校验器进行参数校验?

二、源码分析
出现该问题是在应用某列表页或详情页调用了下面接口:


图2

图3

根据异常堆栈信息,进行分析;(提前说下:导致该问题出现的原因与类加载器有关)
step1、首次加载获取validator,属性中的clazz加载器是LaunchedURLClassLoader, 不是当前线程的TomcatEmbeddedWebappClassLoader
com.alibaba.dubbo.validation.support.AbstractValidation#getValidator
这里clazz对应的是xxx....DepositProjectExternalFacade,见下图


图4

step2、
com.alibaba.dubbo.validation.support.jvalidation.JValidator#JValidator
其中clazz属性通过反射获取
图5

反射使用的是当前线程上下文中的加载器TomcatEmbeddedWebappClassLoader


图6
图7

通过反射获取
TomcatEmbeddedWebappClassLoader的父加载器是LaunchedURLClassLoader,根据双亲委派原则,clazz实际通过父加载器LaunchedURLClassLoader加载,如下图


图8

clazz对象
注:spring 内嵌了tomcat,spring指定TomcatEmbeddedWebappClassLoader的父加载器为LaunchedURLClassLoader
参见:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory #prepareContext


图9
图10

<font color=#FF7F00 >step3、</font> <font color=#FF00FF>上面的JValidator校验器初始化加载部分,下面进入正文分析</font>
com.alibaba.dubbo.validation.support.jvalidation.JValidator#validate(java.lang.String, java.lang.Class<?>[], java.lang.Object[])

图11

图11
step4、接着进入com.alibaba.dubbo.validation.support.jvalidation.JValidator#getMethodParameterBean
图12

首先假设第一次调用xxx.....DepositProjectExternalFacade(应用重启以后第一次调用)
上图中的1 parameterClass = (Class<?>) Class.forName(parameterClassName, true, clazz.getClassLoader());
parameterClassName = xxx......DepositProjectExternalFacadeQueryPaymentParameter ![图13](https://upload-images.jianshu.io/upload_images/18232435-d0e436a499212da7?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 如上图 clazz.getClassLoader()的加载器为LaunchedURLClassLoader(step1中已经分析过), 首次加载,当前jvm中没有parameterClassName这个class,因此通过反射获取该class对象会报错,提示classNotFoundException ![图14](https://upload-images.jianshu.io/upload_images/18232435-621b6e1eaff4cc41?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 然后dubbo做了个try catch,如上图的第2步,CtClass ctClass = pool.makeClass(parameterClassName); 对异常捕获后利用字节码javassist工具,动态生成字节码,相关知识点网上比较多,可以自行查找,限于篇幅本文不做扩展 可以参考[参考文献1](https://blog.csdn.net/a394268045/article/details/51996082)、[参考文献2](https://www.cnblogs.com/sunfie/p/5154246.html)(这里会涉及到冻结frozen 和 解冻defrost的概念 本文的核心)上图的第三步 parameterClass = ctClass.toClass();当CtClass 调用writeFile()、toClass()、toBytecode() 这些方法的时候,Javassist会冻结CtClass Object,对CtClass object的修改将不允许。这个主要是为了警告开发者该类已经被加载,而JVM是不允许重新加载该类的。继续看下ctClass.toClass() ![图15](https://upload-images.jianshu.io/upload_images/18232435-b4c4c640c09bd49c?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 一般是rest请求,当前线程的上下文加载器是TomcatEmbeddedWebappClassLoader,因此第一次加载parameterClass对象的类加载器是TomcatEmbeddedWebappClassLoader; 然后。。。。。; ![图16](https://upload-images.jianshu.io/upload_images/18232435-35872c676ed76872?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 获取到parameterClass对象,然后返回,所有功能正常; step5、继续第二次调用 此时如进入到上图,然后进入图中标注1 parameterClass = (Class<?>) Class.forName(parameterClassName, true, clazz.getClassLoader()); 上文解释过加载 parameterClass (DepositProjectExternalFacadeQueryPaymentParameter)
这个对象的加载器是TomcatEmbeddedWebappClassLoader,但是clazz.getClassLoader()这个是LaunchedURLClassLoader(父加载器),因此也会报错,对象不是同一个加载器,也不符合双亲委派原则;
此时还会继续走catch的逻辑,pool.makeClass(parameterClassName),此时就要涉及上文提到过的冻结了,ctclass对象在第一次调用的时候已经new 出来了,
同时最后做了toclass,故此时ctclass已经被冻结;再第二次makeClass时会进行是否被冻结的校验,如果已经冻结,会直接报错
图17

图18

真线报错的信息,就是这里抛出的,至此定位到问题所在;
step6、抛出错误后对应用会有什么影响吗?
图19

由图20 可知这里在最外层做了try cache,直接返回null,


图20

如果返回null,图21中圈起来的逻辑就不走了,这个对应的是图5中的@Notnull注解不起作用,因此不影响业务功能;

三、问题分析

3.1 我们来看下开头提出的问题
consumer没有注册validation=true,为什么会在consumer端进行校验我们来看web-reverse中,dubbo初始化加载的配置
com.alibaba.dubbo.registry.integration.RegistryDirectory#toInvokers


图21

com.alibaba.dubbo.registry.integration.RegistryDirectory#mergeUrl 如果consumer没有配置会使用provide端的配置


图22

如果consumer端配置了,会对dubbo-validation配置进行覆盖,如果没有配置就继承provider的值


图23

因此我们可以看到,虽然反向竞价consumer中没有配置validation=true,但是provide配置了这个validation=true,因此导致consumer进行相关的校验


图24

3.2 解决方法
在dubbo的consumer配置中,加上validation=false,关闭消费端校验
注:最新版dubbo已经解决该问题,解决方案,和通过反射获取的类加载保持一致,即当前类的加载器
parameterClass = ctClass.toClass(clazz.getClassLoader(), (ProtectionDomain)null);

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容