去年我有幸被老領導邀請以系統架構師的崗位帶技術團隊,並對公司項目以微服務進行了實施。不管是技術團隊仍是技術架構都是由我親自的從0到1的選型與招聘成型的,此過程讓我受益良多,所以也但願在接下來的系列博文儘量的與你們分享個人經驗。nginx
古人有云:將軍難打無兵之仗。想要把微服務很好的實施也並不是能一我的能夠完成的事,一來須要有出色的運維提供支持,二來須要花時間作技術選型與攻關,三來還要開發兄弟們配合實施。所以,此次能順利實施並非一我的的王者,而是團隊的榮耀。git
框架源碼:https://github.com/SkyChenSky/Sikiro (文末有說明)github
以上是咱們公司的技術棧(點擊圖片可在瀏覽器打開),除了統一配置中心沒有服務器資源和Hangfire還沒場景使用外,其餘都已經上線使用了。 面試
俗話說得好:工欲善其事,必先利其器。一個優秀的工程師應該善於使用框架和工具,在微服務這一塊的技術棧選型並不是一蹴而就,也是我屢次對比驗證後,並良好的集成到公司項目而後落地實施。這系列框架單純這麼去用實際上是能夠無縫集成的,可是在落實項目的時候,我爲了集成得更加友好和使用上更加便利,在基礎上作了擴展,例如SkyWalking添加Request和Response,CAP與Chloe.ORM的集成等,下文我會逐個分享。算法
有須要的朋友能夠參照我這套去實施,這樣你們就能夠花更多的時間把精力放在業務、調優、拆分、設計等方面。sql
此外你們看得出,我全部的技術棧基本上找的都是開源社區的比較出名的項目,沒有一個屬於自研的。這樣作的緣由:mongodb
其實能夠看出.Net不缺優秀的開源項目,那麼實施這麼久讓我惟一以爲深入的印象是:缺乏整合。後端
以前我也跟很多同行討論過甚至在面試的時候,他們以爲應該本身造一個輪子,緣由各類各樣,但惟獨缺乏了但願在開源項目基礎上完善下這個緣由。我也理解他們的心理,由於「優秀」的工程師應該本身寫一套證實下本身。其實我認爲這也許是包容心的在做祟,咱們應當求同存異,學會接受已經檢驗過的輪子,在基礎上完善您的須要,有必要還能夠給社區作貢獻,共贏。跨域
我作技術選型的時候,堅持着三大原則,簡單、適合、運維優先。瀏覽器
在知足需求的狀況下,優先選擇輕量級的框架,由於輕量級總比重量級的易學習,易於擴展,易於理解源碼。試想一下,有個框架什麼都很齊全,可是學習曲線高,在寫一個demo的時候各類踩「坑」找緣由,還有可能出了問題不知道怎麼解決,除了開始你初認識該框架以爲他很厲害以外,後面使用每走一步都是阻礙和吐槽。
在有限的資源、人力、時間,咱們更新技術的同時還要保證業務的正常開展,我會優先選擇我比較熟悉的技術,我會將他們進行封裝、優化、集成,儘量的減小開發人員對技術細節的認知負擔,儘量以他們最熟悉的使用方式提供。此外,咱們團隊是有運維崗,若是問題由運維解決更快、更方便則優先交給運維,儘量讓開發關注數據流轉與業務流程。
PS:我選型的時候不是一蹴而就的,下文可能我會提到某些框架工具我沒有去選擇緣由,並非否定它們存在的價值,而絕大問題是這些不適用於咱們團隊。最後我向偉大的開源項目與其做者致敬。
有一條盛傳於咱們行業的公式:軟件 = 程序 + 軟件工程。
程序就是咱們常常產出的算法、數據結構、腳本、框架、架構等。
爲何稱之爲軟件工程?由於這是具備科學方法論引導的、多人協做、有明確目標、有階段性的。從之前瀑布開發再到10年前盛行的敏捷開發最後到最近幾年流行的DevOps,可見開發模式也隨着技術架構更新也不停的演進。咱們團隊選用了原型模式+DevOps模式來應對咱們的微服務架構的開發。
書本的教條主義我就很少說了,我對微服務的理解分爲微和服務。
如何微?微到什麼程度?我藉助兩樣東西,合理的系統架構分層與DDD思想,二者分別管理架構的縱向拆分與橫向拆分。
架構分層,我採用了先後端分離+多層架構,自頂向下的依賴,各司其職。
DDD在最近幾年很是流行,然而這並不是新的技術,十幾年前就已經它的出現了。隨着微服務盛行,DDD的劃分域的化繁爲簡的思想與微服務的本質-拆不謀而合,所以DDD也隨之熱門起來。
下面是咱們的架構圖,這個話題在下一篇重點再討論。
我接下來用一段話描述一下服務化的須要。首先API網關做爲咱們請求流量的入口,隔離了外網與內網的做用。接着開發人員得知道如何調用服務,那麼能夠從註冊中心發現已註冊的服務的IP地址、端口的列表,這就是服務的註冊與發現;接着咱們須要知道服務下接口路徑、請求與響應的格式,所以咱們須要服務描述。知足前面兩個條件後,咱們就能夠進行調用服務了,所以咱們須要RPC框架進行服務通訊。當服務運做後,咱們須要服務監控來監控服務的運行狀況以此方便調優。隨着服務拆分得越細、跨度越大,服務出問題的時候不容易定位,所以咱們須要服務跟蹤進行問題定位。
由上述可見組件主要包括如下6點:
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失敗的問題。
寫到這裏,本篇的分享差很少要結束了,我將開源咱們公司的工具庫,有須要的朋友能夠去使用。
額外說明下DotNetCore.CAP.MySql,這個是我從CAP源碼拷貝過來而後改了MySql.Data的依賴,本來CAP.MySql是用的MySqlConnector,和個人Chloe.ORM衝突了。