一條架構(gòu)師:打開API網(wǎng)關(guān)設(shè)計(jì)的一扇窗

內(nèi)容來源:2017年5月13日,一條架構(gòu)師管凱強(qiáng)在“Java開發(fā)者大會(huì) | Java之美【上海站】”進(jìn)行《API網(wǎng)關(guān)的設(shè)計(jì)思路及落地》演講分享。IT大咖說(ID:itdakashuo)作為獨(dú)家視頻合作方,經(jīng)主辦方和講者審閱授權(quán)發(fā)布。

閱讀字?jǐn)?shù):2640 | 4分鐘閱讀

嘉賓演講視頻及PPT:http://t.cn/RrZR11P

摘要

API網(wǎng)關(guān)是一個(gè)服務(wù)器,是系統(tǒng)的唯一入口。從面向?qū)ο笤O(shè)計(jì)的角度看,它與外觀模式類似。API網(wǎng)關(guān)封裝了系統(tǒng)內(nèi)部架構(gòu),為每個(gè)客戶端提供一個(gè)定制的API。

背景

移動(dòng)互聯(lián)網(wǎng)時(shí)代的挑戰(zhàn)

移動(dòng)互聯(lián)時(shí)代迭代速率對(duì)于后端開發(fā)帶來了一些挑戰(zhàn)。

在我們公司的商品詳情頁上,包含了商品信息、價(jià)格信息、促銷信息和推薦列表四個(gè)部分。

在開發(fā)過程中,我們想要把這四個(gè)信息在一個(gè)接口訪問中全部吐出來,嘗試給出一個(gè)萬能接口。

事實(shí)上,我們?cè)诘谝淮芜@樣做的時(shí)候或許是靠譜的,但是當(dāng)產(chǎn)品發(fā)生變更的時(shí)候,比如產(chǎn)品想把推薦列表換成熱銷榜,那么之前做的萬能接口就已經(jīng)不滿足這個(gè)業(yè)務(wù)場(chǎng)景了,只能新開一個(gè)V2版本。

這樣重復(fù)必然會(huì)導(dǎo)致接口的膨脹以及維護(hù)成本越來越高。

所以雖然我們都在追求一個(gè)萬能的解,但這個(gè)“解”也許并不存在。

微服務(wù)時(shí)代的挑戰(zhàn)

我們商品詳情頁的數(shù)據(jù)來自于商品系統(tǒng)、價(jià)格系統(tǒng)、推薦系統(tǒng)和營銷系統(tǒng)。而對(duì)于客戶端或用戶而言,其實(shí)沒有必要知道每個(gè)接口由哪個(gè)微服務(wù)提供的,只需得到數(shù)據(jù)即可。

所以我們面臨的問題就是怎樣避免讓客戶端感知微服務(wù)邊界的存在,不同的后端、前端團(tuán)隊(duì)需要統(tǒng)一的接口設(shè)計(jì)、接入規(guī)范。

需求

使用API網(wǎng)關(guān)構(gòu)建微服務(wù)

API網(wǎng)關(guān)是擋在所有微服務(wù)之前的一個(gè)透明層,是請(qǐng)求進(jìn)入系統(tǒng)的唯一節(jié)點(diǎn)。基于這一點(diǎn),一方面解決了對(duì)調(diào)用方隱藏微服務(wù)的系統(tǒng)邊界問題,另一方面負(fù)責(zé)服務(wù)請(qǐng)求路由及協(xié)議轉(zhuǎn)換。它可能還具有其它職責(zé),如身份驗(yàn)證、權(quán)限控制、負(fù)載均衡、“請(qǐng)求整形”與管理。

實(shí)踐落地

從HTTP到RPC

在我們的APP和WEB上,所有請(qǐng)求都基于HTTP,RPC服務(wù)是基于DUBBO的RPC框架來做。

API網(wǎng)關(guān)做的最簡(jiǎn)單的一件事就是能夠讓用戶發(fā)起的請(qǐng)求通過API網(wǎng)關(guān)轉(zhuǎn)成對(duì)RPC服務(wù)的調(diào)用,再回到用戶的APP上。

主要解決了HTTP請(qǐng)求到RPC調(diào)用實(shí)例的映射,以及把無類型的參數(shù)轉(zhuǎn)換為帶類型的參數(shù)。

實(shí)現(xiàn)了一個(gè)輕量級(jí)的MVC框架,將請(qǐng)求轉(zhuǎn)換為對(duì)RPC實(shí)例的調(diào)用。

從HTTP到RPC——定義好一個(gè)接口

我認(rèn)為一個(gè)設(shè)計(jì)良好的接口一定包含了明確的異常編碼,以及這個(gè)異常編碼在什么業(yè)務(wù)場(chǎng)景上出現(xiàn),這個(gè)異常編碼怎樣在客戶端得到合適的處理。

還需要有一個(gè)明確的調(diào)用權(quán)限說明,和清晰的參數(shù)列表、返回結(jié)果。

示例

如圖,這上面有一個(gè)ApiGroup,來描述業(yè)務(wù)模塊的屬性。

這是我們某個(gè)特定的方法,和control有些相似。包含了API的方案名、SecurityType是權(quán)限認(rèn)證的level。

Designed ErrorCode是用來對(duì)客戶端接口明確報(bào)出異常。

入?yún)⒚枋鲇蠥piAutowired和ApiParameter兩種類型。

從HTTP到RPC——API注冊(cè)

在API注冊(cè)流程中,我們首先做了ApiParser,用于解析API信息,解析完之后會(huì)得到接口的完備信息,包含了接口的描述及整個(gè)業(yè)務(wù)異常編碼的描述。

第二步是要?jiǎng)?chuàng)建一個(gè)RPC調(diào)用實(shí)例,也就是RpcInvoker。

接下來需要?jiǎng)?chuàng)建一個(gè)服務(wù)的動(dòng)態(tài)代理,來解決無類型參數(shù)到RPC接口上的有類型參數(shù)的轉(zhuǎn)換。

最后是把proxy做一個(gè)緩存。

從HTTP到RPC——proxy實(shí)例

這是一個(gè)動(dòng)態(tài)生成proxy的例子。

我們用ASM字節(jié)碼框架在運(yùn)行期動(dòng)態(tài)生成proxy。圖中函數(shù)是把string類型參數(shù)轉(zhuǎn)換為強(qiáng)類型參數(shù)。

從HTTP到RPC——處理請(qǐng)求

第一步解析請(qǐng)求,在URL中需要描述清楚HTTP調(diào)用哪些接口,解析請(qǐng)求就是要pass出這些信息。

然后要做安全驗(yàn)證,以保證什么樣的請(qǐng)求才可以被路由到微服務(wù)的請(qǐng)求。

第三步構(gòu)建API調(diào)用的上下文。上下文里包含了用戶ID等信息,這些信息來自于用戶訪問令牌解密之后。

接下來就是做代理的執(zhí)行,最后對(duì)結(jié)果做序列化。

從HTTP到RPC——安全策略

做安全策略的大體上的處理方法就是設(shè)備識(shí)別、數(shù)字簽名,也包括HTTPS。

設(shè)備識(shí)別主要是用于確認(rèn)身份,依賴于一個(gè)叫token的訪問令牌來做。

在APP上用戶如果發(fā)起了登錄,首先經(jīng)過網(wǎng)關(guān),然后到用戶服務(wù),登錄完成后下發(fā)appSecret和token。

APP在調(diào)用API之前要做數(shù)字簽名,需要一個(gè)簽名的密鑰,也就是下發(fā)的appSecret。

API到了網(wǎng)關(guān),網(wǎng)關(guān)可以解析出用戶身份對(duì)應(yīng)的appSecret,所以它可以驗(yàn)證這個(gè)請(qǐng)求是不是正常的請(qǐng)求。驗(yàn)證通過后就到了業(yè)務(wù)系統(tǒng)。

中間人攻擊是在APP發(fā)起請(qǐng)求離開設(shè)備以后,在網(wǎng)絡(luò)傳輸過程中如果被第三方竊聽,它會(huì)去嘗試篡改請(qǐng)求。

中間人竊取到網(wǎng)絡(luò)傳輸中的請(qǐng)求報(bào)文,雖然獲得了token等關(guān)鍵信息,但由于簽名密鑰appSecret無法通過監(jiān)聽網(wǎng)絡(luò)請(qǐng)求獲得,所以中間人篡改請(qǐng)求卻無法得到一個(gè)正確的數(shù)字簽名。

組合式調(diào)用

雖然萬能接口是不存在的,但我們嘗試實(shí)現(xiàn)減輕一個(gè)接口中同時(shí)返回來自于不同微服務(wù)的信息的需求?;谶@個(gè)需求,我們?cè)贏PI網(wǎng)關(guān)擴(kuò)展出一個(gè)組合式調(diào)用的協(xié)議。

簡(jiǎn)單來說,這個(gè)協(xié)議是在一次HTTP請(qǐng)求中對(duì)RPC服務(wù)發(fā)起多次調(diào)用,在API網(wǎng)關(guān)做響應(yīng)報(bào)文的整合,最后做返回。

組合式調(diào)用——Fa?ade設(shè)計(jì)

用Fa?ade設(shè)計(jì)模式為多個(gè)RPC服務(wù)的接口做了Fa?ade,在Fa?ade上把API做組裝,統(tǒng)一暴露給客戶端,讓API網(wǎng)關(guān)成為API的Fa?ade。

但我們之前嘗試做的萬能接口跟不上需求的變化,最終它帶來的惡果就是代碼難以維護(hù)。如果在API網(wǎng)關(guān)上去不停地為接口做Fa?ade,API網(wǎng)關(guān)的代碼必然也是很難維護(hù)的。

為了解決這個(gè)問題,我們讓客戶端去定義Fa?ade,API網(wǎng)關(guān)只負(fù)責(zé)組裝。

組合式調(diào)用——并行處理

使用異步并行方式,將組合式調(diào)用串行變并行。

首先是請(qǐng)求前置處理,實(shí)際調(diào)用則采用了dubbo async調(diào)用,是dubbo原生的調(diào)用方式。最后做請(qǐng)求后置處理。

但是Dubbo異步調(diào)用標(biāo)識(shí)會(huì)通過attachment向下傳遞,污染調(diào)用鏈,導(dǎo)致后續(xù)調(diào)用鏈路都變成了異步方式。而且dubbo filter鏈機(jī)制在異步調(diào)用中具有局限性。

這兩個(gè)問題都是最后改了dubbo的源碼來解決的。

降低接入門檻

提供在線API文檔和接入SDK。

但是變化總是存在,我們?cè)撊绾谓夥派a(chǎn)力呢?

擴(kuò)大API的影響力,基于API信息生成敏捷開發(fā)工具;強(qiáng)類型約束的SDK,及時(shí)暴露違背“契約”的行為。

這是我們做的一些敏捷開發(fā)的工具。

這是在線文檔工具和調(diào)試工具。

定位請(qǐng)求

Dubbo attachment是一種隱式傳參機(jī)制,具有傳遞性。

基于這個(gè)機(jī)制可以把請(qǐng)求標(biāo)識(shí)在調(diào)用鏈路上一直做傳遞,調(diào)用鏈路上增加AOP、向日志上下文中傳遞traceid。

請(qǐng)求定位——elk+zabbix

在API網(wǎng)關(guān)落下的所有請(qǐng)求日志都會(huì)有相應(yīng)的錯(cuò)誤編碼,可以做zabbix基于這個(gè)服務(wù)的遠(yuǎn)程報(bào)錯(cuò)。

我理解的API網(wǎng)關(guān)

從技術(shù)設(shè)計(jì)的角度上來說,API是一種抽象,它隔離了我們的使用以及實(shí)現(xiàn);從開發(fā)管理的角度上來說,API是一種契約。

API網(wǎng)關(guān)是一種微服務(wù)的架構(gòu)解決方案,服務(wù)于API“契約”精神,并盡可能的擴(kuò)大這種契約的影響力,構(gòu)建一種圍繞API開發(fā)的“生態(tài)”。

展望

API網(wǎng)關(guān)前面是HTTP的傳輸,在這過程中不一定需要中心化的存儲(chǔ),可以嘗試用CDN來做邊界緩存。

熱發(fā)布服務(wù)于所有微服務(wù),微服務(wù)接口的變更怎樣做到熱重啟,是一個(gè)比較有挑戰(zhàn)性的事情。

我們之前做的事情只是日志定位,談不上調(diào)用鏈路跟蹤,調(diào)用鏈路跟蹤有更專業(yè)的解決方案。

API網(wǎng)關(guān)目前做的只是權(quán)限驗(yàn)證,還沒有和風(fēng)控系統(tǒng)結(jié)合起來。

限流降級(jí)也還沒有做。

在網(wǎng)關(guān)上做ab測(cè)試會(huì)是比較有意義的。

我今天的分享就到這里,感謝聆聽!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容