Dubbo接口中出现复杂参数,调用异常问题处理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo接口中出现复杂参数,调用异常问题处理相关的知识,希望对你有一定的参考价值。
参考技术A 因业务需要,原有dubbo接口参数需要扩展,并且参数格式出现多层嵌套的情况,参数Bean格式如下:@Data
public class AddOrUpdateParam implements Serializable
private User user;
private List<ScopeParam> scopes;
ScopeParam定义如下:
@Data
public class ScopeParam implements Serializable
private int identityId =0;
private List<RegionDivision> regions
private List<Integer> ids;
RegionDivision的定义如下:
@Data
public class RegionDivision implements Serializable
private int groupID;
private int regionID;
private int regionType;
结果调用微服务接口时传递此参数,接口直接抛出404,纳尼, 为什么会是404? 经过查阅资料发现,dubbo参数可能太过复杂,出现了多增嵌套,导致参数解析异常,所以出现了404。既然是参数太过复杂, 那我就不传那么复杂了,直接用json格式试一下,于是直接修改定义:
private List<ScopeParam> scopes ;
改为:
private String regionJson;
然后在dubbo接口内部再进行参数解析:
List<ScopeParam> scopes =new ArrayList<>();
if (!StringUtils.isEmpty(param.getRegionJson()))
MapclassMap =new HashMap();
classMap.put("region", RegionDivision.class);
JSONArray jsonArray =JSONArray.fromObject(param.getRegionJson());
if (jsonArray !=null && !jsonArray.isEmpty())
for (int i =0; i<jsonArray.size(); ++i)
ScopeParam item = (ScopeParam)JSONObject
.toBean(jsonArray.getJSONObject(i), ScopeParam.class, classMap);
if (item !=null)
scopes.add(item);
然后再次调用,正常。
dubbo泛化调用使用及原理解析
参考技术A通常我们想调用别人的dubbo服务时,我们需要在项目中引入对应的jar包。而泛化调用的作用是,我们无需依赖相关jar包,也能调用到该服务。
这个特性一般使用在网关类项目中,在业务开发中基本不会使用。
假设我现在要调用下面的接口服务
在xml文件做以下配置
然后注入使用
在两种调用方式中,我们都需要使用被调用接口的字符串参数生成GenericService,通过GenericService的$invoke间接调用目标接口的接口。
$invoke的三个参数分别为,方法名,方法参数类型数组,方法参数数组。
可以看到泛化调用的一个复杂性在于$invoke的第三个参数的组装,下面介绍几种复杂入参的调用方式
首先丰富提供者接口
与入参相似,虽然$invoke的返回定义为Object,实际上针对不同类型有不同的返回。
泛化调用和直接调用在消费者者端,在 使用上的区别 是,我们调用服务时使用的接口为GenericService,方法为$invoker。在 底层的区别 是,消费者端发出的rpc报文发生了变化。
在使用上,不管哪种配置方式,我们都需要配置generic=true
设置generic=true后,RefereceConfig的interfaceClass会被强制设置为GenericService
这也使得我们的RefereanceBean返回的是GenericService类型的代理。
生成的代理是GenericService的代理只是我们使用方式上的变化,更为核心的是,底层发送的rpc报文发生了什么变化。
Dubbo的rpc报文分为header和body两部分。我们这边只需要关注body部分。构造逻辑如下
那么我们通过直接调用与泛化调用ByeService的bye方法在报文上有啥区别呢?
我一开始以为报文中的path是GenericeService,其实并没有,path就是我们调用的目标方法。
path来源???todo
而报文中的方法名,方法参数类型以及具体参数,还是按照GenericeService的$invoke方法入参传递的。
这么个二合一的报文,发送到提供者那边,它估计也会很懵逼,我应该怎么执行?
所以针对泛化调用报文还会把generic=true放在attchment中传递过去
具体逻辑在GenericImplFilter中。
GenericImplFilter中有很多其他逻辑,比如泛化调用使用的序列化协议,正常接口走泛化调用的模式,我们只需要设置attachment的那部分。
知道消费者端报文发生了什么变化,那么接下来就去看提供者端如何处理这个改造后的报文。
总结一下ReferenceConfig中interfaceClass和interfaceName的区别?(这道面试题好像不错)
interfaceClass用于指定生成代理的接口
interfaceName用于指定发送rpc报文中的path(告诉服务端我要调用那个服务)
消费者泛化调用的rpc报文传递到提供者还不能直接使用,虽然path是对的,但是实际的方法名,参数类型,参数要从rpc报文的参数中提取出来。
GenericFilter就是用来做这件事情。
在提供者这边,针对泛化调用的逻辑全部封装到了GenericFilter,解耦的非常好。
注意第4个条件,一开始很疑惑,后来发现rpc报文中的path是目标接口的,这边invoker.getInterface()返回的肯定就是实际接口了
这边有个疑问,为什么这边还要再次反序列化一次,netty不是有decoder么??
嗯,你别忘了,针对一个POJO你传过来是一个Map,从Map转换为POJO需要这边进一步处理。
这边需要注意一下!!针对接口的泛化调用,抛出的异常都会经过GenericException包装一下。
从功能上来看,泛化调用提供了在没有接口依赖情况下进行的解决方案,丰富框架的使用场景。
从设计上来看,泛化调用的功能还是通过扩展的方式实现的,侵入性不强,值得学习借鉴。
以上是关于Dubbo接口中出现复杂参数,调用异常问题处理的主要内容,如果未能解决你的问题,请参考以下文章