Dubbo RPC开发中的序列化问题:深度解析反序列化导致的HashMap异常
在使用Dubbo RPC进行开发时,我们可能会遇到一些出乎意料的问题。其中之一就是在进行远程调用时,内部嵌套对象出现与预期不符的HashMap。这个问题的根源在于反序列化过程中找不到对象,导致解析成了HashMap。在这篇博客中,我们将深入分析这个问题,并通过调试序列化和反序列化的代码来理解其原因。
问题描述
在我们的项目中,有一次我们遇到了这样一个问题:在进行Dubbo RPC调用时,我们发现返回的结果中,一些内部嵌套的对象被转换成了HashMap,而不是我们预期的类型。这个问题在我们的单元测试中没有出现,只在实际的RPC调用中发生。我们希望处理的响应结果是一个List<JobListRpcResponse>
,但实际上我们得到的却是一个包含HashMap的List。
解决办法
解决这个问题的方法是手动进行类型转换。我们可以先将对象序列化为JSON字符串,然后再将JSON字符串反序列化为我们需要的类型。如下所示:
List<JobListRpcResponse> jobListRpcResponseList = JSON.parseArray(JSON.toJSONString(jobRpcResponseList), JobListRpcResponse.class);
这样,我们就可以得到我们需要的List<JobListRpcResponse>
对象了。
问题本质
问题的本质在于反序列化过程中找不到对象,导致解析成了HashMap。在Dubbo中,序列化和反序列化是通过Hessian库来完成的。Hessian在反序列化对象时,如果找不到对象的类型,就会将对象解析为HashMap。
分析SerializerFactory关键源码
为了理解这个问题,我们需要深入理解Hessian库是如何获取对象的反序列化器的。这个过程是通过SerializerFactory
类的getDeserializer
方法来完成的。
4 public Deserializer getDeserializer(String type) throws HessianProtocolException { 5 if (type != null && !type.equals("") && !this._typeNotFoundDeserializerMap.containsKey(type)) { 6 if (this._cachedTypeDeserializerMap != null) { 7 Deserializer deserializer = (Deserializer)this._cachedTypeDeserializerMap.get(type); 8 if (deserializer != null) { 9 return deserializer; 10 } 11 } 12 13 在这段代码中,首先检查传入的类型(`type`)是否为空,是否为空字符串,以及该类型是否已经在无法找到反序列化器的map(`_typeNotFoundDeserializerMap`)中。如果类型是有效的,并且没有在无法找到反序列化器的map中,那么就尝试从缓存的反序列化器map(`_cachedTypeDeserializerMap`)中获取该类型的反序列化器。如果能够从缓存中获取到反序列化器,那么就直接返回。 14 15 ```java 16 Deserializer deserializer = (Deserializer)_staticTypeMap.get(type); 17 if (deserializer != null) { 18 return (Deserializer)deserializer; 19 } else { 20 ``` 21 如果缓存中没有该类型的反序列化器,那么就尝试从静态类型map(`_staticTypeMap`)中获取反序列化器。静态类型map中存储的是一些预定义的类型和对应的反序列化器。如果能够从静态类型map中获取到反序列化器,那么就直接返回。 22 23 ```java 24 if (type.startsWith("[")) { 25 Deserializer subDeserializer = this.getDeserializer(type.substring(1)); 26 if (subDeserializer != null) { 27 deserializer = new ArrayDeserializer(subDeserializer.getType()); 28 } else { 29 deserializer = new ArrayDeserializer(Object.class); 30 } 31 } else if (_unrecognizedTypeCache.get(type) == null) { 32 ``` 33 如果类型是一个数组类型(以"["开头),那么就尝试获取数组元素类型的反序列化器。如果能够获取到数组元素类型的反序列化器,那么就创建一个新的数组反序列化器。否则,就创建一个Object类型的数组反序列化器。 34 35 ```java 36 try { 37 Class cl = this.loadSerializedClass(type); 38 deserializer = this.getDeserializer(cl); 39 } catch (Exception var4) { 40 log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + this._loader + ":\n" + var4); 41 this._typeNotFoundDeserializerMap.put(type, PRESENT); 42 log.log(Level.FINER, var4.toString(), var4); 43 _unrecognizedTypeCache.put(type, new AtomicLong(1L)); 44 } 45 } else { 46 ((AtomicLong)_unrecognizedTypeCache.get(type)).incrementAndGet(); 47 if (((AtomicLong)_unrecognizedTypeCache.get(type)).get() % 2000L == 0L) { 48 ((AtomicLong)_unrecognizedTypeCache.get(type)).getAndSet(1L); 49 } 50 } 51 ``` 52 如果类型不是数组类型,那么就尝试加载该类型的类,并获取该类的反序列化器。如果加载类或获取反序列化器失败,那么就将该类型加入到无法找到反序列化器的map中,并记录警告日志。同时,将该类型加入到未识别类型缓存(`_unrecognizedTypeCache`)中,并设置计数为1。如果该类型已经在未识别类型缓存中,那么就增加计数。如果计数达到2000,那么就重置计数为1。 53 54 ```java 55 if (deserializer != null) { 56 if (this._cachedTypeDeserializerMap == null) { 57 this._cachedTypeDeserializerMap = new ConcurrentHashMap(8); 58 } 59 60 this._cachedTypeDeserializerMap.put(type, deserializer); 61 } 62 63 return (Deserializer)deserializer; 64 } 65 } else { 66 return null; 67 } 68 } 69 ``` 70 最后,如果能够获取到反序列化器,那么就将反序列化器加入到缓存的反序列化器map中。然后返回反序列化器。如果类型无效,或者在无法找到反序列化器的map中,那么就返回null。 71 72 这段代码的关键在于,如果无法找到对应类型的反序列化器,就会将类型加入到无法找到反序列化器的map中。这就是我们在Dubbo RPC调用中,如果无法找到对象的类型,就会将对象解析为HashMap的原因。
结论
在使用Dubbo RPC进行开发时,我们需要注意序列化和反序列化过程中可能出现的问题。特别是当我们在RPC调用中传递复杂的对象时,我们需要确保我们正确地进行类型转换,以防止类型信息丢失。同时,我们也需要深入理解Hessian库的工作原理,以便在遇到问题时,能够快速地找到问题的根源。
标签:Dubbo,null,HashMap,类型,deserializer,序列化,type From: https://www.cnblogs.com/hld123/p/17822933.html