標題:Dubbo RPC開發中的序列化問題:深度解析反序列化導致的HashMap異常

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庫的工作原理,以便在遇到問題時,能夠快速地找到問題的根源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章