前言
最近在做一個權(quán)限控制的功能,其中一項服務(wù)是對用戶進行凍結(jié),具體的業(yè)務(wù)邏輯不一定合適細講,就以“把大象裝進冰箱”來抽象代替。其有如下流程:
1.把冰箱門打開
2.把大象放進去
3.把冰箱門關(guān)上
需要依賴兩個服務(wù)提供方:
1.冰箱管理員(開門、關(guān)門)
2.搬運公司(搬大象進冰箱)
那么該如何設(shè)計服務(wù)的調(diào)用模式呢?
基于“標準”的調(diào)用
上古時代,服務(wù)提供方、和調(diào)用方會一起約定一個入?yún)⒊鰠⒌膱笪臉藴剩▁ml、json),根據(jù)這個約定,服務(wù)提供方發(fā)布http服務(wù),調(diào)用方也根據(jù)這個入?yún)⒊鰠藴蕘碚{(diào)用提供方的http服務(wù)。這里的入?yún)⒊鰠⒓s定,以及http協(xié)議本身,都是一種標準。即雙方以一種共同遵守的標準,來組織服務(wù)調(diào)用關(guān)系。

基于API的調(diào)用
微服務(wù)時代,服務(wù)提供方對外暴露自己的API接口,隱藏具體實現(xiàn)邏輯。服務(wù)調(diào)用方引入對方的API之后,即可調(diào)用對方的服務(wù)。以API依賴的方式組織服務(wù)調(diào)用關(guān)系。

基于SPI的調(diào)用
中臺時代,提倡大中臺,小前臺。中臺負責實現(xiàn)“把大象裝進冰箱”這個業(yè)務(wù)涉及的可復(fù)用的流程和能力,預(yù)留出對應(yīng)能力的SPI拓展點,業(yè)務(wù)方通過實現(xiàn)對應(yīng)的spi拓展點來提供差異服務(wù)。比如有些業(yè)務(wù)希望使用搬運工把大象搬進冰箱,另外一些業(yè)務(wù)希望使用吊車把大象搬進冰箱,通過在spi拓展點中實現(xiàn)不同的邏輯,即可達成這一點。

三者本質(zhì)區(qū)別
這里引用 張群輝(輝子) 的定義
1.“標準”即雙方共同遵守的協(xié)議。(比如http協(xié)議,或者http調(diào)用中的入?yún)ⅰ⒊鰠⒍x?;谙⒔怦钇鋵嵰矊儆谶@一范疇)
2.接口實現(xiàn)者擁有接口的是API模式 ,一個API一般只有一種實現(xiàn),提供一種能力。(比如搬運公司提供搬大象進冰箱的能力)
3.使用接口者擁有接口的是SPI模式,使用接口者先定義接口,實現(xiàn)者根據(jù)接口定義來實現(xiàn)具體邏輯,一種SPI可以有多個實現(xiàn)。(比如中臺架構(gòu)定義的把大象搬進冰箱SPI拓展點,可以實現(xiàn)為搬運工搬,也可以實現(xiàn)為吊車搬)
三者取舍
我所開發(fā)的權(quán)限控制功能,完全沒有可能成為一個權(quán)限控制中臺,加上中臺的概念現(xiàn)在基本也快涼了,所有首先放棄中臺SPI拓展的設(shè)計模式。
那么如果采用API依賴模式,將會是如下情況.
1.服務(wù)提供方發(fā)布自己的API
/***
* 搬運公司
*/
public interface MovingCompany{
void move(Elephant elephant);
}
/***
* 冰箱管理員
*/
public interface RefrigeratorManager{
void open();
void close();
}
2.調(diào)用方引入對方服務(wù),然后調(diào)用,以實現(xiàn)把大象裝進冰箱
void putElephant2Refrigerator(elephant){
refrigeratorManager.open();//把冰箱門打開
movingCompany.move(elephant);//把大象放進去
refrigeratorManager.close();//把冰箱門關(guān)上
}
這種調(diào)用模式有什么問題呢?
1.如果有一天,業(yè)務(wù)要求把大象裝進冰箱之前,還得清洗一下大象,那么我需要修改代碼,引入一個清洗大象的服務(wù),然后發(fā)布生效。
2.如果有一天,國外一個公司覺得這個“把大象裝進冰箱”的功能很好,希望購買這個服務(wù),部署到他們公司去,那也搞不定,因為這個功能強依賴了我當前引入的冰箱管理員和搬運公司,我需要改代碼,把依賴的冰箱管理員和搬運公司換成他們當?shù)氐摹?/p>
最終,“把大象裝進冰箱”功能使用了基于“標準”的調(diào)用模式。
1.所有需要參與到“把大象裝進冰箱”的服務(wù)提供方,都按照標準(入?yún)⒘斜砗统鰠ⅲ崿F(xiàn)下述接口來提供服務(wù)(接口名、方法名隨意)
Result<Void> putElephant(String xxxId,String xxxType,String ext);
2.服務(wù)節(jié)點提供服務(wù)元數(shù)據(jù),落配置庫,用來進行后續(xù)服務(wù)初始化
{
"operateType":"XXX",
"order":"1",
"interface":"com.xxx.service",
"method":"putElephant",
"Url":"xxx.xxx.xxx.net:12220",
"uniqueId":"xxx",
"argTypes":[
"java.lang.String",
"java.lang.String",
"java.lang.String"
],
"description":"把大象搬進冰箱"
}
3.服務(wù)執(zhí)行時,讀取配置庫,根據(jù)服務(wù)節(jié)點元數(shù)據(jù)的順序,動態(tài)初始化服務(wù),泛化調(diào)用服務(wù)節(jié)點(dubbo泛化調(diào)用、sofa泛化調(diào)用等)。

回到API調(diào)用模式的兩個問題點,這種模式有如下好處。
1.參與“把大象裝進冰箱”的服務(wù)節(jié)點全部配置在數(shù)據(jù)庫,執(zhí)行時動態(tài)初始化。在應(yīng)對“把大象放進去之前,先清洗一下大象”這種業(yè)務(wù)變動時,只需要將清洗大象的服務(wù)節(jié)點在配置庫中進行配置即可,不需要應(yīng)用發(fā)布。
2.當需要SAAS化,部署到一個全新環(huán)境時,數(shù)據(jù)模型和代碼邏輯都是可復(fù)用的,只需要把參與服務(wù)編排的節(jié)點配置替換為對方需要的即可,比如要把冰箱管理員和搬運公司替換為對方當?shù)氐墓荆ㄐ碌姆?wù)也必須遵守接口標準),只需要做配置庫的變更,無需做代碼改造。
后記
不同系統(tǒng)設(shè)計時,面對的領(lǐng)域問題不一樣,解法就有差別。當前系統(tǒng)間調(diào)用,還是以API模式為主;綁在中臺上的業(yè)務(wù)系統(tǒng)的還是以SPI模式來調(diào)用下游的API(個人片面理解,對中臺沒有深入研究)。以“接口標準”這樣的服務(wù)編排、泛化調(diào)用模式,僅適用一些特殊場景。