ASP.NET Core如何實(shí)現(xiàn)簡(jiǎn)單的靜態(tài)網(wǎng)站滾動(dòng)更新
Intro
最近我們老板想讓我實(shí)現(xiàn)一個(gè)靜態(tài)網(wǎng)站“滾動(dòng)更新”的功能,其實(shí)就是希望網(wǎng)站部署的時(shí)候網(wǎng)站內(nèi)容完整的切換,不能網(wǎng)站部署的過(guò)程中一部分是新的內(nèi)容,另外一部分是老的內(nèi)容。
這讓我想到了微軟的 Azure App Service,上家公司主要是用微軟的云服務(wù) Azure,站點(diǎn)是部署到 Azure App Service 上的,Azure App Service 有一個(gè)部署槽的概念,我們的應(yīng)用一個(gè)版本可以對(duì)應(yīng)一個(gè)部署槽,通過(guò)部署槽我們就基本可以無(wú)縫地從一個(gè)版本切換到另外一個(gè)版本。
FileProvider
ASP.NET Core 里靜態(tài)文件的托管是允許自定義一個(gè) IFileProvider 的,默認(rèn)會(huì)使用物理路徑文件, ASP.NET Core 默認(rèn)使用 wwroot 目錄下作為默認(rèn)的靜態(tài)文件來(lái)源。
對(duì)于靜態(tài)文件而言我們簡(jiǎn)單地使用兩個(gè)目錄來(lái)模擬兩個(gè)部署槽,當(dāng)需要的時(shí)候通過(guò)修改配置來(lái)動(dòng)態(tài)修改生效的部署槽,基于 IOptionMonitor 和 PhysicalFileProvider 來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 DynamicFileProvider,實(shí)現(xiàn)代碼如下:
public class DynamicFileProviderOptions
{
public string CurrentSlot { get; set; }
}
public class DynamicFileProvider : IFileProvider
{
private PhysicalFileProvider _physicalFileProvider;
private const string DefaultSlotName = "Slot1";
public DynamicFileProvider(IOptionsMonitor<DynamicFileProviderOptions> optionsMonitor, IWebHostEnvironment webHostEnvironment)
{
var webRoot = webHostEnvironment.ContentRootPath;
_physicalFileProvider = new PhysicalFileProvider(Path.Combine(webRoot, optionsMonitor.CurrentValue.CurrentSlot ?? DefaultSlotName));
optionsMonitor.OnChange(options =>
{
var path = Path.Combine(webRoot, options.CurrentSlot);
_physicalFileProvider = new PhysicalFileProvider(path);
});
}
public IDirectoryContents GetDirectoryContents(string subpath)
{
return _physicalFileProvider.GetDirectoryContents(subpath);
}
public IFileInfo GetFileInfo(string subpath)
{
return _physicalFileProvider.GetFileInfo(subpath);
}
public IChangeToken Watch(string filter)
{
return _physicalFileProvider.Watch(filter);
}
}
看起來(lái)是不是簡(jiǎn)單,其實(shí)就是在 PhysicalFileProvider 的基礎(chǔ)上封裝了一下,配置發(fā)生變化的時(shí)候構(gòu)建一個(gè)新的 PhysicalFileProvider
Construct Host
接著我們來(lái)看一下如何使用,使用代碼如下:
var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder.ConfigureServices((context, services) =>
{
services.Configure<DynamicFileProviderOptions>(context.Configuration);
services.AddSingleton<DynamicFileProvider>();
});
webHostBuilder.Configure(app =>
{
var dynamicFileProvider = app.ApplicationServices.GetRequiredService<DynamicFileProvider>();
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = dynamicFileProvider,
});
});
});
var host = builder.Build();
host.Run();
這里的示例只需要這些代碼我們的應(yīng)用就可以跑起來(lái)了,接著我們就來(lái)看一下使用效果吧
Demo
在項(xiàng)目根目錄運(yùn)行 dotnet run 啟動(dòng)項(xiàng)目,然后訪問(wèn) http://localhost:5000/index.html

然后我們?cè)傩薷呐渲梦募械呐渲?,把配置文件?CurrentSlot 配置修改為 Slot2,然后再刷新頁(yè)面,如下圖所示:

那么是不是可以不修改配置文件實(shí)現(xiàn)部署槽切換呢,也是可以的,我提供了一個(gè)做切換的一個(gè)簡(jiǎn)單的 API
app.Map(new PathString("/redeploy"), appBuilder => appBuilder.Run(context =>
{
if (context.RequestServices.GetRequiredService<IConfiguration>() is ConfigurationRoot configuration)
{
var currentSlot = configuration["CurrentSlot"];
configuration["CurrentSlot"] = "Slot2" != currentSlot ? "Slot2" : "Slot1";
configuration.Reload();
return context.Response.WriteAsync("Success");
}
return Task.CompletedTask;
}));
這個(gè) API 做的事情很簡(jiǎn)單,在 Slot1 和 Slot2 之間進(jìn)行切換,如果原來(lái)是 Slot2 則切換成 Slot1 否則切換成 Slot2,修改配置之后調(diào)用一下 Reload 以觸發(fā)配置更新,刪除配置文件中的 CurrentSlot 配置,重新運(yùn)行示例,查看 http://localhost:5000/index.html,還是看到的 Slot1 中的內(nèi)容,然后我們調(diào)用一下 /redeploy 接口來(lái)動(dòng)態(tài)切換一下配置,然后再次刷新頁(yè)面就會(huì)看到 Slot2 中的內(nèi)容,再調(diào)用一下 redeploy 之后刷新頁(yè)面就會(huì)變回 Slot1 中的內(nèi)容

More
這樣一個(gè)簡(jiǎn)單的 DynamicFileProvider 的功能就完成了,我們就可以動(dòng)態(tài)的切換靜態(tài)資源的不同版本了。
如果實(shí)際使用的話可以考慮更新一下 redeploy 接口,把新的網(wǎng)站內(nèi)容通過(guò)上傳文件的形式上傳到網(wǎng)站下,上傳結(jié)束后觸發(fā)配置的更新,而且可以保留最近幾個(gè)版本的更新,這樣部署歷史也有了,也方便進(jìn)行回滾。
可以思考一下,如果我們的站點(diǎn)是集群部署的,需要改造什么?
對(duì)于集群部署的場(chǎng)景,可能會(huì)有兩個(gè)問(wèn)題,一個(gè)是文件訪問(wèn)的問(wèn)題, 我們可以使用一個(gè)自定義的文件提供者來(lái)訪問(wèn)文件服務(wù)器上的文件,如果使用容器部署的場(chǎng)景,那么我們使用同一個(gè) Volume 就可以實(shí)現(xiàn)統(tǒng)一的文件訪問(wèn), 另一個(gè)問(wèn)題是配置的管理和更新,對(duì)于集群部署的配置,通常我們需要使用配置中心來(lái)統(tǒng)一管理配置,這樣就和上面的配置一樣了,配置更新時(shí)也會(huì)觸發(fā)更新。
總結(jié)
到此這篇關(guān)于ASP.NET Core如何實(shí)現(xiàn)簡(jiǎn)單的靜態(tài)網(wǎng)站滾動(dòng)更新的文章就介紹到這了,更多相關(guān)ASP.NET Core靜態(tài)網(wǎng)站滾動(dòng)更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
References
- https://docs.microsoft.com/en-us/aspnet/core/fundamentals/static-files
- https://docs.microsoft.com/zh-cn/azure/app-service/deploy-best-practices
- https://github.com/WeihanLi/SamplesInPractice/tree/master/DynamicStaticFileProvider
相關(guān)文章
SqlDataSource 鏈接Access 數(shù)據(jù)
如何用AccessDataSource鏈接有密碼的access數(shù)據(jù)庫(kù)總是會(huì)報(bào)錯(cuò),替代方法是用SqlDataSource 鏈接Access 數(shù)據(jù)。2009-04-04
ASP.NET 固定標(biāo)題列與欄位的具體實(shí)現(xiàn)
客戶提這個(gè)要求很久了,最近才時(shí)間弄,但是看到百度中要沒(méi)有很多詳細(xì)的代碼。廢話不多說(shuō)直接貼代碼。2013-06-06
MVC網(wǎng)站開(kāi)發(fā)之權(quán)限管理篇
這篇文章主要為大家詳細(xì)介紹了MVC網(wǎng)站開(kāi)發(fā)之權(quán)限管理的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-08-08
獲取ashx得到的內(nèi)容(已處理好的數(shù)據(jù))
獲取ashx得到的內(nèi)容,一般用于ajax的情況比較多一點(diǎn);重點(diǎn):ashx頁(yè)面?zhèn)鬟^(guò)來(lái)的就是已經(jīng)處理好的數(shù)據(jù),感興趣的朋有可以參考下啊,希望本文對(duì)你學(xué)習(xí)ajax有所幫助2013-01-01
給Asp.Net初學(xué)者的關(guān)于繼承和多態(tài)性的例子
給Asp.Net初學(xué)者的關(guān)于繼承和多態(tài)性的例子...2006-09-09
C#中實(shí)現(xiàn)偽靜態(tài)頁(yè)面兩種方式介紹
偽靜態(tài)技術(shù)的誕生,帶動(dòng)了于搜索引擎友好C#中實(shí)現(xiàn)偽靜態(tài)頁(yè)面有兩種方式,本文將一一詳解,感興趣的朋友可以參考下,希望本文對(duì)你學(xué)習(xí)偽靜態(tài)有所幫助2013-01-01
ASP.NET Core擴(kuò)展庫(kù)之Http日志的使用詳解
這篇文章主要介紹了ASP.NET Core擴(kuò)展庫(kù)之Http日志的使用詳解,幫助大家更好的理解和學(xué)習(xí)使用.net技術(shù),感興趣的朋友可以了解下2021-04-04
ASP.NET?Core?MVC控制器請(qǐng)求依賴(lài)注入
這篇文章介紹了ASP.NET?Core?MVC控制器請(qǐng)求依賴(lài)注入的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04

