Spring Cloud用OpenFeign做遠程調用的話要注意返回值類型能不能被反序列化

每次從零開發一個服務的時候總是遇到各種各樣奇怪的問題,今天又從Spring Initializr開始開發一個服務。抄自己的不算抄,本着CV大法優先的原則,從之前其他項目中拷貝了幾個component,比如響應Entity封裝的類型啊,自定義異常枚舉啊這些,如圖:

壞就壞在這些不是從Spring Cloud項目中拷過來的,渾然不知已經埋下了坑還嘻嘻哈哈地寫了一個client,就是一個簡單的用OpenFeign調用一個遠程服務,如圖:

可以看到這裏給響應Entity封裝了一個HikResponse類型,而這個類就是上面拷貝過來的。服務一起方法一調用,報錯了......

錯誤信息如下:

報錯倒是挺明白的,說不能構造HikResponse,是因爲沒有默認構造器,也就是無參構造器。因爲項目中用了lombok,原先HikResponse是這樣的:

確實沒有無參構造器,那就加一個無參構造器就是了,如下:

這樣問題確實解決了!但是好奇心想讓我找出根本原因。

根據異常棧信息可以定位出是哪一行報的錯,即DeserializationContext.handleMissingInstantiator()方法,在報錯的地方打了一個斷點:

看來是valueInst.canInstantiate()這個方法沒有校驗通過,通過斷點信息可以看到這個valueInst的類型其實就是HikResponse,那也就對上了,就是這個HikResponse不能被實例,可以看看這個canInstantiate()方法,看看什麼才能算是能夠被實例化:

反正都是無參的,第一個就是有默認構造器。

源碼看到這裏我還是有點疑惑,腦子裏想到了之前用的HttpClient,同樣都是調用遠程服務的接口,爲啥HttpClient沒有出現這個問題呢,看了一下以前寫的HttpClient代碼:

其實我們用httpClient.execute()執行調用請求的時候,返回值直接就是Response,這個時候響應還沒有被解析,是需要我們自己去解析的,而我們一般都是用InputStream去解析。

但是OpenFeign不是,OpenFeign調用遠程服務,我們調用的方法返回值直接就是解析好的對象,例如這裏就是ResponseEntity<HikResponse>,從二進制到對象,那這就必然涉及到一個反序列化的過程,反序列化的過程中是需要類可以被實例化,也就是剛剛的canInstantiate()方法。

這樣就串起來理解了,最後可以通過貼源碼的方式看一下OpenFeign調用遠程服務的過程,加深一下理解:

從excutedAndDecode()方法開始看,這裏算是開始調用遠程服務了,template就是遠程服務的地址,可以看出路由還是服務名,說明還沒有經過負載均衡到具體的服務節點,options就是請求的一下參數,比如超時時間等。看excutedAndDecode()方法:

這裏的this.client.execute()是真的開始調用遠程服務了,可以看到這個this.client的實例是LoadBalancerFeignClient,它是org.springframework.cloud.openfeign.ribbon包裏面的,說明這裏的負載均衡是用的ribbon。其實有兩種負載均衡的實現,可以看一下這個繼承關係圖:

 而另一個FeignBlockingLoadBalancerClient是在org.springframework.cloud.openfeign.loadbalancer包裏面的,這是兩種負載均衡的實現,關於loadbalancer和ribbon的比較可以參考這篇博客(https://blog.csdn.net/qq_16063307/article/details/99937060)。繼續往下看,這裏execute後是拿到響應Response了,然後開始handleResponse(),看一下這個方法:

 就是這裏,開始對響應反序列化了,是一個二進制到對象的過程,需要類可以被實例化,然後就到了最初報錯的地方,之後的代碼就不截圖了。

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