回覆列表
  • 1 # 賀權7

    和很多微服務開發框架類似,ServiceComb的早期版本,為了追求高效能,做過非常多的嘗試,比如改善編碼效率,改進通訊協議等。隨著業務規模的遞增,問題隨之而來。首先面對的是和遺留系統的通訊,接著是各種不同的接入終端,然後就涉及到一些更加複雜的問題,協議健壯性、防攻擊等。ServiceComb準備開源的前期,使用者對於與業界其他開源元件和開發框架的整合的述求又源源的湧來。在這些問題的驅動下,ServiceComb設計團隊慢慢的達成了共識,系統應該全開放,使用標準協議,容易拆分和擴充套件,對開發人員友好,並和業界其他系統良好整合。本文將分享這些方面的設計考慮。

    開放和標準

    開放和標準應用到設計的不同的層面。一方面是連線組織和開發人員,一方面是連線異構系統。組織和開發人員的複雜性來源於技能的多樣性,大家使用不同的開發語言,同一種開發語言存在多樣的開發習慣;系統的多樣性來源於系統之間的通訊協議,為了實現與異構系統的通訊,必須具備良好的適配不同通訊協議的能力。

    連線組織和開發人員

    l 程式碼風格

    每個開發人員都有自己熟練的技術和寫程式碼的方式,使用熟悉的方式寫程式碼往往更加高效。ServiceComb的早期版本實現了gRPC協議,推廣的過程中發現大量開發人員不能熟練的書寫IDL,對IDL支援哪些特性不清楚,碰到一個場景就需要翻開協議規範看一遍。加上介面語言一般缺少配套的編輯和語法檢查工具,開發效率非常低。如果開發人員能夠以熟悉的開發方式馬上開始工作,將是一件美妙的事情,新進來的人沒門檻,老系統也能快速切過來,一舉兩得。在JAVA世界裡開發框架不勝列舉,我們從這些方式中找到了很多的共性,能夠涵蓋到90%以上的習慣:1. 使用RPC的方式描述對外介面。這個在gRPC、Corba、WebService等開發人員面前非常熟悉, 是最直接和有效的寫程式碼方式; 2. 使用JAX-RS或者Spring MVC風格開發REST介面。REST風格開發隨著微服務架構興起,JAX-RS是JAVA REST開發標準,Spring MVC是JAVA開發的事實標準,Spring擁護者很熟悉。 ServiceComb選擇這幾種預設的開發風格,擁抱90%的開發者,讓大家能夠快速開始工作。

    在下面的例子中,呈現了Provider和Consumer程式碼的各種實現,在同一個微服務中,這些呈現方式可以同時出現;同一段Consumer程式碼,可以訪問各種不同的Provider實現,非常靈活。

    程式碼:RPC形式的Provider.

    @RpcSchema(schemaId="hello") publicclassHelloImplimplementsHello{ @Override publicStringsayHi(Stringname){ return"Hello"+name; } @Override publicStringsayHello(Personperson){ return"Helloperson"+person.getName(); } }

    程式碼:JAX-RS形式的Provider.

    @RestSchema(schemaId="jaxrsHello") @Path("/jaxrshello") @Produces(MediaType.APPLICATION_JSON) publicclassJaxrsHelloImplimplementsHello{ @Path("/sayhi") @POST @Override publicStringsayHi(Stringname){ return"Hello"+name; } @Path("/sayhello") @POST @Override publicStringsayHello(Personperson){ return"Helloperson"+person.getName(); } }

    程式碼:Spring MVC形式的Provider.

    @RestSchema(schemaId="springmvcHello") @RequestMapping(path="/springmvchello",produces=MediaType.APPLICATION_JSON) publicclassSpringmvcHelloImplimplementsHello{ @Override @RequestMapping(path="/sayhi",method=RequestMethod.POST) publicStringsayHi(@RequestParam(name="name")Stringname){ return"Hello"+name; } @Override @RequestMapping(path="/sayhello",method=RequestMethod.POST) publicStringsayHello(@RequestBodyPersonperson){ return"Helloperson"+person.getName(); } }

    程式碼:RPC方式訪問上述三種服務的Consumer.

    @RpcReference(microserviceName="hello",schemaId="hello") privateHellohello; System.out.println(hello.sayHi("JavaChassis"));

    看到這裡,開發者或者有個疑問,Consumer既然可以透過一致的API方式訪問不同的Provider,為什麼還需要額外的JAX-RS和SpringMVC標籤了?這裡的主要考量並不僅僅是SDK的Consumer,還有瀏覽器等非SDK的Consumer,他們識別的是HTTP形式的訊息,透過這些標籤,可以更加精細的指定瀏覽器如何訪問後臺介面。這一點非常類似於Web Service的WSDL描述語言,ServiceComb稱之為契約。服務契約會在執行時透過程式碼定義生成,並註冊到服務中心。契約在執行時可以用於獨立的服務治理邏輯開發,生成Consumer程式碼。也可以作為文件對外發布,供非SDK的Consumer參考。

    l 契約

    微服務強調服務自治,對外體現的功能,全部以介面提供,而且只能以通訊的方式相互訪問。這個原則給團隊協作帶來了根本的變革,一個團隊通常由5~6個人的全功能團隊組成,端到端的完成功能設計、開發和運維,系統的結構和組織的結構是匹配的。小團隊以後的核心問題就是團隊之間如何進行高效的溝通。為了更好的連線開發人員,我們讓不同的開發人員能夠使用自己熟悉的語言和程式設計習慣寫程式碼,這樣就需要一種中立的機制讓每個功能團隊進行有效協作。在RPC的世界裡,有Corda IDL,WSDL,ProtoBuffer等可以參考的優秀實踐。REST風格的介面讓團隊成員之間可以透過HTTP的語義進行溝通,但是不能像IDL一樣描述跨語言時的資料格式。Open API的出現很好的解決了這些問題。Open API首先是一個開放的標準,並且在不斷的發展壯大。Open API對於RPC、REST等不同的開發方式都完整的兼顧到了,並且吸收了大量的跨語言經驗,能夠在不同的語言之間解析。 對於JAVA開發者,下面的型別定義已經是毫無疑問的:

    User: type:object properties: age: type:integer

    如果開發人員有豐富的跨語言開發經驗,可以看出Swagger在解決跨語言程式設計方面的努力,swagger透過format來定義資料型別的儲存格式,以解決不同的語言在資料型別表示上的差異:

    User: type:object properties: age: type:integer format:int32

    SerivceComb兼顧開發效率和開發規範。開發者可以先寫介面定義,再寫程式碼的方式來完成自己的開發過程,也可以直接透過自己熟悉的方式寫程式碼。兩種方式都會生成服務的契約(Open API描述檔案),並且將內容註冊到服務中心。使用者可以從服務中心下載相關的契約進行開發。ServiceComb的各種治理結構也是基於契約的,可以讓開發者獨立於業務實現對系統進行統一的管控。

    連線異構系統

    ServiceComb早期版本提供了gRPC、REST、SOAP等多種協議。gRPC相對於REST的最大好處就是效能,採用長連線,高效的二進位制序列化方式,並提供多種語言支援。在介面定義方面,提供了IDL語言約束開發者按照標準的方式工作。一切看起來是那麼的完美,實際上ServiceComb的第一輪重構,首選的也是gRPC的方式。系統上線以後,首要的問題來源於閘道器的壓力。閘道器作為所有業務的接入端,必須高效的管理連線和保證公平,長連線非常容易導致拒絕服務。gRPC程式開發完成後,開發聯調也變得不方便起來,特別是生產環境。開發人員無法利用系統提供的各種工具進行測試,網路包分析也變得困難。隨著系統規模的擴大,gRPC再次面對更加嚴峻的問題,其他系統如何與它直接通訊,如何跨閘道器與它間接通訊,解決這些問題,意味著我們需要擴充套件和改善老的協議和程式,提供gRPC客戶端支援,開發者需要提供一個額外的表示層用於業務介面的邏輯轉換,造成大量的重複程式碼。同時由於gRPC依賴於介面定義,並根據定義生成程式碼,一套程式碼只能跑在gRPC協議上,希望業務程式碼使用其他更加靈活的方式,比如REST訪問的時候,就得重新寫一套程式碼。面對以上問題,gRPC在選擇上,最終被定義為只能在中小型系統內部之間使用,並透過協議閘道器與外部系統進行溝通。

    接著就是REST了。業界可用的REST實現,和gRPC比較起來,最大的痛點就是效能。有一個觀點在很多設計人員和開發人員腦海裡根深蒂固:”二進位制編碼效率遠高於文字協議,採用二進位制編碼的系統的效能遠高於採用文字的HTTP”。這個觀點甚至會讓多數決策止步於理論,大家甚至不願意嘗試去最佳化REST。可喜的是ServiceComb走出了重構REST底層通訊實現的第一步,基於Netty的非同步框架來替換Tomcat實現,效果大大超出我們的預期。一些基準測試資料的結果顯示比gRPC還要好,gRPC最終輸在了HTTP2協議上的額外報文。同時最佳化後的REST和業界開源的其他基於二進位制的RPC實現的效能也基本持平,只有微小的差異。在一個簡單的提供資料庫查詢的程式碼邏輯中,REST通訊框架處理時間,佔用總的處理時間遠小於千分之一。這意味著在系統框架層面的大量最佳化,抵不上業務系統最簡單的一筆操作,為了最佳化效能放棄的其他好處就不值得了。最終,ServiceComb選擇了REST作為首選和預設協議(http + json)。

    使用REST也存在和其他系統對接,透過閘道器對接的場景,但是問題已經好了很多。

    但是我們遠沒有止步於此。隨著需要遷移到系統平臺的服務越來越多,早期的一些遺留系統也需要進行對接。我們提供的每一種通訊協議,都對應著不同的開發者介面,增加通訊協議,意味著需要對業務程式碼進行大量的重複構建。為了進一步滿足更多的場景,通訊協議層被剝離了出來,做到和業務程式碼分離,系統執行基於契約,實現通訊協議的擴充套件。利用協議擴充套件機制,使用者解決了與老的gRPC框架、自定義二進位制框架等很多遺留系統的通訊問題。

    在ServiceComb框架中,切換協議非常簡單,不需要修改一行業務程式碼。多個協議共存也是允許的。

    cse: rest: address:0.0.0.0:8084 highway: address:0.0.0.0:8094

    關於協議擴充套件的更多內容,將在下面的章節進行介紹。

    擴充套件性

    擴充套件性是系統進一步發展的基石。ServiceComb創造性的將擴充套件性拓展到Provider和Consumer,讓它們擁有一致的開發體驗。這個在現有的各種開發框架裡面是獨有的。

    內部系統結構

    連線開發者和通訊協議層面已經讓系統具備了很大的擴充套件性。微服務化給系統解耦、團隊自治帶來了很大的靈活性,加快了生產效率;同時也帶來了服務管控的複雜性。需要用通用的管控機制來解決雪崩效應、呼叫跟蹤、效能監控與分析等問題。基於服務契約,ServiceComb提供了動態插拔擴充套件的處理鏈機制,並且為應對這些管控,提供了預設實現,開發者可以靈活插拔這些處理模組,或者調整他們的順序以應對不同的處理場景,增加新的處理模組等。不管Provider,還是Consumer,都會經過該處理鏈,這給客戶端治理功能開發帶來了非常大的便利,這是其他現有的微服務框架不具備的特性。ServiceComb的執行結構如下圖:

    ServiceComb在使用者程式設計介面上,支援同步和非同步兩種方式。在通訊實現上,採用了純非同步的方式,對於執行模型的擴充套件,也是基於非同步回撥介面的。這種方式提供了比同步模式(比如Filter)更加優雅和靈活的擴充套件方式。

    在這個系統結構中,有幾個核心的介面,這些介面均在core模組進行定義:

    ProducerProviderProvider程式設計模型的擴充套件,透過實現這個介面,可以適配不同的Provider程式設計風格;目前實現了RPC、SpringMVC、JAX-RS三種風格。

    ConsumerProviderConsumer程式設計模型的擴充套件,透過實現這個介面,可以適配不同的Consumer程式設計風格;目前實現了RPC、RestTemplate兩種風格。RestTemplate是Spring MVC提供了REST程式設計介面,可以在服務層方面解除介面依賴,只需要依賴資料模型。

    Handler處理鏈的介面。透過擴充套件該介面,可以在處理過程中插入任意的邏輯。目前實現了負載均衡、服務治理、流量控制等多個處理鏈。開發者可以針對Consumer和Provider定義不同的處理鏈,並且為訪問不同的微服務定製不同的處理鏈。

    Transport通訊協議擴充套件。目前實現了REST over Vertx,Rest over Servlet,Highway等多個協議。

    Invocation中立的物件。所有的執行模型都面對這個中立的物件進行程式設計,當定義好服務介面後,對服務的治理和服務業務邏輯的開發可以並行。在程式設計模型和通訊模型裡面,也面對這個模型進行編解碼。

    對接外部系統

    執行框架涉及到的外部系統包括服務註冊發現的服務中心、配置管控和治理的配置中心、執行監控和運維的治理中心等。這些功能也預留了介面,能夠讓開發者靈活切換使用第三方提供的服務。 下圖是不同的開發框架支援和執行的第三方系統情況,這些基礎服務都給開發者預留了可以進行支援接入的介面。

    下面是幾個重要的擴充套件:

    ServiceRegistryClient: 實現這個介面以對接不同的註冊服務。

    ConfigCenterConfigurationSource: 實現這個介面以對接不同的配置服務。

    此外,ServiceComb還提供了對接Zipkin、Servo等開源系統的功能,這些可以從github官網程式碼中查詢到對應的例子。

    執行環境整合

    一個完整的業務系統不是使用RPC框架就算完成了,它們還需要其他的計算資源。對於一般的業務系統都需要訪問資料庫,或者基於J2EE的設施進行工作。ServiceComb可以以非常輕量級的方式執行,也可以整合到其他系統框架裡面工作。下面的示意圖說明了ServiceComb的一些工作環境。

    如果業務只需要提供REST介面,可以以輕量級的方式執行ServiceComb。所有的REST介面運行於ServiceComb提供的Netty HTTP之上。

    如果業務是基於J2EE來構建,那麼ServiceComb可以作為一個Servlet,運行於WEB容器裡面(比如Tomcat、Jetty等)。

    如果業務要基於Spring Boot生態構建,ServiceComb可以作為一個starter對外提供REST服務,開發者可以自由使用其他基於Spring Boot的功能。

    由於ServiceComb使用了Spring,因此也能夠和很多通用的元件很好的整合,比如mybatis、JPA等。各種整合方式,都可以從ServiceComb官網或者CSE示例庫找到對應的例子。

    總結

    本文簡單的介紹了一下ServiceComb開發框架開發性設計的一些考慮。除了這些特性之外,ServicComb還提供了Edge Service閘道器服務、Saga分散式事務等特性。

    參考文獻:

    1. ServiceComb官網包括原始碼:https://github.com/apache/incubator-servicecomb-java-chassis和文件資料:http://servicecomb.incubator.apache.org/

    2. 華為雲使用ServiceComb作為首選微服務開發框架,商用支援參考幫助網站:http://support.huaweicloud.com/cse_dld/index.html 和資料的Preview版本:https://java.huaweicse.cn/

    【版權宣告】本文為華為雲社群使用者原創內容,轉載時必須標註文章的來源(華為雲社群),文章連結,文章作者等基本資訊,否則作者和本社群有權追究責任。如果您發現本社群中有涉嫌抄襲的內容,歡迎傳送郵件至:[email protected]進行舉報,並提供相關證據,一經查實,本社群將立刻刪除涉嫌侵權內容。

  • 中秋節和大豐收的關聯?
  • 法律上怎樣舉證“沒有”?