微服务远程调用参数莫名丢失

1)问题描述

服务A ServiceA 远程调用服务B ServiceB(HttpClient/RestTemplet/Fegin等方式)

调用参数简单描述如下

//组装参数,大致如下,忽略处理细节
Customer c1 = new Customer();
Map map1 = new HashMap();
map1.put('user1',c1);

Customer c2 = new Customer();
Map map2 = new HashMap();
map2.put('user2',c2);

Map map3 = new HashMap();
map3.putAll(map2);

List paramList = new ArrayList();
paramList.add(map1);
paramList.add(map2);
paramList.add(map3);

//发起调用
ServiceA.invokeServiceB(paramList);

2)问题现象

调用至 ServiceB,接收的参数中 paramList 第三个元素为null,莫名缺失。

3)原因分析

i)Java远程调用(HttpClient/RestTemplet/Feign等等)无论通过何种方式调用,最终都是需要序列化后,以字节的形式传输。

i)Java对象序列化时,若包含重复的对象(相同引用),则只会序列化一次,其他的仅使用句柄。

i)Map.putAll()方法是浅拷贝。

结合以上三点,问题根源解释如下:

入参 map3是通过map.putAll()赋值,因此map3\map2 实际都是同一个对象 Customer c2,

paramList 序列化时,只会处理 map1,map2。map3不会做序列化,仅使用map2的句柄。

所以,ServiceB 处理请求,对参数反序列化时,由于map3为句柄,且ServiceB中未定义Customer对象,从而无法反序列化,导致参数缺失。

4)解决方案

远程调用时,若同一个对象可能出现多次,则需通过深拷贝创建新的对象,再进行调用。本例修改示意如下:

//组装参数,大致如下,忽略处理细节
Customer c1 = new Customer();
Map map1 = new HashMap();
map1.put('user1',c1);

Customer c2 = new Customer();
Map map2 = new HashMap();
map2.put('user2',c2);

//利用JSONObject实现深拷贝
Map map3 = JSONObeject.parseObject( JSONObject.toJSONString(map2),Map.class );

List paramList = new ArrayList();
paramList.add(map1);
paramList.add(map2);
paramList.add(map3);

//发起调用
ServiceA.invokeServiceB(paramList);

5)问题延申

问题1. 假若入参paramList中Map存放的不是自定义对象,而是int、String等类型,如下方式能正常反序列化吗?

//组装参数,大致如下,忽略处理细节
Map map1 = new HashMap();
map1.put('age',20);
map1.put('addr',"北京");

Map map2 = new HashMap();
map2.put('age',30);
map2.put('addr',"上海");

List paramList = new ArrayList();
paramList.add(map1);
paramList.add(map2);
paramList.add(map2);

//发起调用
ServiceA.invokeServiceB(paramList);

问题2. 假若在ServiceA,ServiceB中各自定义了Customer对象,且属性完全一致。(使用浅拷贝传参)最终能正常反序列化吗?

问题3. 假若自定义对象Customer 定义在公共组件common中,ServiceA,ServiceB均引用了common组件。(使用浅拷贝传参)最终能正常反序列化吗?

你可能感兴趣的:(java,基础,java,微服务,1024程序员节)