微服務(wù)與RPC
在支付系統(tǒng)的微服務(wù)架構(gòu)中,基礎(chǔ)服務(wù)的構(gòu)建是重中之重, 本文重點分析如何使用Apache Thrift + Google Protocol Buffer來構(gòu)建基礎(chǔ)服務(wù)。
一、RPC vs Restful
在微服務(wù)中,使用什么協(xié)議來構(gòu)建服務(wù)體系,一直是個熱門話題。 爭論的焦點集中在兩個候選技術(shù): (binary) RPC or Restful。
以Apache Thrift為代表的二進制RPC,支持多種語言(但不是所有語言),四層通訊協(xié)議,性能高,節(jié)省帶寬。相對Restful協(xié)議,使用Thrifpt RPC,在同等硬件條件下,帶寬使用率僅為前者的20%,性能卻提升一個數(shù)量級。但是這種協(xié)議***的問題在于,無法穿透防火墻。
以Spring Cloud為代表所支持的Restful 協(xié)議,優(yōu)勢在于能夠穿透防火墻,使用方便,語言無關(guān),基本上可以使用各種開發(fā)語言實現(xiàn)的系統(tǒng),都可以接受Restful 的請求。 但性能和帶寬占用上有劣勢。
所以,業(yè)內(nèi)對微服務(wù)的實現(xiàn),基本是確定一個組織邊界,在該邊界內(nèi),使用RPC; 邊界外,使用Restful。這個邊界,可以是業(yè)務(wù)、部門,甚至是全公司。
二、 RPC技術(shù)選型
RPC技術(shù)選型上,原則也是選擇自己熟悉的,或者公司內(nèi)部內(nèi)定的框架。 如果是新業(yè)務(wù),則現(xiàn)在可選的框架其實也不多,卻也足夠讓人糾結(jié)。
Apache Thrift
國外用的多,源于facebook,后捐獻給Apache基金。是Apache的***項目 Apache Thrift。使用者包括facebook, Evernote, Uber, Pinterest等大型互聯(lián)網(wǎng)公司。 而在開源界,Apache hadoop/hbase也在使用Thrift作為內(nèi)部通訊協(xié)議。 這是目前最為成熟的框架,優(yōu)點在于穩(wěn)定、高性能。缺點在于它僅提供RPC服務(wù),其他的功能,包括限流、熔斷、服務(wù)治理等,都需要自己實現(xiàn),或者使用第三方軟件。
Dubbo
國內(nèi)用的多,源于阿里公司。 性能上略遜于Apache Thrift,但自身集成了大量的微服務(wù)治理功能,使用起來相當方便。 Dubbo的問題在于,該系統(tǒng)目前已經(jīng)很長時間沒有維護更新了。 官網(wǎng)顯示最近一次的更新也是8個月前。
Google Protobuf
和Apache Thrift類似,Google Protobuf也包括數(shù)據(jù)定義和服務(wù)定義兩部分。問題是,Google Protobuf一直只有數(shù)據(jù)模型的實現(xiàn),沒有官方的RPC服務(wù)的實現(xiàn)。 直到2015年才推出gRPC,作為RPC服務(wù)的官方實現(xiàn)。但缺乏重量級的用戶。
以上僅做定性比較。定量的對比,網(wǎng)上有不少資料,可自行查閱。 此外,還有一些不錯的RPC框架,比如Zeroc ICE等,不在本文的比較范圍。
Thrift 提供多種高性能的傳輸協(xié)議,但在數(shù)據(jù)定義上,不如Protobuf強大。
- 同等格式數(shù)據(jù), Protobuf壓縮率和序列化/反序列化性能都略高。
- Protobuf支持對數(shù)據(jù)進行自定義標注,并可以通過API來訪問這些標注,這使得Protobuf在數(shù)據(jù)操控上非常靈活。比如可以通過option來定義protobuf定義的屬性和數(shù)據(jù)庫列的映射關(guān)系,實現(xiàn)數(shù)據(jù)存取。
- 數(shù)據(jù)結(jié)構(gòu)升級是常見的需求,Protobuf在支持數(shù)據(jù)向下兼容上做的非常不錯。只要實現(xiàn)上處理得當,接口在升級時,老版本的用戶不會受到影響。
而Protobuf的劣勢在于其RPC服務(wù)的實現(xiàn)性能不佳(gRPC)。為此,Apache Thrift + Protobuf的RPC實現(xiàn),成為不少公司的選擇。
三、Apache Thrift + Protobuf
如上所述,利用Protobuf在靈活數(shù)據(jù)定義、高性能的序列化/反序列化、兼容性上的優(yōu)勢,以及Thrift在傳輸上的成熟實現(xiàn),將兩者結(jié)合起來使用,是不少互聯(lián)網(wǎng)公司的選擇。
服務(wù)定義:
- service HelloService{
- binary hello(1: binary hello_request);
- }
- 協(xié)議定義:
- message HelloRequest{
- optional string user_name = 1; //訪問這個接口的用戶
- optional string password = 2; //訪問這個接口的密碼
- optional string hello_word = 3; //其他參數(shù);
- }
- message HelloResponse{
- optional string hello_word = 1; //訪問這個接口的用戶
- }
想對于純的thrift實現(xiàn),這種方式雖然看起來繁瑣,但其在可擴展性、可維護性和服務(wù)治理上,可以帶來不少便利。
四、服務(wù)注冊與發(fā)現(xiàn)
Spring cloud提供了服務(wù)注冊和發(fā)現(xiàn)功能,如果需要自己實現(xiàn),可以考慮使用Apache Zookeeper作為注冊表,使用Apache Curator 來管理Zookeeper的鏈接,它實現(xiàn)如下功能:
- 偵聽注冊表項的變化,一旦有更新,可以重新加載注冊表。
- 管理到zookeeper的鏈接,如果出現(xiàn)問題,則進行重試。
Curator的重試策略是可配置的,提供如下策略:
- BoundedExponentialBackoffRetry
- ExponentialBackoffRetry
- RetryForever
- RetryNTimes
- RetryOneTime
- RetryUntilElapsed
一般使用指數(shù)延遲策略,比如重試時間間隔為1s,2s, 4s, 8s……指數(shù)增加,避免把服務(wù)器打死。
對服務(wù)注冊來說,注冊表結(jié)構(gòu)需要詳細設(shè)計,一般注冊表結(jié)構(gòu)會按照如下方式組織:
機房區(qū)域-部門-服務(wù)類型-服務(wù)名稱-服務(wù)器地址
由于在zookeeper上的注冊和發(fā)現(xiàn)有一定的延遲,所以在實現(xiàn)上也得注意,當服務(wù)啟動成功后,才能注冊到zookeeper上;當服務(wù)要下線或者重啟前,需要先斷開同zookeeper的連接,再停止服務(wù)。
五、連接池
RPC服務(wù)訪問和數(shù)據(jù)庫類似,建立鏈接是一個耗時的過程,連接池是服務(wù)調(diào)用的標配。目前還沒有成熟的開源Apache Thrift鏈接池,一般互聯(lián)網(wǎng)公司都會開發(fā)內(nèi)部自用的鏈接池。自己實現(xiàn)可以基于JDBC鏈接池做改進,比如參考Apache commons DBCP鏈接池,使用Apache Pools來管理鏈接。 在接口設(shè)計上,連接池需要管理的是RPC 的Transport:
- public interface TransportPool {
- /**
- * 獲取一個transport
- * @return
- * @throws TException
- */
- public TTransport getTransport() throws TException;}
連接池實現(xiàn)的主要難點在于如何從多個服務(wù)器中選舉出來為當前調(diào)用提供服務(wù)的連接。比如目前有10臺機器在提供服務(wù),上一次分配的是第4臺服務(wù)器,本次應(yīng)該分配哪一臺?在實現(xiàn)上,需要收集每臺機器的QOS以及當前的負擔,分配一個***的連接。
六、API網(wǎng)關(guān)
隨著公司業(yè)務(wù)的增長,RPC服務(wù)越來越多,這也為服務(wù)調(diào)用帶來挑戰(zhàn)。如果有一個應(yīng)用需要調(diào)用多個服務(wù),對這個應(yīng)用來說,就需要維護和多個服務(wù)器之間的鏈接。服務(wù)的重啟,都會對連接池以及客戶端的訪問帶來影響。為此,在微服務(wù)中,廣泛會使用到API網(wǎng)關(guān)。API網(wǎng)關(guān)可以認為是一系列服務(wù)集合的訪問入口。從面向?qū)ο笤O(shè)計的角度看,它與外觀模式類似,實現(xiàn)對所提供服務(wù)的封裝。
網(wǎng)關(guān)作用
API網(wǎng)關(guān)本身不提供服務(wù)的具體實現(xiàn),它根據(jù)請求,將服務(wù)分發(fā)到具體的實現(xiàn)上。 其主要作用:
- API路由: 接受到請求時,將請求轉(zhuǎn)發(fā)到具體實現(xiàn)的worker機器上。避免使用方建立大量的連接。
- 協(xié)議轉(zhuǎn)換: 原API可能使用http或者其他的協(xié)議來實現(xiàn)的,統(tǒng)一封裝為rpc協(xié)議。注意,這里的轉(zhuǎn)換,是批量轉(zhuǎn)換。也就是說,原來這一組的API是使用http實現(xiàn)的,現(xiàn)在要轉(zhuǎn)換為RPC,于是引入網(wǎng)關(guān)來統(tǒng)一處理。對于單個服務(wù)的轉(zhuǎn)換,還是單獨開發(fā)一個Adapter服務(wù)來執(zhí)行。
- 封裝公共功能: 將微服務(wù)治理相關(guān)功能封裝到網(wǎng)關(guān)上,簡化微服務(wù)的開發(fā),這包括熔斷、限流、身份驗證、監(jiān)控、負載均衡、緩存等。
- 分流:通過控制API網(wǎng)關(guān)的分發(fā)策略,可以很容易實現(xiàn)訪問的分流,這在灰度測試和AB測試時特別有用。
解耦合
RPC API網(wǎng)關(guān)在實現(xiàn)上,難點在于如何做到服務(wù)無關(guān)。我們知道使用Nginx實現(xiàn)HTTP的路由網(wǎng)關(guān),可以實現(xiàn)和服務(wù)無關(guān)。而RPC網(wǎng)關(guān)由于實現(xiàn)上的不規(guī)范,很難實現(xiàn)和服務(wù)無關(guān)。統(tǒng)一使用thrift + protobuf 來開發(fā)RPC服務(wù)可以簡化API網(wǎng)關(guān)的開發(fā),避免為每個服務(wù)上線而帶來的網(wǎng)關(guān)的調(diào)整,使得網(wǎng)關(guān)和具體的服務(wù)解耦合:
- 每個服務(wù)實現(xiàn)的worker機器將服務(wù)注冊到zookeeper上;
- API網(wǎng)關(guān)接收到zookeeper的變更,更新本地的路由表,記錄服務(wù)和worker(連接池)的映射關(guān)系。
- 當請求被提交到網(wǎng)關(guān)上時,網(wǎng)關(guān)可以從rpc請求中提取出服務(wù)名稱,之后根據(jù)這個名稱,找到對應(yīng)的worker機(連接池),調(diào)用該worker上的服務(wù),接受到結(jié)果后,將結(jié)果返回給調(diào)用方。
權(quán)限和其他
Protobuf的一個重要特性是,數(shù)據(jù)的序列化和名稱無關(guān),只和屬性類型、編號有關(guān)。 這種方式,間接實現(xiàn)了類的繼承關(guān)系。如下所示,我們可以通過Person類來解析Girl和Boy的反序列化流:
- message Person {
- optional string user_name = 1;
- optional string password = 2; }message Girl {
- optional string user_name = 1;
- optional string password = 2;
- optional string favorite_toys = 3; }message Boy {
- optional string user_name = 1;
- optional string password = 2;
- optional int32 favorite_club_count = 3;
- optional string favorite_sports = 4; }
我們只要對服務(wù)的輸入?yún)?shù)做合理的編排,將常用的屬性使用固定的編號來表示,既可以使用通用的基礎(chǔ)類來解析輸入?yún)?shù)。比如我們要求所有輸入的***個和第二個元素必須是user_name和password,則我們就可以使用Person來解析這個輸入,從而可以實現(xiàn)對服務(wù)的統(tǒng)一身份驗證,并基于驗證結(jié)果來實施QPS控制等工作。
七、熔斷與限流
Netflix Hystrix提供不錯的熔斷和限流的實現(xiàn),參考其在GitHub上的項目介紹。這里簡單說下熔斷和限流實現(xiàn)原理。
熔斷一般采用電路熔斷器模式(Circuit Breaker Patten)。當某個服務(wù)發(fā)生錯誤,每秒錯誤次數(shù)達到閾值時,不再響應(yīng)請求,直接返回服務(wù)器忙的錯誤給調(diào)用方。 延遲一段時間后,嘗試開放50%的訪問,如果錯誤還是高,則繼續(xù)熔斷;否則恢復(fù)到正常情況。
限流指按照訪問方、IP地址或者域名等方式對服務(wù)訪問進行限制,一旦超過給定額度,則禁止其訪問。 除了使用Hystrix,如果要自己實現(xiàn),可以考慮使用使用Guava RateLimiter
八、服務(wù)演化
隨著服務(wù)訪問量的增加,服務(wù)的實現(xiàn)也會不斷演化以提升性能。主要的方法有讀寫分離、緩存等。
讀寫分離
針對實體服務(wù),讀寫分離是提升性能的***步。 實現(xiàn)讀寫分離一般有兩種方式:
在同構(gòu)數(shù)據(jù)庫上使用主從復(fù)制的方式: 一般數(shù)據(jù)庫,比如MySQL、HBase、Mongodb等,都提供主從復(fù)制功能。數(shù)據(jù)寫入主庫,讀取、檢索等操作都從從庫上執(zhí)行,實現(xiàn)讀寫分離。這種方式實現(xiàn)簡單,無需額外開發(fā)數(shù)據(jù)同步程序。一般來說,對寫入有事務(wù)要求的數(shù)據(jù)庫,在讀取上的性能會比較差。雖然可以通過增加從庫的方式來sharding請求,但這也會導(dǎo)致成本增加。
在異構(gòu)數(shù)據(jù)庫上進行讀寫分離。發(fā)揮不同數(shù)據(jù)庫的優(yōu)勢,通過消息機制或者其他方式,將數(shù)據(jù)從主庫同步到從庫。 比如使用MySQL作為主庫來寫入,數(shù)據(jù)寫入時投遞消息到消息服務(wù)器,同步程序接收到消息后,將數(shù)據(jù)更新到讀庫中??梢允褂肦edis,Mongodb等內(nèi)存數(shù)據(jù)庫作為讀庫,用來支持根據(jù)ID來讀取;使用Elastic作為從庫,支持搜索。
緩存使用
如果數(shù)據(jù)量大,使用從庫也會導(dǎo)致從庫成本非常高。對大部分數(shù)據(jù)來說,比如訂單庫,一般需要的只是一段時間,比如三個月內(nèi)的數(shù)據(jù)。更長時間的數(shù)據(jù)訪問量就非常低了。 這種情況下,沒有必要將所有數(shù)據(jù)加載到成本高昂的讀庫中,即這時候,讀庫是緩存模式。 在緩存模式下,數(shù)據(jù)更新策略是一個大問題。
- 對于實時性要求不高的數(shù)據(jù),可以考慮采用被動更新的策略。即數(shù)據(jù)加載到緩存的時候,設(shè)置過期時間。一般內(nèi)存數(shù)據(jù)庫,包括Redis,couchbase等,都支持這個特性。到過期時間后,數(shù)據(jù)將失效,再次被訪問時,系統(tǒng)將觸發(fā)從主庫讀寫數(shù)據(jù)的流程。
- 對實時性要求高的數(shù)據(jù),需要采用主動更新的策略,也就是接受Message后,立即更新緩存數(shù)據(jù)。
當然,在服務(wù)演化后,對原有服務(wù)的實現(xiàn)也會產(chǎn)生影響。 考慮到微服務(wù)的一個實現(xiàn)原則,即一個服務(wù)僅管一個存儲庫,原有的服務(wù)就被分裂成多個服務(wù)了。 為了保持使用方的穩(wěn)定,原有服務(wù)被重新實現(xiàn)為服務(wù)網(wǎng)關(guān),作為各個子服務(wù)的代理來提供服務(wù)。
以上是RPC與微服務(wù)的全部內(nèi)容,以下是thrift + protobuf的實現(xiàn)規(guī)范的介紹。
附一、基礎(chǔ)服務(wù)設(shè)計規(guī)范
基礎(chǔ)服務(wù)是微服務(wù)的服務(wù)棧中***層的模塊, 基礎(chǔ)服務(wù)直接和數(shù)據(jù)存儲打交道,提供數(shù)據(jù)增刪改查的基本操作。
附1.1 設(shè)計規(guī)范
文件規(guī)范
rpc接口文件名以 xxx_rpc_service.thrift 來命名; protobuf參數(shù)文件名以 xxx_service.proto 來命名。
這兩種文件全部使用UTF-8編碼。
命名規(guī)范
服務(wù)名稱以 “XXXXService” 的格式命名, XXXX是實體,必須是名詞。以下是合理的接口名稱。
- OrderService
- AccountService
附1.2 方法設(shè)計
由于基礎(chǔ)服務(wù)主要是解決數(shù)據(jù)讀寫問題,所以從使用的角度,對外提供的接口,可以參考數(shù)據(jù)庫操作,標準化為增、刪、改、查、統(tǒng)計等基本接口。接口采用 操作+實體來命名,如createOrder。 接口的輸入輸出參數(shù)采用 接口名+Request 和 接口名Response 的規(guī)范來命名。 這種方式使得接口易于使用和管理。
- file: xxx_rpc_service.thrift
- /**
- * 這里是版權(quán)申明
- **/
- namespace java com.phoenix.service
- /**
- * 提供關(guān)于XXX實體的增刪改查基本操作。
- **/
- service XXXRpcService {
- /**
- * 創(chuàng)建實體
- * 輸入?yún)?shù):
- * 1. createXXXRequest: 創(chuàng)建請求,支持創(chuàng)建多個實體;
- * 輸出參數(shù)
- * createXXXResponse: 創(chuàng)建成功,返回創(chuàng)建實體的ID列表;
- * 異常
- * 1. userException:輸入的參數(shù)有誤;
- * 2. systemExeption:服務(wù)器端出錯導(dǎo)致無法創(chuàng)建;
- * 3. notFoundException: 必填的參數(shù)沒有提供。
- **/
- binary createXXX(1: binary create_xxx_request) throws (1: Errors.UserException userException, 2: Errors.systemException, 3: Errors.notFoundException)
- /**
- * 更新實體
- * 輸入?yún)?shù):
- * 1. updateXXXRequest: 更新請求,支持同時更新多個實體;
- * 輸出參數(shù)
- * updateXXXResponse: 更新成功,返回被更行的實體的ID列表;
- * 異常
- * 1. userException:輸入的參數(shù)有誤;
- * 2. systemExeption:服務(wù)器端出錯導(dǎo)致無法創(chuàng)建;
- * 3. notFoundException: 該實體在服務(wù)器端沒有找到。
- **/
- binary updateXXX(1: binary update_xxx_request) throws (1: Errors.UserException userException, 2: Errors.systemException, 3: Errors.notFoundException)
- /**
- * 刪除實體
- * 輸入?yún)?shù):
- * 1. removeXXXRequest: 刪除請求,按照id來刪除,支持一次刪除多個實體;
- * 輸出參數(shù)
- * removeXXXResponse: 刪除成功,返回被刪除的實體的ID列表;
- * 異常
- * 1. userException:輸入的參數(shù)有誤;
- * 2. systemExeption:服務(wù)器端出錯導(dǎo)致無法創(chuàng)建;
- * 3. notFoundException: 該實體在服務(wù)器端沒有找到。
- **/
- binary removeXXX(1: binary remove_xxx_request) throws (1: Errors.UserException userException, 2: Errors.systemException, 3: Errors.notFoundException)
- /**
- * 根據(jù)ID獲取實體
- * 輸入?yún)?shù):
- * 1. getXXXRequest: 獲取請求,按照id來獲取,支持一次獲取多個實體;
- * 輸出參數(shù)
- * getXXXResponse: 返回對應(yīng)的實體列表;
- * 異常
- * 1. userException:輸入的參數(shù)有誤;
- * 2. systemExeption:服務(wù)器端出錯導(dǎo)致無法創(chuàng)建;
- * 3. notFoundException: 該實體在服務(wù)器端沒有找到。
- **/
- binary getXXX(1: binary get_xxx_request) throws (1: Errors.UserException userException, 2: Errors.systemException, 3: Errors.notFoundException)
- /**
- * 查詢實體
- * 輸入?yún)?shù):
- * 1. queryXXXRequest: 查詢條件;
- * 輸出參數(shù)
- * queryXXXResponse: 返回對應(yīng)的實體列表;
- * 異常
- * 1. userException:輸入的參數(shù)有誤;
- * 2. systemExeption:服務(wù)器端出錯導(dǎo)致無法創(chuàng)建;
- * 3. notFoundException: 該實體在服務(wù)器端沒有找到。
- **/
- binary queryXXX(1: binary query_xxx_request) throws (1: Errors.UserException userException, 2: Errors.systemException, 3: Errors.notFoundException)
- /**
- * 統(tǒng)計符合條件的實體的數(shù)量
- * 輸入?yún)?shù):
- * 1. countXXXRequest: 查詢條件;
- * 輸出參數(shù)
- * countXXXResponse: 返回對應(yīng)的實體數(shù)量;
- * 異常
- * 1. userException:輸入的參數(shù)有誤;
- * 2. systemExeption:服務(wù)器端出錯導(dǎo)致無法創(chuàng)建;
- * 3. notFoundException: 該實體在服務(wù)器端沒有找到。
- **/
- binary countXXX(1: binary count_xxx_request) throws (1: Errors.UserException userException, 2: Errors.systemException, 3: Errors.notFoundException)
- }
附1.3 參數(shù)設(shè)計
每個方法的輸入輸出參數(shù),采用protobuf來表示。
- file: xxx_service.protobuf
- /**
- *
- * 這里是版權(quán)申明
- **/
- option java_package = "com.phoenix.service";
- import "entity.proto";
- import "taglib.proto";
- /**
- * 創(chuàng)建實體的請求
- *
- **/
- message CreateXXXRequest {
- optional string user_name = 1; //訪問這個接口的用戶
- optional string password = 2; //訪問這個接口的密碼
- repeated XXXX xxx = 21; // 實體內(nèi)容;
- }
- /**
- * 創(chuàng)建實體的結(jié)果響應(yīng)
- *
- **/
- message CreateXXXResponse {
- repeated int64 id = 11;//成功創(chuàng)建的實體的ID列表
- }
- 附1.4 異常設(shè)計
- RPC接口也不需要太復(fù)雜的異常,一般是定義三類異常。
- file errors.thrift
- /**
- * 由于調(diào)用方的原因引起的錯誤, 比如參數(shù)不符合規(guī)范、缺乏必要的參數(shù),沒有權(quán)限等。
- * 這種異常一般是可以重試的。
- *
- **/
- exception UserException {
- 1: required ErrorCode error_code;
- 2: optional string message;
- }
- /**
- * 由于服務(wù)器端發(fā)生錯誤導(dǎo)致的,比如數(shù)據(jù)庫無法連接。這也包括QPS超過限額的情況,這時候rateLimit返回分配給的QPS上限;
- *
- **/
- exception systemException {
- 1: required ErrorCode error_code;
- 2: optional string message;
- 3: i32 rateLimit;
- }
- /**
- * 根據(jù)給定的ID或者其他條件無法找到對象。
- *
- **/
- exception systemException {
- 1: optional string identifier;
- }
附二、服務(wù)SDK
當然,RPC服務(wù)不應(yīng)該直接提供給業(yè)務(wù)方使用,需要提供封裝好的客戶端。 一般來說,客戶端除了提供訪問服務(wù)端的代理外,還需要對常有功能進行封裝,這包括服務(wù)發(fā)現(xiàn)、RPC連接池、重試機制、QPS控制。這里首先介紹服務(wù)SDK的設(shè)計。 直接使用Protobuf作為輸入?yún)?shù)和輸出參數(shù),開發(fā)出來的代碼很繁瑣:
- GetXXXRequest.Builder request = GetXXXRequest.newBuilder();
- request.setUsername("username");
- request.setPassword("password");
- request.addId("123");
- GetXXXResponse response = xxxService.getXXX(request.build());
- if(response.xxx.size()==1)
- XXX xxx = response.xxx.get(0);
如上,有大量的重復(fù)性代碼,使用起來不直觀也不方便。 因而需要使用客戶端SDK來做一層封裝,供業(yè)務(wù)方調(diào)用:
- class XXXService {
- //根據(jù)ID獲取對象
- public XXX getXXX(String id){
- GetXXXRequest.Builder request = GetXXXRequest.newBuilder();
- request.setUsername("username");
- request.setPassword("password");
- request.addId("123");
- GetXXXResponse response = xxxService.getXXX(request.build());
- if(response.xxx.size()==1)
- return response.xxx.get(0);
- return null;
- }
- }
對所有服務(wù)器端接口提供對應(yīng)的客戶端SDK,也是微服務(wù)架構(gòu)***實踐之一。以此封裝完成后,調(diào)用方即可以像使用普通接口一樣,無需了解實現(xiàn)細節(jié)。
【本文為51CTO專欄作者“鳳凰牌老熊”的原創(chuàng)稿件,轉(zhuǎn)載請通過微信公眾號“鳳凰牌老熊”聯(lián)系作者本人】