研发协同平台持续交付之代理服务实践
源寶導(dǎo)讀:插件系統(tǒng)大大提高了系統(tǒng)的擴展性,有利于模塊化開發(fā)。系統(tǒng)發(fā)布后,當我們需要對系統(tǒng)進行擴充,可以再不編譯的情況下更新系統(tǒng)的插件即可。基于熱拔插的軟件系統(tǒng)提高了持續(xù)交付能力,在添加新特性的同時保持核心結(jié)構(gòu)穩(wěn)定。本文將介紹研發(fā)協(xié)同平臺代理服務(wù)在插件化設(shè)計方面的技術(shù)實踐。
一、背景
? ? 插件系統(tǒng)大大提高了系統(tǒng)的擴展性,有利于模塊化開發(fā)。系統(tǒng)發(fā)布后,當我們需要對系統(tǒng)進行擴充,可以再不編譯的情況下更新系統(tǒng)的插件即可。基于熱拔插的軟件系統(tǒng)提高了持續(xù)交付能力,在添加新特性的同時保持核心結(jié)構(gòu)穩(wěn)定。
? ? ERP是To B產(chǎn)品, To B產(chǎn)品的持續(xù)交付和To C產(chǎn)品的持續(xù)交付是有很大不同的。To B產(chǎn)品需要交付給成千上萬家客戶,每家客戶的產(chǎn)品和環(huán)境都不一樣,要保證ERP產(chǎn)品能持續(xù)的、穩(wěn)定的交付給成千上萬家客戶,在客戶端需要一個代理服務(wù)來協(xié)同完成。由此,ERP代理服務(wù)應(yīng)運而生。
二、設(shè)計架構(gòu)
? ? ERP 代理服務(wù)在客戶端負責(zé)產(chǎn)品的持續(xù)交付承擔(dān)在重要的角色,ERP 產(chǎn)品每次的最終交付到客戶,它需要在客戶端完成很多重要的工作:更新包到客戶機 IIS、收集客戶機更新日志、服務(wù)器運行狀態(tài)、同時由于客戶機網(wǎng)絡(luò)環(huán)境安全限制時也承擔(dān)著信息的中轉(zhuǎn)服務(wù),為了日后公司產(chǎn)品的多樣化、環(huán)境的多變性、產(chǎn)品的迅速更新,我們需要隨時做出的相應(yīng)響應(yīng),并能迅速的完成支撐,因此 ERP 代理服務(wù)在保證自身穩(wěn)定的同時,需要快速的更新自己的產(chǎn)品所承擔(dān)的功能職責(zé)。
? ? ERP代理服務(wù)的核心需求如下:
自更新。
自恢復(fù)。
灰度更新。
業(yè)務(wù)邏輯可升級。
業(yè)務(wù)邏輯可靈活擴展。
較好的容錯機制,在網(wǎng)絡(luò)不穩(wěn)定或其他其他因素的影響下,也能穩(wěn)定持續(xù)運行。
? ? 基于以上需求,我們新設(shè)計了 ERP 代理服務(wù)的架構(gòu)方案,我們將原有 ERP 代理服務(wù)改造為熱拔插的插件支撐方案。
三、框架設(shè)計
? ? 代理服務(wù)通過支持三種類型的插件熱拔插,實現(xiàn)多種多樣的需求:
類庫插件:實現(xiàn)服務(wù)端下發(fā)命令執(zhí)行業(yè)務(wù)處理。
Web 插件:可靈活的提供 API 或者視圖在頁面中呈現(xiàn)。
Console 插件:通過外部啟動 exe 程序的方式,守護進程或者其他特殊需求。
? ? 插件功能高內(nèi)聚,與框架低耦合,開發(fā)人員根據(jù)規(guī)范,開發(fā)完成并進行單元測試通過后,打包并安裝到宿主中,即可使用。
3.1、插件加載模式
? ? 通過向更新服務(wù)獲取用戶可用的插件集合,動態(tài)新增、升級、卸載插件,通過在內(nèi)存中加載 DLL 的方式熱拔插插件,能有效的更新客戶端服務(wù)器上的代理服務(wù)功能,靜默升級插件后續(xù)將讓我們的服務(wù)有很高的拓展性。
3.2、目前規(guī)劃的插件
? ? 針對現(xiàn)有的業(yè)務(wù)場景我們規(guī)劃了多個插件,并用于實現(xiàn)不同的功能模塊:
更新包插件:用于更新ERP產(chǎn)品到客戶服務(wù)器的站點中。
更新服務(wù)API插件:用于提供產(chǎn)品注冊等接口給ERP站點調(diào)用,將相應(yīng)信息上報至服務(wù)端。
守護插件:守護代理服務(wù)運行狀況,負責(zé)重啟、更新代理服務(wù)。
? ? 其中部分插件為熱加載方式,我們可快速的進行插件迭代開發(fā),并發(fā)布上線(同時支持灰度發(fā)布),能快速的應(yīng)對產(chǎn)品需求。
四、實現(xiàn)方案
? ??我們從現(xiàn)有的 ERP 代理服務(wù),以及 ERP 的交付特性,來考慮我們系統(tǒng)的可拓展性、可維護性,同時在一定程度上引入更新的技術(shù)棧來。我們從三個部分實現(xiàn)我們的插件系統(tǒng):
主程序開發(fā)。
公共接口的基礎(chǔ)插件類庫開發(fā)。
各類插件開發(fā)。
? ? 以上,我們需要實現(xiàn)各部分之間的接口規(guī)范。
4.1、技術(shù)調(diào)研
? ? 我們調(diào)研了最新的 ASP.NET CORE 3.1(https://docs.microsoft.com/zh-cn/aspnet/core/?view=aspnetcore-3.1) 技術(shù)棧,它能很好的支持我們的熱拔插的系統(tǒng)設(shè)計方案,微軟也給出了簡單的示例,以下是提供一些關(guān)鍵技術(shù)文檔進行參考:
ASP.NET CORE 3.1 熱拔插插件的實現(xiàn)方案。
(https://docs.microsoft.com/zh-cn/dotnet/standard/assembly/unloadability?)
ASP.NET CORE 支持獨立部署方式避免系統(tǒng)需要安裝.NETCORE SDK。
(https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/?view=aspnetcore-3.1)
引入 NLog 日志組件。
(https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3)
? ? 在以上技術(shù)棧的基礎(chǔ)上,我們對基礎(chǔ)知識加以深入研究,引入項目的過程中不斷思考優(yōu)化項目結(jié)構(gòu)。
4.2、實現(xiàn)解析
? ? 我們改變原有ERP代理服務(wù)的插件方案,采用 AssemblyLoadContext 來實現(xiàn)熱拔插插件方案,同時重新調(diào)整主程序框架根據(jù)支持多種的插件的的加載,將公共服務(wù)接口提升到基礎(chǔ)插件類庫中以便繼承插件可以通過依賴注入的方式使用我們的公共接口。
4.2.1、封裝插件加載器SDK
功能特性
支持將dll以文件流的方式加載(可卸載)。
支持將占用dll文件的方式加載(可卸載)。
支持插件文件變更卸載插件并自動重新加載。
設(shè)計目錄:
核心代碼:
// 程序集加載 protected override Assembly Load(AssemblyName assemblyName) {var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);if (assemblyPath == null){var localFile = Path.Combine(Path.GetDirectoryName(MainAssemblyToLoadPath), assemblyName.Name + ".dll");if (File.Exists(localFile)){assemblyPath = localFile;}else{return null;}}// 內(nèi)存方式加載if (IsLoadInMemory){using var file = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read);return LoadFromStream(file);}else{return LoadFromAssemblyPath(assemblyPath);} } /// <summary>/// 卸載插件/// </summary>[MethodImpl(MethodImplOptions.NoInlining)]public bool Unload(){ if (!_context.IsCollectible) { Console.WriteLine($"AssemblyLoadContext 不是可回收的"); return false; } if (!_context.IsLoadInMemory) { var mainAssemblyToLoadPath = _context.MainAssemblyToLoadPath; // 判斷資源是否存在引用 var weakRef = new WeakReference(_context, trackResurrection: true); _context.Unload(); _context = null; // 嘗試至多20次垃圾回收,將資源引用釋放 for (int i = 0; weakRef.IsAlive && (i < 20); i++) { GC.Collect(); GC.WaitForPendingFinalizers(); // 睡眠500毫秒,確保GC回收完畢,資源引用釋放 Thread.Sleep(500); } if (weakRef.IsAlive) { Console.WriteLine($"程序集({mainAssemblyToLoadPath})回收失敗,請確認程序集未被使用"); } return !weakRef.IsAlive; } else { _context.Unload(); _context = null; return true; }}4.2.2、 主程序功能模塊解析
功能特性及結(jié)構(gòu)分層:
全局異常過濾。
計劃任務(wù):命令定時執(zhí)行任務(wù)、定時上報任務(wù)、定時更新插件任務(wù)。
插件加載器:類庫插件加載器、Web插件加載器、控制臺(exe)類插件加載器。
中間件:請求記錄日志中間件。
應(yīng)用程序全局配置。
服務(wù)層:命令服務(wù)、插件緩存、插件服務(wù)、上報服務(wù)。
目錄結(jié)構(gòu):
應(yīng)用設(shè)計結(jié)構(gòu):
4.2.2.1、 啟動說明
? ? ERP代理服務(wù)啟動前會注冊所需服務(wù):
注冊配置中心服務(wù)。
注冊日志服務(wù)。
注冊全局異常處理。
注冊更新服務(wù)。
注冊Web插件View視圖引擎目錄。
注冊各類型插件加載器(類庫插件、Web插件、控制臺類插件)。
注冊插件緩存服務(wù)、命令服務(wù)、上報服務(wù)、插件管理服務(wù)。
注冊上報調(diào)度任務(wù)、命令執(zhí)行調(diào)度任務(wù)、更新及加載插件調(diào)度任務(wù)。
? ? 并根據(jù)配置中心配置的服務(wù)啟動地址啟動,改地址用于ERP產(chǎn)品調(diào)用代理服務(wù)上報產(chǎn)品信息等接口調(diào)用。
4.2.2.2、API接口服務(wù)說明
? ? 目前由更新服務(wù)API插件提供給ERP產(chǎn)品將相應(yīng)信息提交至更新服務(wù),以便后續(xù)我們能很好的利用信息完成客戶的產(chǎn)品交付:
注冊產(chǎn)品信息。
獲取服務(wù)器狀態(tài)信息。
獲取更新包更新狀態(tài)。
獲取更新頁面地址。
獲取更新服務(wù)狀態(tài)。
? ? 同時由于該插件可以后期升級并熱加載,我們可以根據(jù)需求快速迭代插件,為ERP產(chǎn)品提供更多的所需服務(wù)。
4.2.2.3、調(diào)度任務(wù)說明
? ? 應(yīng)用啟動后,通過異步方式啟動調(diào)度任務(wù),在調(diào)度任務(wù)中完成各自的職責(zé)。
命令任務(wù):通常用于執(zhí)行發(fā)布通知更新包任務(wù)或即時更新插件。
上報任務(wù):用于上報客戶相關(guān)信息、插件運行狀態(tài)等信息。
更新插件任務(wù):用于異步加載插件,不影響主程序運行,并定期同步客戶端插件與服務(wù)端插件版本信息。
? ? 通過將核心任務(wù)使用調(diào)度任務(wù)異步運行的方式,避免影響主程序。
五、寫在最后
? ? 目前我們的項目已經(jīng)進入尾聲,我們已基本在運用這套方案實現(xiàn)了原有ERP代理服務(wù)的功能,同時我們更加友好的支持灰度插件發(fā)布上線。未來我們還會加入日志主動或被動上報、客戶服務(wù)器監(jiān)控,ERP站點運行監(jiān)控等功能插件來幫助我們產(chǎn)品發(fā)展的越來越好。
? ? 最后,我們在實踐中也遇到了一些問題:
當使用 XmlSerializer 操作相關(guān)資源時,由于改對象目前還不支持可卸載,暫不能用于可卸載插件中 ———?詳情請查閱 Issue
(https://github.com/dotnet/runtime/issues/1388)。
當插件依賴了 System.Data.SqlClient 類庫時,會導(dǎo)致報異常:
SqlClient is not supported on this platform. ——— ?詳情請查閱 Issue。
(https://github.com/dotnet/SqlClient/issues/115)
此時我們可改用 Microsoft.Data.SqlClient 包。
(https://devblogs.microsoft.com/dotnet/introducing-the-new-microsoftdatasqlclient/)
? ? 我們在努力克服困難的同時也在實踐中成長進步。
------ END ------
作者簡介
? ? 曾同學(xué):?研發(fā)工程師,負責(zé)研發(fā)協(xié)同平臺產(chǎn)品的研發(fā)工作。
也許您還想看
? ??研發(fā)協(xié)同平臺持續(xù)集成實踐
? ??研發(fā)協(xié)同平臺持續(xù)集成2.0架構(gòu)演進
? ? 研發(fā)協(xié)同平臺微服務(wù)監(jiān)控的技術(shù)實踐
? ? ERP開放平臺定制化遠程高效協(xié)作秘笈
總結(jié)
以上是生活随笔為你收集整理的研发协同平台持续交付之代理服务实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core开发实战(第5课:依赖
- 下一篇: 排名前15位的Kubernetes监控和