API網(wǎng)關(guān)泛化調(diào)用
可能大部分人第一次在軟件開發(fā)中接觸到“泛化”這個詞是在學(xué)習(xí)UML的過程中秸歧,泛化是UML所述的四中關(guān)系(泛化關(guān)系灾锯、實現(xiàn)關(guān)系葬凳、依賴關(guān)系绰垂、關(guān)聯(lián)關(guān)系)中的一種。泛化關(guān)系指的是類與類火焰、接口與接口之間的繼承關(guān)系劲装,UML中用帶箭頭的實線表示,如下:
本文中講到的泛化是一個動作荐健,可以理解為“變的寬泛”酱畅,由具體到抽象,由特殊到一般的一個動作實現(xiàn)江场》乃幔可以先簡單理解為就是沒有了具體的POJO,統(tǒng)一使用Map來封裝對象址否。為什么說泛化調(diào)用時網(wǎng)關(guān)的基石呢餐蔬?原因之一也在于此,使用者在調(diào)用提供者接口時佑附,不再需要依賴服務(wù)提供方客戶端的JAR包樊诺,因此也就沒有了POJO,通過泛化的方式進(jìn)行遠(yuǎn)程調(diào)用音同。
一般情況下我們需要通過RPC調(diào)用接口提供方的服務(wù)词爬,首先在消費端嵌入提供方的Jar包,從而使用Jar包中的類和方法权均。那對于網(wǎng)關(guān)服務(wù)來說顿膨,如果一個網(wǎng)關(guān)調(diào)用了N個服務(wù),那就需要引入N個Jar依賴叽赊,這樣網(wǎng)關(guān)系統(tǒng)難以維護(hù)恋沃,如下:
在網(wǎng)關(guān)系統(tǒng)中,我們需要另外一種方式實現(xiàn)調(diào)用必指,這就是泛化調(diào)用囊咏。這種方式不在需要服務(wù)提供方提供jar包便可完成RPC調(diào)用,其中的原理跟普通的RPC調(diào)用時一致的,網(wǎng)絡(luò)梅割、序列化霜第、反射這些底層的技術(shù)原理一致。區(qū)別在于參數(shù)和返回值都用Map來表示户辞。通過GenericService來調(diào)用所有的服務(wù)實現(xiàn)庶诡。任何一個成熟的RPC框架都會支持泛化調(diào)用,比如阿里巴巴優(yōu)秀的RPC框架Dubbo官網(wǎng)提供的泛化為例咆课,示例代碼:
import org.apache.dubbo.rpc.service.GenericService;
...
// 引用遠(yuǎn)程服務(wù)
// 該實例很重量,里面封裝了所有與注冊中心及服務(wù)提供方連接扯俱,請緩存
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
// 弱類型接口名
reference.setInterface("com.xxx.XxxService");
reference.setVersion("1.0.0");
// 聲明為泛化接口
reference.setGeneric(true);
// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用
GenericService genericService = reference.get();
// 基本類型以及Date,List,Map等不需要轉(zhuǎn)換书蚪,直接調(diào)用
Object result = genericService.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"});
// 用Map表示POJO參數(shù),如果返回值為POJO也將自動轉(zhuǎn)成Map
Map<String, Object> person = new HashMap<String, Object>();
person.put("name", "xxx");
person.put("password", "yyy");
// 如果返回POJO將自動轉(zhuǎn)成Map
Object result = genericService.$invoke("findPerson", new String[]
{"com.xxx.Person"}, new Object[]{person});
...
使用泛化調(diào)用的網(wǎng)關(guān)系統(tǒng)只需要繼承RPC框架基礎(chǔ)的一個JAR包即可迅栅,其余的接口都通過泛化來調(diào)用服務(wù)的實現(xiàn)殊校,這樣無論網(wǎng)關(guān)系統(tǒng)承載多少個接口,都不需要引入多余的JAR包了读存,結(jié)構(gòu)如下:
這樣看來为流,泛化調(diào)用并不是一種很神秘的技術(shù),它仍然實在基于RPC底層通信的基礎(chǔ)之上進(jìn)行調(diào)用的让簿,跟普通RPC區(qū)別在于不在依賴多個服務(wù)提供方客戶端JAR包了敬察,入?yún)⒑统鰠⒕拖馜ubbo官方示例代碼中的一樣,完全用Map來代替尔当。泛化在RPC中出現(xiàn)的概念來與此莲祸,從具體到抽象,不在需要具體的POJO對象椭迎。
如何發(fā)布API接口到網(wǎng)關(guān)
網(wǎng)關(guān)系統(tǒng)與RPC環(huán)境起初是兩個環(huán)境的事務(wù)锐帜,網(wǎng)關(guān)系統(tǒng)不需要依賴RPC的存在,各有各的生命周期:
在RPC環(huán)境下畜号,調(diào)用者編寫好各自的業(yè)務(wù)邏輯代碼缴阎,通過提供者的客戶端JAR包調(diào)用提供者的服務(wù),注冊中心負(fù)責(zé)同步數(shù)據(jù)简软,如下圖所示蛮拔。這個時候網(wǎng)關(guān)系統(tǒng)跟RPC服務(wù)是沒有聯(lián)系的。
現(xiàn)在要把API網(wǎng)關(guān)暴漏給外部使用者替饿,我們通過前面介紹的泛化調(diào)用技術(shù)语泽,以不需要引入API客戶端的JAR包的方式調(diào)用服務(wù)提供者,如下圖所示:
現(xiàn)在的問題是如何把API接口發(fā)布到網(wǎng)關(guān)系統(tǒng)中视卢。實際問題是需要利用什么方法將RPC環(huán)境下的API接口讓網(wǎng)關(guān)也能識別識別到踱卵,剩余的工作還是交給RPC本身去完成,包括編解碼、序列化惋砂、反序列化妒挎、長連接等。
有了泛化調(diào)用作為基礎(chǔ)支持西饵,我們需要做的就是將API通過一種方式存儲到網(wǎng)關(guān)系統(tǒng)能夠訪問的一種存儲中酝掩,為了提高系統(tǒng)的性能一般會選用Redis存儲。根據(jù)泛化調(diào)用的方式眷柔,網(wǎng)關(guān)系統(tǒng)需要知道服務(wù)的類名和方法名期虾。網(wǎng)關(guān)系統(tǒng)可以提供一個API發(fā)布平臺入口,讓API發(fā)布者將RPC環(huán)境下的API數(shù)據(jù)錄入到API發(fā)布平臺驯嘱。RPC本身就可以為官網(wǎng)系統(tǒng)提供一個獲取API信息的接口镶苞,API發(fā)布平臺可以在用戶輸入服務(wù)的類名之后直接通過這個接口獲取要發(fā)布的API整體信息,包括所有的方法鞠评、入?yún)⒚尽⒊鰠ⅰ⒆⑨屘昊稀⒚枋隽恰⒔涌谪?fù)責(zé)人信息等。剩余的工作就可以交給API網(wǎng)關(guān)的泛化調(diào)用邏輯了负乡,如下圖:
總結(jié)
本片文章主要介紹了API網(wǎng)關(guān)的基石:泛化調(diào)用牍白,以及通過如何發(fā)布API到API網(wǎng)關(guān)示例來說明為什么被稱之為基石。
文章內(nèi)容來源:《架構(gòu)修煉之道》
更多個人博客:http://www.wangqi94.com