Bing 廣告平台遷移到 .net6

  • 2022 年 10 月 9 日
  • 筆記

   原文鏈接 //devblogs.microsoft.com/dotnet/bing-ads-campaign-platform-journey-to-dotnet-6/

   廣告組件平台對於微軟搜索平台能給用戶提供好的用戶體驗至關重要,這個平台支持超過450000個廣告商允許他們創建廣告。

   在一秒鐘內,該平台處理數千個web請求,而且時延不超過100毫秒。

 

 

   支撐這個平台的是幾十個分佈式服務,現在這些服務都要構建在.net 6 之上,運行在微軟自己的AKS雲服務的Linux容器中。

   要實現這一目的其實並不容易,博文後面部分就介紹多年來我們將代碼升級到.net 6所面臨的挑戰以及如何解決的。(注意:.net core 現在已經統一到.net 平台,這裡提到.net 都指.net core,如果是.net framework 會明確指出)

代碼倉庫概述

  我們有超級多的代碼,光c#項目就超過600個,一下就是分解以後的代碼佔比圖

 

 

 我們有超過800萬行代碼,其中c#超過了700萬行,統計代碼行數雖然不能說明代碼倉庫的全部,但是可以給出一個粗略的印象我們有多大的工作量要搞。在這超過600多個的項目中,我們引用了500多個不同的nuget包,而且很多依賴項會對遷移產生影響。

我們的出發點

多年來我們的服務發生了很大的變化,而且託管他們的基礎設施也發生了巨變,從物理機到雲服務。以前我們是在windows服務託管在iis上,此外我們還有大量服務構建使用wcf,這是問題的複雜所在。所以最開始我們的遷移只是從物理機的windows服務遷移到了雲上的windows 虛擬機,仍然是windows-only。

為什麼遷移到.net(.netcore)

為什麼我們要花費兩年多時間從.netframework -.net 我們的技術團隊給出了以下理由:

跨平台

好處很明顯,不用跟windows綁定了。

.Net  代表未來

.net framework 4.8 是計劃更新的 最後一個版本,很多新的特性,以及性能優化的創新都只能在.net 上可用,堅持.net framework 死路一條。

開源

更好用的工具

dotnet cli 工具無需IDE藉助命令行終端就可以完成很多工作,其他的感覺不是工具,就不列舉了。

我們的遷移過程

對於類庫:

.NET Framework 4.6 -> .NET Framework 4.7 -> .NET Standard 2.0

對於應用:

.NET Framework 4.6 -> .NET Framework 4.7 -> .NET Core 3.1 -> .NET 5 -> .NET 6

.NET Standard

簡單理解這是一個提供了一部分公共api抽象的定義,在.net framework 跟.net 會有相應的實現。

挑戰

主要是我們依賴的那500多個包如果更新了會有很多break changes,比如Unity這個IOC庫,支持 .NET Standard 的可用版本已經完全重寫了其 API,我們必須更新數萬行代碼才能與這些更改兼容。

綁定重定向地獄

WCF

我們重度依賴 WCF,超過45 個服務構建在wcf之上,但是現在普遍看法是基於REST的服務是未來,但是我們不能這樣,因為我們有無數個現有客戶大量構建在這些 WCF 服務之上,並使用我們的 SDK 直接調用它們,告訴我們的付費客戶,嘿,你得用GRPC這類新東西沖洗你的客戶端,這顯然不現實,所以這是一個大問題。

解決問題

不兼容的Nuget 包

這個最需要花費時間了,對於開源的不支持.net standard 的我們在內部自己打一個補丁包使得支持,對於不開源的我們也會選擇反編譯使得與.net standard 兼容。

綁定重定向問題

WCF

最終,微軟決定針對.Net d的wcf有限子集開源Core wcf捐獻給開源社區。CoreWCF 對來自傳統 WCF 的 System.ServiceModel 命名空間的許多現有類型使用全新的命名空間,因此轉換現有服務並非易事。此外,我們的代碼庫中使用了如此廣泛的通用代碼,以至於我們需要在轉換過程中支持在 CoreWCF 服務和 .NET Framework 服務中運行該代碼。我們最終使用multi-targeting (在代碼中可以使用條件編譯指定不同平台實現不同代碼,在工程文件中指定目標平台使用TargetFrameworks,這樣可以指定多個來實現這一目標。過程很艱辛,單總算遷移完了,所以如果您需要託管 SOAP 服務並且想要在 .NET 6 上運行所帶來的高性能,那麼 CoreWCF 是一個很好的答案。

結果

性能層面

值得嗎?做了這麼多答案是肯定的。這圖顯示了我們的一項服務的延遲改進,這僅僅是由於將其更改為目標 .NET 並重新編譯。

 

 

 這甚至沒有任何新的功能做任何優化,這些都是.net 團隊做的運行時級別的改進,下圖是另一個例子,我們遷移到Core wcf 以後內存使用情況

 

 

 現代服務架構

除了這些明顯的性能優勢之外,進入 .NET 為我們提供了從 IIS 和 Windows 遷移到在 AKS 中託管的 Linux 容器內運行的 Kestrel 服務器的機會,我們現在可以使用所有可用於管理和配置雲服務的現代化工具和資源(比如K8s)

總結

對於計劃將大型現有 .NET Framework 代碼庫遷移到 .NET 6 及更高版本的其他人,可以應用從我們的經驗中吸取的教訓:

1. 現有代碼從4.7 升級到4.8

2. 將所有項目遷移到新的 SDK 格式,以便它們在執行任何其他操作之前使用 PackageReference。(說實話我忘了.net framework 是咋回事了,所以上面這塊兒原文也沒怎看就沒翻譯)

3. 使用 .NET Standard 作為橋樑,允許在遷移過程中在 .NET Framework 和 .NET 項目之間共享庫代碼

4. 使用集中的包引用來極大地簡化向較新 NuGet 包的過渡。

遷移完成了,我們的腳步並沒有停下,接下來我們會研究.net的一些新的特性:

1. 找到我們可以使用 Span\<T> 來減少堆分配並提高性能的地方,例如,我們的代碼有一些地方可以檢查兩個位元組緩衝區是否相同。我們可以使用高度優化的 SequenceEqual 方法,而不是遍歷每個位元組並測試相等性

“`return bufferA.AsSpan().SequenceEqual(bufferB);“`

以下是benchmark

 隨着我們的前進,重寫我們的一些代碼以專門利用新的語言和運行時特性,這將繼續是一項有趣且非常有益的練習