前言
最近在做一個權限控制的功能,其中一項服務是對用戶進行凍結痊班,具體的業(yè)務邏輯不一定合適細講惊搏,就以“把大象裝進冰箱”來抽象代替。其有如下流程:
1.把冰箱門打開
2.把大象放進去
3.把冰箱門關上
需要依賴兩個服務提供方:
1.冰箱管理員(開門果港、關門)
2.搬運公司(搬大象進冰箱)
那么該如何設計服務的調(diào)用模式呢?
基于“標準”的調(diào)用
上古時代糊昙,服務提供方、和調(diào)用方會一起約定一個入?yún)⒊鰠⒌膱笪臉藴剩▁ml谢谦、json)释牺,根據(jù)這個約定,服務提供方發(fā)布http服務回挽,調(diào)用方也根據(jù)這個入?yún)⒊鰠藴蕘碚{(diào)用提供方的http服務没咙。這里的入?yún)⒊鰠⒓s定,以及http協(xié)議本身千劈,都是一種標準祭刚。即雙方以一種共同遵守的標準,來組織服務調(diào)用關系墙牌。
基于API的調(diào)用
微服務時代涡驮,服務提供方對外暴露自己的API接口,隱藏具體實現(xiàn)邏輯喜滨。服務調(diào)用方引入對方的API之后捉捅,即可調(diào)用對方的服務。以API依賴的方式組織服務調(diào)用關系虽风。
基于SPI的調(diào)用
中臺時代棒口,提倡大中臺,小前臺辜膝。中臺負責實現(xiàn)“把大象裝進冰箱”這個業(yè)務涉及的可復用的流程和能力无牵,預留出對應能力的SPI拓展點,業(yè)務方通過實現(xiàn)對應的spi拓展點來提供差異服務厂抖。比如有些業(yè)務希望使用搬運工把大象搬進冰箱茎毁,另外一些業(yè)務希望使用吊車把大象搬進冰箱,通過在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)伞鲫。(比如中臺架構定義的把大象搬進冰箱SPI拓展點粘茄,可以實現(xiàn)為搬運工搬,也可以實現(xiàn)為吊車搬)
三者取舍
我所開發(fā)的權限控制功能秕脓,完全沒有可能成為一個權限控制中臺柒瓣,加上中臺的概念現(xiàn)在基本也快涼了,所有首先放棄中臺SPI拓展的設計模式吠架。
那么如果采用API依賴模式芙贫,將會是如下情況.
1.服務提供方發(fā)布自己的API
/***
* 搬運公司
*/
public interface MovingCompany{
void move(Elephant elephant);
}
/***
* 冰箱管理員
*/
public interface RefrigeratorManager{
void open();
void close();
}
2.調(diào)用方引入對方服務,然后調(diào)用傍药,以實現(xiàn)把大象裝進冰箱
void putElephant2Refrigerator(elephant){
refrigeratorManager.open();//把冰箱門打開
movingCompany.move(elephant);//把大象放進去
refrigeratorManager.close();//把冰箱門關上
}
這種調(diào)用模式有什么問題呢磺平?
1.如果有一天,業(yè)務要求把大象裝進冰箱之前拐辽,還得清洗一下大象拣挪,那么我需要修改代碼,引入一個清洗大象的服務薛训,然后發(fā)布生效媒吗。
2.如果有一天,國外一個公司覺得這個“把大象裝進冰箱”的功能很好乙埃,希望購買這個服務闸英,部署到他們公司去,那也搞不定介袜,因為這個功能強依賴了我當前引入的冰箱管理員和搬運公司甫何,我需要改代碼,把依賴的冰箱管理員和搬運公司換成他們當?shù)氐摹?/p>
最終遇伞,“把大象裝進冰箱”功能使用了基于“標準”的調(diào)用模式辙喂。
1.所有需要參與到“把大象裝進冰箱”的服務提供方,都按照標準(入?yún)⒘斜砗统鰠ⅲ崿F(xiàn)下述接口來提供服務(接口名鸠珠、方法名隨意)
Result<Void> putElephant(String xxxId,String xxxType,String ext);
2.服務節(jié)點提供服務元數(shù)據(jù),落配置庫巍耗,用來進行后續(xù)服務初始化
{
"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.服務執(zhí)行時,讀取配置庫渐排,根據(jù)服務節(jié)點元數(shù)據(jù)的順序炬太,動態(tài)初始化服務,泛化調(diào)用服務節(jié)點(dubbo泛化調(diào)用驯耻、sofa泛化調(diào)用等)亲族。
回到API調(diào)用模式的兩個問題點炒考,這種模式有如下好處。
1.參與“把大象裝進冰箱”的服務節(jié)點全部配置在數(shù)據(jù)庫霎迫,執(zhí)行時動態(tài)初始化斋枢。在應對“把大象放進去之前,先清洗一下大象”這種業(yè)務變動時知给,只需要將清洗大象的服務節(jié)點在配置庫中進行配置即可瓤帚,不需要應用發(fā)布。
2.當需要SAAS化炼鞠,部署到一個全新環(huán)境時缘滥,數(shù)據(jù)模型和代碼邏輯都是可復用的,只需要把參與服務編排的節(jié)點配置替換為對方需要的即可谒主,比如要把冰箱管理員和搬運公司替換為對方當?shù)氐墓荆ㄐ碌姆找脖仨氉袷亟涌跇藴剩恍枰雠渲脦斓淖兏叻В瑹o需做代碼改造霎肯。
后記
不同系統(tǒng)設計時,面對的領域問題不一樣榛斯,解法就有差別观游。當前系統(tǒng)間調(diào)用,還是以API模式為主驮俗;綁在中臺上的業(yè)務系統(tǒng)的還是以SPI模式來調(diào)用下游的API(個人片面理解懂缕,對中臺沒有深入研究)。以“接口標準”這樣的服務編排王凑、泛化調(diào)用模式搪柑,僅適用一些特殊場景。