車家號作為一個PGC平臺,聚合了全網大多數汽車行業的專家及意見領袖,每天為用戶提供大量的汽車類優質內容。用戶日瀏覽量在幾千萬級,後端的介面也承載億級的日訪問量。 車家號WEB、API、後臺管理等系統採用 .net4.5進行開發。一直以來為用戶及調用方提供了穩定的服務。由於其只能運行於W... ...
一、背景
車家號作為一個PGC平臺,聚合了全網大多數汽車行業的專家及意見領袖,每天為用戶提供大量的汽車類優質內容。用戶日瀏覽量在幾千萬級,後端的介面也承載億級的日訪問量。
車家號WEB、API、後臺管理等系統採用 .net4.5進行開發。一直以來為用戶及調用方提供了穩定的服務。由於其只能運行於Windows平臺上,其擴展及遷移的能力受到了極大限制。需要將車家號業務轉移到Linux平臺,可以進行更為靈活的運維,並且具有容器化能力。
方案之一,用java重寫,這個對於一個已經維護多年的有大量的業務邏輯在裡面的系統來說,工作量是相當大的。只重寫介面相對簡單,但如果將PC 及後臺管理進行重寫工作量極大。或前後分離NodeJS 方式,這樣也會給前端開發及測試帶來巨大大的壓力。還有更重要的一點,需求不斷的提出來,還要不斷有新特性加入進來,如果維護兩套異構語言的系統,會給業務及系統的穩定性帶來很大的風險。
另一個方案,項目從.net framework 升級為dotnet core,基本上語法方面不需要變化,業務代碼不需要變化,新需求的加入,單向同步代碼即可。同時,.net core 是跨平臺的,另外,可以使用docker進行彈性擴容縮容。.net core 在性能方面從官方數據及後來的一些測試看的確有了明顯的提升。
所以,基於這些主要原因,我們選擇了方案二,.net core ,並且容器化。
二、人員投入
時間 |
天數 |
人員 |
人日 |
2月21日-2月27日 |
5 |
1人 |
5人日 |
4月1日 -5月13日 |
27 |
0.5人 |
13.5人日 |
5月14日-5月24日 |
9 |
1.5 人 |
13.5人日 |
5月27日-6月6日 |
9 |
2.5人 |
22.5人日 |
合計: |
54.5人日 |
升級前期,因存在嘗試、學習等因素,人員投入較少。後面調整重心,加強人力投入到升級工作中,快速推進升級上線。
公司範圍內,我們是第一個將較大項目進行.net core 升級的團隊。沒有太多dotnet core的積累,無成功案例可借鑒,同時大量工作也用在了構建基礎設施。未來類似規模項目進行升級,時間及人力投入方面會大大減少。
三、升級效果
1.性能提升
從5.25日開始逐步灰度,tp99也從100ms逐步降到45ms左右,到發稿為止穩定保持於45ms以下。
2.無感知升級
介面,WEB,後臺管理等從外觀及行為上沒有的變化,做到用戶及調用方無感知。
3.彈性
由於core 可運行於Linux上,可以藉助於之家雲平臺,在容器中彈性擴容。
四、主要過程
在業務需求不斷變更的同時,在第一次全量完成代碼同步後,嚴格從原有業務主幹(.net framework + TFS)單向同步到新版本業務主幹(.net core+ git),保證業務行為的一致性,並不斷同步發佈新版本進行灰度,兩個版本的行為保持一致性,升級過程無縫切換。如下圖所示:
1.準備階段
由於.net core相容性及某些功能的缺失,需要使用重新編譯或編寫新組件進行相容,這些作為基礎組件,需要進行更多的單元測試及測試項目中的測試,保證其它正確性,穩定性及相容性。
-
.net core 已經無法使用.net framework的組件,所有原有組件都需要重新編譯適配。(見組件列表)
-
Asp.net core 中 有些功能已經不再支持,需要重新適配。見附相容組件
2.編譯通過階段可運行階段
-
自底向上,逐個項目進行升級。
-
卸載所有項目。
-
從基礎的底層的類庫進行升級,通常其依賴著準備階段中提到的各種組件。
-
底層公共項目,通常都可以順利編譯通過。
-
對於實在無法編譯通過的項目,根據其作作決定是否先註釋掉,並用異常拋出的方式先行替代(throw new NotImplementedException()),防止未來的誤調用或忘記實現。
-
根據原有項目間依賴並系,逐步上向。
-
先將介面項目編譯通過,由於其不依賴UI等,比較容易通過,替換相容,運行。
-
WEB 項目
-
Asp.net Core 與 asp.net 項目結構不同需要進行調整。
-
批量替換命名空間
-
編譯,如果有不能通過的,看是否能快速解決或重要性,決定是否跳過。
經過上面的操作,項目基本可以運行,完成升級過程的第一步。
3.細化解決問題並同步代碼
Core 版代碼使用git 進行管理,從 TFS某個時間點進行快照,並Push到git庫。作為基礎,進行升級改造,對組件及代碼級別的不相容進行調整。後面新業務也是在這個版本的基礎上進行增量同步。
後面的需求還在繼續在TFS上提交,採用由TFS到Git單向同步的方式,TFS上的新需求由負責的開發人員同步至git 上。這種情況將持續到全量上線。
這個階段經歷了大致兩個星期。
4.測試
-
開發自測:開發人員 自動用例生成工具,生成測試用例,使用JMeter 測試,大規模的覆蓋,快速發現並修複問題。另外,使用自動化介面對比工具,完成全量介面對數據對比,完全節省了人工對於介面的重覆測試,快速發現並定位問題。
-
測試人員測試:對於一些重要場景,如作者發現文章及與財務相關的功能,需要人工方式進行二次驗證。
5.灰度上線
雖然經過了多次測試,但一些場景可能會無法覆蓋,需要採用灰度方式。將新版加入負載,經過從萬分之一,百分之一,十分之一,二分之一,最終全量。
在灰度過程中,對錯誤日誌、訪問日誌、性能日誌等多維度進行監測,保證系統負載在可控範圍內,並且發現有異常及時修複或回調流量。
6.全量切換
系統上是先從PC開始的,等PC上線穩定以後,再上線介面,服務,對外介面等其它系統。
PC:在構成方面雖然比較複雜,但它前面有CDN 及SCS,萬一齣現問題,來得及進行補救。
介面:流量大,但結構相對簡單,如果出現問題,影響面比較大,所以,在PC成功切換後,有了經驗及信心,再進行切換。
後臺管理:運營使用,有問題可能快速反饋,並能快速修複。
服務:核心業務已經上線,服務,也要跟著上線了。
Open介面:由於流量及用戶都不多,並且大多數是Post介面,需要更充分的測試,選擇最後上線。
6月6日 最終完成所有系統的全量的上線。
五、經驗
1.備用方案,隨時可回滾
在最初制訂方案的時候,考慮到極有可能出現意外,導致新系統有不可預知的問題。需要具有隨時切換回舊系統的能力,當然,這個也是灰度的基礎。
保證可以切回,要保證:
(1)最重要的,代碼要保持同步。
(2)同步發佈,新舊兩個環境都要保持最新,同步。
(3)在完成穩定遷移,舊的環境一直保持高可用。
在本次遷移過程中,由於Core所在集群故障,緊急切回舊系統,這樣避免了一次非常嚴重的事故。
2.擴容要謹慎
升級過程中某天,系統的流量突然異常。系統開始變慢,但還能勉強頂住。
隨後做出了擴容決定,從8個實例,直接擴容為30個。接下來,就是連鎖反應,WEB,API,相繼告急,性能極度下降。資料庫不斷報警。
對於我們從傳統的單體應用進行遷移至Docker, 不要急於擴容,一定要看到你的系統是否有單點,這個單點是否可以避免。
3.自測工具
本次升級,測試人員投入大概兩個半天,進行測試。剩下的都由開發人員進行自測。使用工具進行大規模的覆蓋。
測試工具,對比工具等都根據實際業務進行自主開發。在未來也可以重覆用於其它或日常工作中。
介面對比及批量對比測試:
自動生成用例及在JMeter中測試:
4.日誌工具
.net core 在 Linux下是沒有相應的訪問日誌,另一個途徑也可以從Nginx獲取訪問日誌,但它與我們現有日誌分析及監控系統不相容。
基於.net core 中間件,對訪問進行攔截並記錄日誌。使用Udp 方式發送至日誌收集系統。
這樣對系統的訪問數量及處理性能通過報表有直觀的瞭解。
5.監控工具
.net core 由於是一個新的生態,沒有類似於windows下的性能記數器,或Java的Jmx這種工具對性能進行監控。所以,我們需要自己來做一套監控解決方案。
(1) 做SDK,收集系統運行性能指標。
(2) 提交至Logstash
(3) Logstash 寫入 ES
(4) 通過Grafana進行圖形化展示。
6.緩存註解
從Java中學習到Spring boot 的Cache註解,按其思路實現註解並應用於.net core , 一個註解,省去大片代碼。
代碼中使用註解示例效果:
使用註解前後效果TP 99對比:
7.緩存的一致性
由於我們的系統極度依賴於緩存層,並且服務會更新緩存,新舊的系統如果可以使用同一套緩存,使用同一個Key,那可以減少很多必要的麻煩。
我們重新編譯了ServiceStack.Redis 。保證了緩存的行為的一致性,這為我們節省了至少三分之一的工作量,使得新舊各系統中都是一致的緩存,一致數據。
8. Json 框架
在當前系統中用到了多個框架,Json.net , FastJson , MS Json , 在代碼調試過程中,新舊系統使用返回數據不一致,也發生各種序列化異常,經過分析,原來一舊代碼與新代碼使用不同的Json序列化方式。
在系統遷移過程中,由於多處使用了這三個框架,只重構掉了一種MS Json,現在還有 Json.net , FastJson 兩種在運行著。
後面系統穩定後,應該僅保留一種序列化框架。
9.Docker性能
當時轉core一個原因就是想使用 docker 進行動態擴容、縮容的特性。本次升級過程中,由於所在機房資源不足,docker集群不穩定,遷移機房暫時不具備條件等原因。只能將部分業務,如後臺,服務,Open介面等運行在docker上,其它隨時壓力較大的如WEB ,介面等運行在VM上,在遷移機房後,再將大流量產品進行遷移。
10. 先運行再解決Bug
過程中不要把問題放在一個點上,先保證系統可以整體運行,再去解決某個點的技術問題,為團隊樹立信心,讓每個人都可以看得到它是可運行的。
六、最後
在本次升級過程中,讓我們感受到升級.net core很小的學習曲線,快速入手,性能的提升也是一種驚喜。還有,大多數常用的開源組件對core提供了支持,改極少或不用改代碼即可使用。
同時,升級過程中,積累了經驗,產出了通用性功能及組件,對於其它系統的升級具有指導和借鑒意義。
還有,這個成功的案例,也給有升級想法的團隊,樹立信心,使他們可以更快邁出這一步。
升級過程發現了一些問題,促使我們對於架構進一步的思考,在後面通過對架構的優化使系統具有更好的擴展能力。
七、附錄
1.組件
-
dotcoreActionMessageSdk 發送HttpMessageQuene 組件
-
dotNews.ServiceStack.Redis Redis連接組件
-
dotcoreAutohome.DataHelper Sqlserver連接組件
-
dotCoreCasClient SSO 組件
-
dotcorejobclient 分散式Job組件
-
dotcoreNews.Comm 通用類型組件
-
dotcoreNews.Common.Extends.Log ELK 日誌組件
-
dotcoreNews.Common.Extends.Upload 圖片上傳組件
-
dotcoreNews.Common.UploadObject 文件上傳組件
-
dotcoreWebdiyer.MvcPagerCore ajax分頁組件
2.相容手段
-
dotcoreHttpContextCurrentCore組件 asp.net core 已經沒有HttpContext.Current, 本項目實現 System.Web.HttpContext.Current 功能,最大程度相容舊代碼,減少重寫量
-
core 不再支持 WCF ,未來版本是否支持社區還在爭論中,我們採用重寫路由的方式進行相容。
-
Html.Action 不再支持,重寫這個方法,為了區別更別為Html.RenderAction,參數功能不變。
-
EF Repository 模式代碼,基於 sqlSugarCore 改寫。保持調用外觀不變。
-
Microsoft.Practices.ServiceLocation 不再支持,使用 Castle.Core
-
using System.Web.Mvc,不再支持, 使用 usingMicrosoft.AspNetCore.Mvc;
-
Areas 的支持不再,使用 router
-
Ashx 一般處理程式不支持,改用 router相容
-
[assembly: PreApplicationStartMethod] 不支持,改用 StartUp.cs中調用
-
HTTP handlers and modules 改為 middleware
3. 工具支持
-
自製應用程式指標監控
-
自製批量用例自動生成
-
自製指量介面比較工具
-
Jmeter
作者|
本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/Autohome-netframework_to_netcore.html