.Net微服務實戰之技術選型篇

王者榮耀 

  去年我有幸被老領導邀請以系統架構師的崗位帶技術團隊,並對公司項目以微服務進行了實施。無論是技術團隊還是技術架構都是由我親自的從0到1的選型與招聘成型的,此過程讓我受益良多,因此也希望在接下來的系列博文儘可能的與大家分享我的經驗。

  古人有云:將軍難打無兵之仗。想要把微服務很好的實施也並非能一個人可以完成的事,一來需要有出色的運維提供支持,二來需要花時間做技術選型與攻關,三來還要開發兄弟們配合實施。因此,這次能順利實施並不是一個人的王者,而是團隊的榮耀。

  框架源碼:https://github.com/SkyChenSky/Sikiro (文末有說明)

工欲善其事,必先利其器

  以上是我們公司的技術棧(點擊圖片可在瀏覽器打開),除了統一配置中心沒有服務器資源和Hangfire還沒場景使用外,其他都已經上線使用了。  

  俗話說得好:工欲善其事,必先利其器。一個優秀的工程師應該善於使用框架和工具,在微服務這一塊的技術棧選型並非一蹴而就,也是我多次對比驗證後,並良好的集成到公司項目然後落地實施。這系列框架單純這麼去用其實是可以無縫集成的,但是在落實項目的時候,我爲了集成得更加友好和使用上更加便利,在基礎上做了擴展,例如SkyWalking添加Request和Response,CAP與Chloe.ORM的集成等,下文我會逐個分享。

  有需要的朋友可以參照我這套去實施,這樣大家就可以花更多的時間把精力放在業務、調優、拆分、設計等方面。

  此外大家看得出,我所有的技術棧基本上找的都是開源社區的比較出名的項目,沒有一個屬於自研的。這樣做的原因:

  •   快速搭建
  •   降低成本
  •   社區支持維護
  •   利於人才引進

  其實可以看出.Net不缺優秀的開源項目,那麼實施這麼久讓我唯一覺得深刻的印象是:缺少整合。

  之前我也跟不少同行討論過甚至在面試的時候,他們覺得應該自己造一個輪子,原因各種各樣,但唯獨缺少了希望在開源項目基礎上完善下這個原因。我也理解他們的心理,因爲“優秀”的工程師應該自己寫一套證明下自己。其實我認爲這也許是包容心的在作祟,我們應當求同存異,學會接受已經檢驗過的輪子,在基礎上完善您的需要,有必要還可以給社區做貢獻,雙贏。

原則

  我做技術選型的時候,堅持着三大原則,簡單、適合、運維優先

  在滿足需求的情況下,優先選擇輕量級的框架,因爲輕量級總比重量級的易學習,易於擴展,易於理解源碼。試想一下,有個框架什麼都很齊全,但是學習曲線高,在寫一個demo的時候各種踩“坑”找原因,還有可能出了問題不知道怎麼解決,除了開始你初認識該框架覺得他很厲害之外,後面使用每走一步都是阻礙和吐槽。

  在有限的資源、人力、時間,我們更新技術的同時還要保證業務的正常開展,我會優先選擇我比較熟悉的技術,我會將他們進行封裝、優化、集成,儘可能的減少開發人員對技術細節的認知負擔,儘可能以他們最熟悉的使用方式提供。此外,我們團隊是有運維崗,如果問題由運維解決更快、更方便則優先交給運維,儘可能讓開發關注數據流轉與業務流程。

  PS:我選型的時候不是一蹴而就的,下文可能我會提到某些框架工具我沒有去選擇原因,並不是否認它們存在的價值,而絕大問題是這些不適用於我們團隊。最後我向偉大的開源項目與其作者致敬。

  

微服務

 

  有一條盛傳於我們行業的公式:軟件 = 程序 + 軟件工程

  程序就是我們經常產出的算法、數據結構、腳本、框架、架構等。

  爲什麼稱之爲軟件工程?因爲這是具有科學方法論引導的、多人協作、有明確目標、有階段性的。從以前瀑布開發再到10年前盛行的敏捷開發最後到最近幾年流行的DevOps,可見開發模式也隨着技術架構更新也不停的演進。我們團隊選用了原型模式+DevOps模式來應對我們的微服務架構的開發。

  書本的教條主義我就不多說了,我對微服務的理解分爲服務

  如何微?微到什麼程度?我藉助兩樣東西,合理的系統架構分層DDD思想,兩者分別管理架構的縱向拆分與橫向拆分。

  架構分層,我採用了前後端分離+多層架構,自頂向下的依賴,各司其職。

  DDD在最近幾年非常流行,然而這並非新的技術,十幾年前就已經它的出現了。隨着微服務盛行,DDD的劃分域的化繁爲簡的思想與微服務的本質-不謀而合,因此DDD也隨之熱門起來。

  下面是我們的架構圖,這個話題在下一篇重點再討論。

服務

  我接下來用一段話描述一下服務化的需要。首先API網關作爲我們請求流量的入口,隔離了外網與內網的作用。接着開發人員得知道如何調用服務,那麼可以從註冊中心發現已註冊的服務的IP地址、端口的列表,這就是服務的註冊與發現;接着我們需要知道服務下接口路徑、請求與響應的格式,因此我們需要服務描述。滿足前面兩個條件後,我們就可以進行調用服務了,因此我們需要RPC框架進行服務通信。當服務運作後,我們需要服務監控來監控服務的運行情況以此方便調優。隨着服務拆分得越細、跨度越大,服務出問題的時候不容易定位,因此我們需要服務跟蹤進行問題定位。

  由上述可見組件主要包括以下6點:

  •   API網關
  •   服務描述
  •   服務註冊中心
  •   RPC框架
  •   服務監控
  •   分佈式鏈路跟蹤

API網關

  API網關主要起到了隔離內外網、身份驗證、路由、限流等作用。我用一個生活的例子搭地鐵比喻來描述下:過閘前我們需要經過安檢保證客流的安全性,上下班高峯期還會排隊進行限流,我們還可以通過看指示牌或者詢問工作人員瞭解到應該往什麼方向走,這就是路由。

  我們團隊選型了Kong和KongA作爲我們的API網關,Kong是一個在Nginx運行的Lua應用程序,由lua-nginx-module實現。Kong和OpenResty一起打包發行,其中已經包含了lua-nginx-module。基本功能有路由、負載均衡、基本認證、限流、跨域、日誌等功能,其他功能例如jwt認證可以通過插件進行擴展。

  有人會問爲什麼不用Ocelot?回答這個問題之前,我首先聲明我尊敬Ocelot項目與其開發者。

  1.易用性。需要二次開發,雖然對.Net開發者來說能接受,但不利於運維。

  2.性能。社區很多測試數據,據我瞭解就是kong 11K,Ocelot 3.5K,四捨五入3倍性能差,作爲流量的入口,性能這塊我還是比較注重的。

  3.可擴展性,Kong很多功能可以通過插件式按需使用與開發。

 

服務描述

  我們團隊採用了Swagger,以此來銜接前後端開發的接口對接,省去了編寫接口文檔的成本,此外也支持接口調試,讓開發效率提高不少。我們的服務都是以HTTP協議提供,對外API用RESTful風格,對內統一以POST的RPC風格提供。

服務註冊中心

  服務註冊,服務在發佈後自動把IP地址與端口註冊進服務中心;服務發現,通過調用服務中心的接口獲取到某服務IP地址與端口的列表。我們團隊選用Consul+Consul Tamplate+nginx,Consul是基於GO語言開發的開源工具,主要面向分佈式,服務化的系統提供服務註冊、服務發現和配置管理的功能。Consul的核心功能包括:服務註冊/發現、健康檢查、Key/Value存儲、多數據中心和分佈式一致性保證等特性。

  Consul作爲服務註冊中心的存在,但是我們服務發現只能拿到IP列表,我們使用RPC調用時還是得做負載均衡算法,於是使用了Consul Tamplate把服務列表同步到nginx的配置,那麼RPC框架就無需集成負載均衡算法經過nginx路由。

  開始選型我並沒有選擇Consul Tamplate,而是選擇了fabio的這個中間件。fabio是一個應用於Consul的輕量級、零配置負載均衡路由器,開始用的時候部署起來很方便、很簡單。後來上了Skywalking分佈式鏈路跟蹤系統,只要經過fabio路由的都無法把調用鏈串起來,雖然將就的用是沒什麼問題,但是Skywalking的調用鏈日誌無法很好的展示出來就會影響日後的問題排查。我當時花了兩天時間研究與issue提問,並沒有很好的結果,所以最後另外選擇了Consul Tamplate+nginx。

服務通信

  RPC框架主要三大核心,序列化、通信細節隱藏、代理。協議支持分TCP和HTTP,當然還有兩者兼容+集成MQ的。我們選擇了WebApiClient做客戶端,服務端仍是.Net Core WebAPI,主要考慮到WebAPIClient的輕量、易用,而且和Skywalking、Consul集成方便。我當時用的時候時.Net Core 2.2版本,gRPC並沒有集成進來。

  此外我也選擇過ServiceStack,ServiceStack的技術棧很全,缺點是依賴得很深,當時試用的時候,它所以依賴的一個底層包ServiceStack.Common的某個類與WebAPI衝突了,所以對於不熟悉該框架的我斷定存在依賴污染,無論我需要還是不需要都統統依賴進來了,然而我只是希望要一個簡單的RPC框架。此外還需要破解。

  Surging也作爲我當時選型的目標,開始也是我抱着最大希望的,因爲描述得很牛逼,什麼都是全得。然而深入去用的時候,沒有一個完整的文檔,入門demo也不友好,說實話我駕馭不住只能放棄。

服務跟蹤

  市面上的分佈式鏈路跟蹤系統基本上都是根據谷歌的dapper論文實現的,基本上都分三大塊,UI、收集器、代理(探針),原理大概是把涉及的服務鏈路的RequestID串起來。

  我們團隊選擇了SkyWalking作爲了項目的分佈式鏈路跟蹤系統,原因很簡單:易用,無侵入,集成良好。

  實施到我們項目的時候我做了點擴展,把Reqeust、Response、Header、異常給記錄了下來,並過濾了部分不需要記錄的路徑。

分佈式事務

  只要在分佈式系統,分佈式事務必不可缺。

分佈式事務
分類 理論 案例 中間件
強一致性 ACID 二階段提交 msdtc
最終一致性 BASE 本地消息表 CAP

 

 

 

 

  本地消息表是eBay在N年前提出的方案,而CAP以該思想實現的一門框架,原理大概是,本地業務表與消息憑據表作爲一個事務持久化,通過各種補償手段保證MQ消息的可靠性,包括MQ正常發佈與消費。

  我花了多天的時間專門測試了該框架可靠性,的確有保證。然而有個地方我認爲可以優化,Retry的查詢語句條件可以更加嚴謹點,只需要負責相應的Group進行Retry就好,沒必要全部都查詢出來,因爲這個問題我在測試環境與本地環境共同調試時,剛好兩個環境的Group不一致,導致會Retry失敗的問題。

限流與熔斷

  我的架構圖有兩個網關:入站API網關 出站API網關

  限流是針對外部流量的控制,保護了下游服務避免了流量衝擊後雪崩,可以通過入站API網關的限流開關與策略控制。

  熔斷是保護上游服務避免因爲下游的異常而且拖垮,更多是針對不可控的第三方依賴,例如微信、支付寶等。可以通過出站API網關的熔斷開關與策略控制。

框架源碼

  寫到這裏,本篇的分享差不多要結束了,我將開源我們公司的工具庫,有需要的朋友可以去使用。

  •   Sikiro.Tookits -公共基礎庫
  •   Sikiro.Nosql.Redis-StackExchange.Redis的基本封裝
  •   Sikiro.Nosql.Mongo-mongodb驅動封裝更新、排序等支持lambda
  •   Sikiro.MicroService.Extension-RPC註冊,微服務框架-服務註冊,終端跟蹤忽略
  •   Sikiro.Chloe.Extension-支持多數據、事務封裝、分頁、IOC
  •   Sikiro.Chloe.Cap-把Chloe,ORM與CAP整合
  •        SkyApm.Diagnostics.AspNetCore|SkyApm.Diagnostics.HttpClient  修改了兩個庫的-XXXXXTracingDiagnosticProcessor的代碼,主要增加request、response、header的記錄。

  額外說明下DotNetCore.CAP.MySql,這個是我從CAP源碼拷貝過來然後改了MySql.Data的依賴,原本CAP.MySql是用的MySqlConnector,和我的Chloe.ORM衝突了。

  開源項目的庫使用:保證包名不修改的前提下,增加版本號引入項目就可以覆蓋。

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章