百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

上线前一个小时,dubbo这个问题可把我折腾惨了

lipiwang 2024-10-23 14:02 7 浏览 0 评论

以下文章来源于猿天地 ,作者尹吉欢

前因

那是一个月黑风高的夜晚,不管有没有圆圆的月亮,都无法解救要加班的我。这就是苦涩的人生啊!

那天正好是春节回家的日子,定了晚上的票,然后还是上线的日子。

测试在做回归测试的时候,发现一个老功能报错了,什么鬼,都没改过那块代码怎么会出问题?案件疑点重重呀。。。

为了能够早点上线,早点回家,所以这个 Bug 就显得十万火急了,因为就这一个问题,其他都没问题,解决好了就可以上线了,于是开启了破案之路。

第一步:找到错误信息

机智的我在第一时间打开了 Cat 查看具体的错误,由于当时并没有想到去写一篇文章出来,错误信息也就没有截图,后面通过模拟的操作,得到了类似的一样的错误信息如下:

居然是类转换错误,点进去查看详细的错误信息,如下图:

真正有价值的错误信息如下:

dubbo version: 2.7.3, current host: 192.168.8.224 java.lang.ClassCastException:

第二步:排查报错的代码

公司代码不方便透露,下面都是模拟的代码:

public ResponseData<String> login(UserLoginRequest loginRequest) {
    loginRequest.getAddress().stream().map(a -> a.getStatus()).collect(Collectors.toList());
return Response.ok("xxxxxxxxx");
}

问题就出在了 map 这里,从 loginRequest 参数中获取 address 是一个 List

,Address 中有 status 字段,如果是正常的对象没有问题,错误告诉我们是 HashMap 不能转换成 Address 类,也就是说参数中的 Address 变成了 HashMap 导致的错误。

参数代码:

@Data
public class UserLoginRequest implements Serializable {
    private String username;
    private String pass;
    private List<Address> address;
}
@Data
@AllArgsConstructor
public class Address implements Serializable {
    private int status;
}

第三步:本地复现错误

找到错误后,马上本地启动相关的两个服务,我们分别叫 A 和 B 吧,现象是 A 调用 B 的某个 RPC 接口报错。

本地启动后马上复现了错误,在报错的地方打断点看参数是否变成了 HashMap,果不其然,如下图:

到这里感觉有点懵,参数中明明是具体的对象类型,怎么突然就变成了 HashMap,匪夷所思。

然后想着是不是在上层什么地方出问题了,继续查看报错的上层代码,没有发现异常。然后决定在 PRC 的入口处打个断点看看是不是参数一过来就出问题了,最后经过验证确实如此,也就排除了 B 服务中对参数做了转换。

接着再看下 Dubbo 内部的参数解码,

org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation#decode(org.apache.dubbo.remoting.Channel, java.io.InputStream)。也就是请求到达 B 之后解码出来的已经是 HashMap 了,那么问题肯定是调用方传输的参数有问题。

第四步:排查调用方代码

在调用方这边发起请求前,查看了参数对象,发现这个时候参数已经出问题了,字段类型发生了变化,所以问题就出在这里,都是老代码,应该都没改过,而是事实却被改了,通过 Idea 的 Annotate 快速的查看了当前方法中有被修改的记录,找到了修改的代码,下面通过模拟的方式贴出有问题的代码,如下:

@Reference(version = DubboConstant.VERSION_V100, group = DubboConstant.DEFAULT_GROUP)
private UserRemoteService userRemoteService;
public void test() {
    UserLoginRequest request = new UserLoginRequest();
    request.setUsername("yjh");
    request.setPass("123456");
    List<Address> address = new ArrayList<>();
    address.add(new Address(1));
    request.setAddress(address);
    UserLoginRequest2 request2 = new UserLoginRequest2();
    request2.setUsername("yjh2");
    request2.setPass("1234562");
    List<Address2> address2 = new ArrayList<>();
    address2.add(new Address2(StatusEnum.INVALID));
    request2.setAddress(address2);

    BeanUtils.copyProperties(request2, request);

    userRemoteService.login(request);
}

出问题的就是 BeanUtils.copyProperties(request2, request); 这行代码,将一个对象复制到另一个对象,两个对象的属性都一样,唯一不一样的是 Address 中的 status 是 int 类型,Address2 中的 status 是 Enum,复制过去就出问题了。

这种情况也只在 Dubbo 的 RPC 请求出问题,如果是 Http 请求,基本类型变成了枚举,直接就报错了,无法转换。

第五步:BeanUtils 问题排查

归根到底还是 copy 的问题,我做了个小实验,如果是 Address2 copy 到 Address 是不会出问题的,只有嵌套的对象才会出问题。

特意看了下 copy 的代码,如果是 Address2 copy 到 Address,那么就是 status 到 status,在 copy 之前会进行判断 Address 的 setStatus 的第一个参数类型和 Address2 的 getStatus 的返回值是否相同,如果相同才会进行赋值操作,不同就不会,如果是单个对象在这里就会直接过滤掉了,一个是 int 一个是 Enum。

嵌套对象之所以可以那是因为 address 的参数和返回类型都是 List,没有去判断嵌套类里面的,是整个集合直接复制赋值的,下图是目标方法:

value 是新的集合对象,invoke 后整个 address 就变了。

第六步:Dubbo 解码问题排查

前面分析中,调用之前通过 BeanUtils 复制,只是将枚举赋值给了基本类型,如果 Dubbo 在接收到参数进行解码时能够识别出类型不一致,这样就直接会报错了,然而并没有,特意调试了下 Dubbo 解码的代码,默认是 Hessian 的解码,怀疑跟 Hessian 有关,于是我把序列化改成了 FastJson,在解码参数的时候就直接报错了,不能转换成 int 类型。而 Hessian 在映射不了的时候就直接变成 HashMap 了,这才有了我们前面的错误。

结局

找到原因后解决就是分分钟的事了,通过这个问题还是说明了加任何的代码都有风险。剩下的就是开发的锅了,加了代码没有自测,好在有测试把关,否则就凉凉了。

相关推荐

前端入门——css 网格轨道详细介绍

上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...

HTML中script标签中的那些属性

HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...

CSS 中各种居中你真的玩明白了么

页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...

CSS样式更改——列表、表格和轮廓

上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...

一文吃透 CSS Flex 布局

原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...

css实现多行文本的展开收起

背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...

css 垂直居中的几种实现方式

前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...

WordPress固定链接设置

WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...

面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿

前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...

3种CSS清除浮动的方法

今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...

2025 年 CSS 终于要支持强大的自定义函数了?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...

css3属性(transform)的一个css3动画小应用

闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...

CSS基础知识(七)CSS背景

一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...

CSS 水平居中方式二

<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...

取消回复欢迎 发表评论: