Dubbo还有这样的bug,你能忍?

Posted 占小狼的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo还有这样的bug,你能忍?相关的知识,希望对你有一定的参考价值。

优质文章,及时送达

本文场景基于dubbo-2.5.3版本。

如果你对StackOverflowError有一定的了解,就可以知道出现这个问题的主要原因就是调用栈太深,比如常见的无限递归调用。那本文要介绍的Dubbo抛出的这个StackOverflowError又是什么原因呢?且往下看。

重现问题

话不多说,直入主题。这次碰到的StackOverflowError非常好重现,只需要如下简短的代码即可。需要注意的是这里调用的是com.alibaba.dubbo.common.json.JSON,而不是fastjson中的com.alibaba.fastjson.JSON:

 
   
   
 
  1. package com.afei.test.dubbo.provider.main;


  2. import com.alibaba.dubbo.common.json.JSON;

  3. import java.util.Locale;


  4. public class DubboTest {

  5. public static void main(String[] args) throws Exception {

  6. Locale locale = Locale.getDefault();

  7. System.out.println(JSON.json(locale));

  8. }

  9. }

运行这段代码能得到如下异常:

 
   
   
 
  1. Exception in thread "main" java.lang.StackOverflowError

  2. at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)

  3. at sun.util.locale.provider.LocaleResources.getLocaleName(LocaleResources.java:233)

  4. at java.util.Locale.getDisplayName(Locale.java:1879)

  5. at java.util.Locale.getDisplayName(Locale.java:1845)

  6. at com.alibaba.dubbo.common.bytecode.Wrapper0.getPropertyValue(Wrapper0.java)

  7. at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:125)

  8. at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:73)

  9. at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:129)

  10. at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:73)

  11. at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:129)

  12. at com.alibaba.dubbo.common.json.GenericJSONConverter.writeValue(GenericJSONConverter.java:73)

  13. ... ...

分析原因

由这个异常堆栈信息,我们很容易知道在GenericJSONConverter中的第73行和129行之间出现了无限递归调用,打开dubbo源码并debug,发现在调用GenericJSONConverter中的writeValue()方法时,首先会判断需要序列化的对象的类型。当对象是如下类型时会特殊处理:

  1. 原生类型或者封装类型;

  2. JSONNode类型;

  3. 枚举;

  4. 数组;

  5. Map;

  6. 集合类型;

如果需要序列化的对象是其他类型,比如这里的Locale类型,序列化逻辑如下所示:

 
   
   
 
  1. jb.objectBegin();


  2. Wrapper w = Wrapper.getWrapper(c);

  3. // 得到这个对象的所有属性

  4. String pns[] = w.getPropertyNames();

  5. // 遍历属性

  6. for( String pn : pns )

  7. {

  8. // 被序列化的对象Locale并不是Throwable类型,忽略这段逻辑

  9. if ((obj instanceof Throwable) && (

  10. "localizedMessage".equals(pn)

  11. || "cause".equals(pn)

  12. || "stackTrace".equals(pn))) {

  13. continue;

  14. }


  15. jb.objectItem(pn);

  16. // 得到当前遍历属性的值

  17. Object value = w.getPropertyValue(obj,pn);

  18. if( value == null || value == obj)

  19. jb.valueNull();

  20. else

  21. // 无限递归死循环出现在这里

  22. writeValue(value, jb, writeClass);

  23. }

通过这段源码的分析,我们大概可以知道Locale的属性中肯定有Locale类型的属性。由于有Locale类型的属性,导致继续调用GenericJSONConverter中的writeValue()方法,从而无限递归下去,让我们继续Debug源码验证这个猜想。

Debug到String pns[] = w.getPropertyNames();,我们通过查看Locale的属性pns[]可以验证我们前面的猜想,如下图所示。Locale属性availableLocales的类型还是Locale,从而出现死循环直到抛出StackOverflowError:

Dubbo还有这样的bug,你能忍?

解决问题

那么如何解决这个问题呢?很简单,不要使用dubbo中的JSON,改为使用fastjson中的JSON,或者jackson和GSON都可以:

 
   
   
 
  1. Locale locale = Locale.getDefault();

  2. System.out.println(com.alibaba.fastjson.JSON.toJSON(locale));

  3. System.out.println(new com.google.gson.Gson().toJson(locale));

Dubbo Fix

修复的代码片段如下所示,主要改动点有:

  1. 如果序列化对象是Locale类型,那么序列化方式就是调用toString()方法;

  2. 如果反序列化目标对象类型是Locale,那么将value以下划线分割,然后构造Locale对象,用法参考:JSON.parse("zhCN", Locale.class);

近期热文

 


最后,分享一份面试宝典Java核心知识点整理.pdf》,覆盖了JVM、锁、高并发、反射、Spring原理、微服务、Zookeeper、数据库、数据结构等等。

明天见(。・ω・。)ノ♡

以上是关于Dubbo还有这样的bug,你能忍?的主要内容,如果未能解决你的问题,请参考以下文章

够强!一行代码就修复了我提的Dubbo的Bug。

vscode代码片段建议bug

敖丙找出Dubbo源码BUG,三歪夸了我一天

敖丙找出Dubbo源码BUG,三歪夸了我一天

离职两年后,程序员遭前东家索赔:Bug 是你写的

离职两年后,程序员遭前东家索赔:Bug 是你写的