dubbo小坑 ClassCastException

1.背景

有一天同事找我看一個(gè)問(wèn)題,說(shuō)rpc調(diào)用出錯(cuò)了赏表,具體錯(cuò)誤:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Short
    at XXXXXXXXXXXXXXXXXXXManageServiceInner.createXXXX(XXXXServiceInner.java:266)
    at XXXXXXXXXXXXXXXXXXXManageServiceImpl.create(XXXXXServiceImpl.java:80)
    at XXXXXXXXXXXXXXXXXXXManageServiceImpl$$FastClassBySpringCGLIB$$10c16c30.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:150)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:150)
    at com.XXXXXXXXXXXXXXXXXXX.ParamValidatorAspect.doRequestClass(ParamValidatorAspect.java:41)
    at sun.reflect.GeneratedMethodAccessor402.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
    at com.XXXXXXXXXXXXXXXXXXX.ParamLogAspect.execWithLog(ParamLogAspect.java:128)
    at com.XXXXXXXXXXXXXXXXXXX.ParamLogAspect.doRequestClass(ParamLogAspect.java:100)
    at sun.reflect.GeneratedMethodAccessor401.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

類型轉(zhuǎn)化錯(cuò)誤检诗?? 為啥我看了一下 接口定義類似如下:

public interface XXXXManageService {
  PlainResult<Long> create(XXXRequest request);
}
public class XXXRequest implements Serializable {
  private static final long serialVersionUID = 1L;
  private String a;
  private Boolean b; 
  private HashMap<String,Short> c;
}

感覺(jué)沒(méi)有毛病捌敖恕逢慌?!间狂!
現(xiàn)在出現(xiàn)的問(wèn)題:
A業(yè)務(wù)方調(diào)用通過(guò)普通rpc調(diào)用報(bào)類型轉(zhuǎn)換錯(cuò)誤
B公司對(duì)外網(wǎng)關(guān)也是通過(guò)rpc調(diào)用攻泼,為啥沒(méi)有報(bào)錯(cuò)?
第一反應(yīng)鉴象,sb業(yè)務(wù)方使用方式有問(wèn)題忙菠,會(huì)不會(huì)寫代碼!纺弊! 別人都可以就你不行


image.png

檢查了一下業(yè)務(wù)方的代碼沒(méi)毛病牛欢。。淆游。氢惋。
自己寫了一個(gè)demo,也是同樣的異常稽犁。焰望。。


image.png

公司的網(wǎng)關(guān)服務(wù)調(diào)用沒(méi)有問(wèn)題R押ァ熊赖!我們自己調(diào)用有問(wèn)題!B亲怠震鹉!
1.dubbo版本不一致導(dǎo)致的? (排除捆姜,版本是一致的)
2.開(kāi)始懷疑人生传趾。。泥技。


image.png

開(kāi)始debug 看了一下 dubbo的源碼解碼和編碼 在 dubboCodec類中來(lái)看一下decodeBody方法中的 這段代碼

DecodeableRpcInvocation inv;
if (channel.getUrl().getParameter(
        Constants.DECODE_IN_IO_THREAD_KEY,
        Constants.DEFAULT_DECODE_IN_IO_THREAD)) {
    inv = new DecodeableRpcInvocation(channel, req, is, proto);
    //從這里看到真正的解碼在 DecodeableRpcInvocation中 我們進(jìn)去看看他們的源碼
    inv.decode();
} else {
    inv = new DecodeableRpcInvocation(channel, req,
            new UnsafeByteArrayInputStream(readMessageData(is)), proto);
}

主要的解碼邏輯在DecodeableRpcInvocation 中

  public Object decode(Channel channel, InputStream input) throws IOException {
        ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
                .deserialize(channel.getUrl(), input);

        try {
            //解析dubbo協(xié)議版本
            setAttachment(Constants.DUBBO_VERSION_KEY, in.readUTF());
            //解析接口類路徑 "com.XXXXXXXX.service.XXXXService"
            setAttachment(Constants.PATH_KEY, in.readUTF());
            /解析版本 協(xié)議版本0.0.0
            setAttachment(Constants.VERSION_KEY, in.readUTF());
            //解析方法名稱create 如果是泛化調(diào)用 方法名稱就是 $invoke
            setMethodName(in.readUTF());
            try {
                Object[] args;
                Class<?>[] pts;

                // NOTICE modified by lishen
                int argNum = -1;

                if (CodecSupport.getSerialization(channel.getUrl(), serializationType) instanceof OptimizedSerialization) {
                    argNum = in.readInt();
                }
                if (argNum >= 0) {
                    浆兰。。。簸呈。榕订。忽略代碼
                } else {
                //重點(diǎn)在這里如果是普通的rpc調(diào)用 desc就是參數(shù)名稱 例如 XXXX.XX.XXXRequest
                //如果是泛化調(diào)用 desc 就是 Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;
                //說(shuō)明一下是這個(gè)desc 實(shí)際上就是入?yún)?來(lái)看一下泛化請(qǐng)求你就明白 
                //GenericService類中的 方法 Object $invoke(String method, String[] parameterTypes, Object[] args) 
                    String desc = in.readUTF();
                    if (desc.length() == 0) {
                        pts = DubboCodec.EMPTY_CLASS_ARRAY;
                        args = DubboCodec.EMPTY_OBJECT_ARRAY;
                    } else {
                    //通過(guò)反射生成class   XXXX.XX.XXXRequest
                        pts = ReflectUtils.desc2classArray(desc);
                        args = new Object[pts.length];
                        for (int i = 0; i < args.length; i++) {
                            try {
                            //把流中的數(shù)據(jù)寫入到 pts中 實(shí)際上就是 直接賦值 XXXX.XX.XXXRequest
                            //那為什么會(huì)出現(xiàn)類型轉(zhuǎn)換錯(cuò)誤? 
                            //看了一下readObject方法的實(shí)現(xiàn) 我使用的dubbo序列化是 Hessian2
                            //然而Hessian2ObjectInput判斷是整數(shù)類型的化最終封裝成兩種類型一種是Integer蜕便,一種是Long
                            //readObject這個(gè)方法對(duì)把讀取到數(shù)據(jù)賦值給XXXX.XX.XXXRequest
                            //所以導(dǎo)致了在實(shí)際使用中會(huì)出現(xiàn)類型轉(zhuǎn)換問(wèn)題
                            //疑問(wèn)劫恒? 泛化調(diào)用為什么沒(méi)有問(wèn)題
                            //解答 泛化調(diào)用 desc 等于Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;
                            //args的數(shù)組長(zhǎng)度等于3 pts 等于 desc以逗號(hào)切割 arg[0] = create arg[1]=XXXX.XX.XXXRequest arg[2]=map 這個(gè)map實(shí)際上就是入?yún)⒌某蓡T變量和value 
                            //也就是說(shuō)泛化調(diào)用的參數(shù)并沒(méi)有在下面這個(gè)方法被反序列,真正執(zhí)行把入?yún)⒎葱蛄械膭?dòng)作在后面
                                args[i] = in.readObject(pts[i]);
                            } catch (Exception e) {
                                if (log.isWarnEnabled()) {
                                    log.warn("Decode argument failed: " + e.getMessage(), e);
                                }
                            }
                        }
                    }
                }
                轿腺。两嘴。。族壳。憔辫。。忽略

            } catch (ClassNotFoundException e) {
                throw new IOException(StringUtils.toString("Read invocation data failed.", e));
            }
        } finally {
            // modified by lishen
            if (in instanceof Cleanable) {
                ((Cleanable) in).cleanup();
            }
        }
        return this;
    }

來(lái)看一下 泛化調(diào)用的參數(shù)序列化實(shí)現(xiàn)在GenericFilter的invoke方法中

String name = ((String) inv.getArguments()[0]).trim();
String[] types = (String[]) inv.getArguments()[1];
Object[] args = (Object[]) inv.getArguments()[2];
try {
Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
Class<?>[] params = method.getParameterTypes();
if (args == null) {
    args = new Object[params.length];
}
String generic = inv.getAttachment(Constants.GENERIC_KEY);
if (StringUtils.isEmpty(generic)
    || ProtocolUtils.isDefaultGenericSerialization(generic)) {
    args = PojoUtils.realize(args, params, method.getGenericParameterTypes());

PojoUtils.realize這個(gè)作用就是就是把參數(shù)反序列到 XXXX.XX.XXXRequest中
這個(gè)里面會(huì)更具 XXXX.XX.XXXRequest的每一個(gè)參數(shù)類型去設(shè)置值决侈,如果dubbo解析的參數(shù)類型是integer 會(huì)被轉(zhuǎn)成 XXXX.XX.XXXRequest成員的真正類型螺垢。
這也就是為什么公司網(wǎng)關(guān)服務(wù)能夠成功調(diào)用,而為什么普通的rpc會(huì)出現(xiàn)異常的原因赖歌。枉圃。。庐冯。
總結(jié):dubbo在解析整數(shù)類型的時(shí)候只提供 Integer孽亲、和Long 如果在設(shè)計(jì)接口的時(shí)候使用short 類型的 就要注意了 。會(huì)導(dǎo)致類型轉(zhuǎn)化問(wèn)題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末展父,一起剝皮案震驚了整個(gè)濱河市返劲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌栖茉,老刑警劉巖篮绿,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吕漂,居然都是意外死亡亲配,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門惶凝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吼虎,“玉大人,你說(shuō)我怎么就攤上這事苍鲜∷蓟遥” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵混滔,是天一觀的道長(zhǎng)洒疚。 經(jīng)常有香客問(wèn)我歹颓,道長(zhǎng),這世上最難降的妖魔是什么拳亿? 我笑而不...
    開(kāi)封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任晴股,我火速辦了婚禮愿伴,結(jié)果婚禮上肺魁,老公的妹妹穿的比我還像新娘。我一直安慰自己隔节,他們只是感情好鹅经,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著怎诫,像睡著了一般瘾晃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上幻妓,一...
    開(kāi)封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天蹦误,我揣著相機(jī)與錄音,去河邊找鬼肉津。 笑死强胰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妹沙。 我是一名探鬼主播偶洋,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼距糖!你這毒婦竟也來(lái)了玄窝?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悍引,失蹤者是張志新(化名)和其女友劉穎恩脂,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體趣斤,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俩块,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唬渗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片典阵。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖镊逝,靈堂內(nèi)的尸體忽然破棺而出壮啊,到底是詐尸還是另有隱情,我是刑警寧澤撑蒜,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布歹啼,位于F島的核電站玄渗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏狸眼。R本人自食惡果不足惜藤树,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拓萌。 院中可真熱鬧岁钓,春花似錦、人聲如沸微王。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炕倘。三九已至钧大,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罩旋,已是汗流浹背啊央。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涨醋,地道東北人瓜饥。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像东帅,于是被迫代替她去往敵國(guó)和親压固。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容