非标类型导致Dubbo接口出入参异常的本质 | 得物技术

一、概述

笔者支持过程中多次发现诡异的Dubbo接口异常问题,抓耳挠腮最后定位到代码上和代码外的原因,事后只感觉脑瓜子嗡嗡的。考虑到这不是第一次,也绝不会是最后一次出现类似问题,下面笔者将尽可能详细的梳理、总结一下该问题的现象和本质。

二、问题是什么?

DubboRPC+Protostuff序列化场景下,如果DTO中的字段使用了非标集合类型,可能会导致接口出入参为nullerror,进一步导致业务受损。

该问题的产生存在如下前提或者门槛:

  1. 接口使用的是DubboRPC;

  2. 接口使用的是Protostuff序列化;

  3. DTO中声明了类型模糊的字段;

  4. 类型模糊的字段赋值了非标集合类型数据。

DTO中声明了类型模糊的字段

所谓类型模糊的字段,指的是在DTO中存在没法一眼判断出运行时具体类型的字段(静态分析)。听起来还是很抽象是吧?我们看下面的例子:

图片

例1

图片

例2

图片

例3

相信读者在看到上述案例的时候,肯定没法一口笃定字段obj3在运行时具体的、实际的类型是啥吧?如果机智如你肉眼都无法识别、判定这个字段的类型,那死板的序列化库(lib)代码更没法识别、判定。

你看到都迷糊,更别提代码了。

DTO中赋值了非标集合类型数据

要解释非标集合类或者非标准集合类,首先我们需要对齐标准集合类。

标准集合类指的是常见的、jdk标准库内自带的集合类。一般在java.util包下,实现了顶层的、公共的Interface,有公开的的构造方法。常见的有:

图片

io.protostuff.CollectionSchema.MessageFactories

三、为什么会有这个问题?

如上述,当DTO中存在类型模糊的字段时,常见的序列化框架如Protostuff,没有办法根据类的定义和反射得到准确的数据类型。为了在序列化后(object -> byte[ ])能正常的、按预期完成反序列化(byte[ ] -> object),一般会在byte[ ]中添加类型提示,以辅助反序列化过程正常进行。

以如下DTO为例:

图片

假设按如下代码构造上述Req:

图片

我们用类json格式来进行序列化、反序列化推演,如果不做任何处理,序列化(object -> byte[ ])后的结果如下:

不管是json这种文本格式,还是Protostuff这种二进制格式,序列化产物(byte[ ])在逻辑结构上是相似的~

图片

可以看到,序列化产物上没有任何类型信息(运行时信息),同时我们上面也看到,DemoReq类的obj3字段上也没有任何具体的类型提示(编译期信息)。那么对端收到上述数据进行反序列化(byte[ ] -> object)时,就无法恢复出正确的、类型和上游一致的字段对象。

Protostuff希望解决这个问题!Protostuff希望达成如下效果:

图片

换句话说,Protostuff想保证:客户端的一个对象被序列化(object -> byte[ ])、传输到对端反序列化后(byte[ ] -> object),对端可以安全的对这个对象进行equals判断并以此进行业务逻辑处理。

为了实现这个目的,Protostuff在序列化产物上做了一些黑盒的、对上层业务透明的处理。在序列化产物的字段结构上添加类型提示,具体到java语言平台就是类的全限定名。

图片

如上图,Protostuff在序列化产物(object -> byte[ ])中,将类型信息java.util.ArrayList关联到obj3字段上传输到对端,对端在反序列化(byte[ ] -> object)时,如果无法通过反射在DTO类字段(静态的、编译期信息)上提取数据类型时,会从序列化产物中提取字段类型,利用反射或硬编码创建实例后,再进行后续的数据解析、提取。

这就出现了一个问题:

如果在DTO中模糊字段上赋值了对端(另一个进程)不存在的类型或无法通过反射实例化的类型,那对象序列化、传输到对端后,对端进行反序列化(byte[] -> object)的过程就会异常!

对端(另一个进程)不存在的类型

图片

如上图,Foo类是一个静态私有类,不在Api Jar中,传输到对端(另一个进程)后,对端反序列化时可能找不到该类(ClassNotFoundError)。

无法通过反射实例化的类型

图片

如上图,Bar类是一个内部类,只能寄生于外部类,无法被单独实例化。对端反序列化时可能会找不到构造方法(NoSuchMethodException)或类状态不完整无法正常使用。

技术上来说,所有类都有手段进行实例化,比如使用sun.misc.Unsafe#allocateInstance,只是因为绕过了构造方法,实例化出来的类状态是不完整的,不能正常使用。

四、怎么处理这个问题?

调整序列化方案

调整到hessian2、json等代码质量更好、使用范围更广、社区更活跃的序列化方式~

需要注意,存量的很多业务接口依赖了Protostuff的模糊类型序列化特性,不能贸然切换到json、hessian2,需要进行完整的回归测试才能发布到生产环境。

包容它、迁就它

千言万语汇成一句话:避免在DTO中传输非标集合类型数据,或者传输之前进行标准集合类型转换!

你一定想问:为什么中间件同学不能向前一步cover这个问题,还得我们使用者注意,中间件同学的担当、责任感呢?

您的拷问很有道理!但是没有办法,或许Protostuff是为了追求极致的性能而完全抛弃了代码可读性、可维护性,中间件同学完全改不动、不敢改。

日常支持过程中,我们发现常用的、会产生非标集合类型数据的工具库、工具方式包括但不限于:

欢迎大家随时反馈相关的使用场景~

标准库工具类

java.util.ArrayList#subList

ArrayList<Integer> list = Lists.newArrayList(1, 2, 3);List<Integer> obj3 = list.subList(1, 2);

图片

处置方法

ArrayList<Integer> list = Lists.newArrayList(1, 2, 3);List<Integer> obj3 = list.subList(1, 2);List<Integer> obj4 = new ArrayList<>(obj3);

lang3库工具类

org.apache.commons.collections.FastArrayList.SubList#subList​​​​​​​

List<Integer> list = new FastArrayList(1, 2, 3);List<Integer> obj3 = list.subList(1, 2);

图片

处置方法​​​​​​​

ArrayList<Integer> list = Lists.newArrayList(1, 2, 3);List<Integer> obj3 = list.subList(1, 2);List<Integer> obj4 = new ArrayList<>(obj3);

guava库工具类

com.google.common.collect.Lists#transform​​​​​​​

ArrayList<Integer> list = Lists.newArrayList(1, 2, 3);List<Integer> obj3 = Lists.transform(list, it -> it);

图片

处置方法​​​​​​​

ArrayList<Integer> list = Lists.newArrayList(1, 2, 3);List<Integer> obj3 = Lists.transform(list, it -> it);List<Integer> obj4 = new ArrayList<>(obj3);

com.google.common.collect.Lists#partition

ArrayList<Integer> list = Lists.newArrayList(1, 2, 3, 4);List<List<Integer>> obj3 = Lists.partition(list,2);

图片

处置方法

​​​​​​​

ArrayList<Integer> list = Lists.newArrayList(1, 2, 3, 4);List<List<Integer>> lists = Lists.partition(list, 2)        .stream()        .map(Lists::newLinkedList)        .collect(Collectors.toList());

五、总结

以上,笔者基于日常排障&答疑过程中的沉淀和深入到Protostuff源码的细节思考,总结了DubboRPC+Protostuff场景下常见的接口出参、入参为null或error的现象、本质和解决方案。笔者在这只能做到提纲挈领、抛砖引玉,希望可以帮大家理清关键环节和概念,更好的使用DubboRPC支持业务~

 *文/羊羽

本文属得物技术原创,更多精彩文章请看:得物技术官网

未经得物技术许可严禁转载,否则依法追究法律责任!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/597800.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

6个月小猫成长必备!福派斯无麸质幼猫粮评测

你知道吗&#xff1f;给小猫选择适合的猫粮是一件非常不容易但很重要的事情。那么&#xff0c;对于6个月大的小猫来说&#xff0c;什么样的猫粮是最适合它们的呢&#xff1f;&#x1f431; 我们首先要考虑的是猫粮的营养成分。6个月大的小猫正处于快速生长期&#xff0c;所以需…

vue3 + ts实现canvas绘制的waterfall

实际运行效果(仅包含waterfall图表部分) component.vue <template><div ref="heatmap" :style="{ height: props.containerHeight + px }" /> </template><script setup> import ColorMap from "colormap"; import…

Labels and Databases for Mac:强大的标签与数据库管理工具

Labels and Databases for Mac是一款集标签制作与数据库管理于一体的强大工具&#xff0c;专为Mac用户打造&#xff0c;旨在提供高效、便捷的标签制作与数据管理体验。 这款软件拥有丰富的内置标签格式&#xff0c;用户可轻松创建各种标签、信封和卡片&#xff0c;满足个性化需…

掌控网络流量,优化网络性能 - AnaTraf网络流量分析仪登场

在当今日新月异的网络环境中,网络流量监控和性能诊断已成为企业IT部门不可或缺的重要工作。只有充分了解网络流量状况,才能有效优化网络性能,提高业务运营效率。针对这一需求,全新推出的AnaTraf网络流量分析仪应运而生,为企业提供全面的网络监控和性能诊断解决方案。 快速定位…

Java双亲委派机制

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 概述 Java程序在运…

pygame实现鼠标绘制并调节画笔大小

pygame实现鼠标绘制并调节画笔大小 pygame介绍调节画笔大小鼠标绘制效果 pygame介绍 Pygame是一个开源的Python库&#xff0c;专为电子游戏开发而设计。它建立在SDL&#xff08;Simple DirectMedia Layer&#xff09;的基础上&#xff0c;允许开发者使用Python这种高级语言来实…

C语言趣味代码(五)

我想以此篇结束关于C语言的博客&#xff0c;因为在C语言拖得越久越不能给大家带来新的创作&#xff0c;在此我也相信大家对C语言已经有了一个新的认知。进入正题&#xff0c;在这一篇中我主要编一个“英语单词练习小程序”来给大家展开介绍&#xff0c;从测试版逐步改良&#x…

数据结构——图的基础知识与其表示

一&#xff1a;定义 由顶点的集合和边的集合组成&#xff1b;常以 G(V,E) 表示&#xff0c;G 代表图&#xff0c;V代表 顶点的集合&#xff0c;E代表边的集合&#xff1b; 如图&#xff1a; 在G1图中&#xff0c;有 0~4 五个顶点&#xff0c;有 0-1&#xff0c;0-2&…

专题五_位运算(2)

目录 面试题 01.01. 判定字符是否唯一 解析 题解 268. 丢失的数字 解析 题解 371. 两整数之和 解析 题解 面试题 01.01. 判定字符是否唯一 面试题 01.01. 判定字符是否唯一 - 力扣&#xff08;LeetCode&#xff09; 解析 题解 class Solution { public:bool isUnique…

Ubuntu上使用audit2allow解决Android Selinux问题

1.安装工具 sudo apt install policycoreutils 2.运行命令 提前用dmesg或者串口抓取kernel log 遇到错误&#xff0c;提示需要用-p指定policy file&#xff0c;然偶尝试创建一个policy空文件&#xff0c;用-p选项&#xff0c;遇到如下错误 3.规避问题 首先跟进错误log的堆栈…

面试集中营—Spring篇

Spring 框架的好处 1、轻量&#xff1a;spring是轻量的&#xff0c;基本的版本大约2MB&#xff1b; 2、IOC&#xff1a;控制反转&#xff0c;Spring的IOC机制使得对象之间的依赖不再需要我们自己来控制了&#xff0c;而是由容易来控制&#xff0c;一个字&#xff1a;爽&#xf…

leetcode-有重复数字的全排列-98

题目要求 思路 1.同【没有重复项的全排列-97】这个题一样&#xff0c;都是递归的题&#xff0c;区别在于这个可能会包含重复的数字&#xff0c;因此&#xff0c;不能只是简单的通过两个值是否相等然后用标志位标记&#xff0c;而是新增了一个数组&#xff0c;这个数组专门用于…

libevent的使用

文章目录 libevent封装的框架思想常用函数分析使用fifo的读写未决和非未决bufferevent特性bufferevent函数客户端和服务器连接和监听libevent实现socket通信 libevent封装的框架思想 libevent框架&#xff1a;1. 创建 event_base (乐高底座)2. 创建 事件evnet 3. 将事件 添加…

【C++练级之路】【Lv.20】位图和布隆过滤器(揭开大数据背后的神秘面纱)

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、位图1.1 位图的概念1.2 位图的优势1.3 位图的模拟实现1.3.1 成员变量与默认成员函数1.3.2 test1.3.3…

AI智能分析视频监控行业的发展趋势和市场发展浅析

监控视频AI智能分析技术的现状呈现出蓬勃发展的态势&#xff0c;这一技术源于计算机视觉和人工智能的研究&#xff0c;旨在将图像与事件描述之间建立映射关系&#xff0c;使计算机能够从视频图像中分辨出目标信息。 在技术上&#xff0c;监控视频AI智能分析技术已经实现了对视…

Jenkins 2.164.3 安装插件(当前官网正式版本: 2.440.3 LTS)

Jenkins 2.164.3安装插件 1. 安装jenkins1.1 宿主机安装1.2 docker安装(linux) 2. 登录jenkins3. 修改配置文件 这篇文章如果放在5、6年前写出来毫无意义&#xff0c;因为安装2.164.3之后&#xff0c;推荐的插件即可自动安装。但是在2024年&#xff0c;当前正式版本是2.440.3 L…

【论文阅读】 Loss Functions for Image Restoration with Neural Networks

Loss Functions for Image Restoration with Neural Networks 论文地址摘要I. 引言II 相关工作用于图像恢复的神经网络B 找到更好的解决方案。 三、图像恢复的损失层A. l1 错误 The l1 ErrorB. SSIMC. MS-SSIMD. The Best of Both Worlds: MS-SSIM L1 四、结果A. Joint Denois…

四化智造MES(WEB)对接打通金蝶云星空余料入库查询(入库记录查询)接口与生产退料单新增接口

四化智造MES&#xff08;WEB&#xff09;对接打通金蝶云星空余料入库查询&#xff08;入库记录查询&#xff09;接口与生产退料单新增接口 接通系统&#xff1a;四化智造MES&#xff08;WEB&#xff09; “MES助力智能制造过程控制:MES管理生产订单的整个生产流程,通过对生产过…

npm install 及使用cordova打包常见错误大全(附解决方案)

问题1、cb() 这是我们在install过程中最最常见问题&#xff0c;网络上的解决方式也都是大同小异&#xff0c;要么就是升级node(误人子弟)&#xff0c;项目里的node是不可以随意升级的&#xff0c;它有可能会导致其他依赖又不适配&#xff0c;起始很多时候就是由于咱们配置的镜像…

Linux基础之git与调试工具gdb

目录 一、git的简单介绍和使用方法 1.1 git的介绍 1.2 git的使用方法 1.2.1 三板斧之git add 1.2.2 三板斧之git commit 1.2.3 三板斧之git push 二、gdb的介绍和一些基本使用方法 2.1 背景介绍 2.2 基本的使用方法 一、git的简单介绍和使用方法 1.1 git的介绍 Git是一…