目录
  • intro
  • fileprovider
  • construct host
  • demo
  • more
  • 总结

intro

最近我们老板想让我实现一个静态网站“滚动更新”的功能,其实就是希望网站部署的时候网站内容完整的切换,不能网站部署的过程中一部分是新的内容,另外一部分是老的内容。

这让我想到了微软的 azure app service,上家公司主要是用微软的云服务 azure,站点是部署到 azure app service 上的,azure app service 有一个部署槽的概念,我们的应用一个版本可以对应一个部署槽,通过部署槽我们就基本可以无缝地从一个版本切换到另外一个版本。

fileprovider

asp.net core 里静态文件的托管是允许自定义一个 ifileprovider 的,默认会使用物理路径文件, asp.net core 默认使用 wwroot 目录下作为默认的静态文件来源。

对于静态文件而言我们简单地使用两个目录来模拟两个部署槽,当需要的时候通过修改配置来动态修改生效的部署槽,基于 ioptionmonitor 和 physicalfileprovider 来实现一个简单的 dynamicfileprovider,实现代码如下:

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);
    }
}

看起来是不是简单,其实就是在 physicalfileprovider 的基础上封装了一下,配置发生变化的时候构建一个新的 physicalfileprovider

construct host

接着我们来看一下如何使用,使用代码如下:

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();

这里的示例只需要这些代码我们的应用就可以跑起来了,接着我们就来看一下使用效果吧

demo

在项目根目录运行 dotnet run 启动项目,然后访问 http://localhost:5000/index.html

然后我们再修改配置文件中的配置,把配置文件中 currentslot 配置修改为 slot2,然后再刷新页面,如下图所示:

那么是不是可以不修改配置文件实现部署槽切换呢,也是可以的,我提供了一个做切换的一个简单的 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;
}));

这个 api 做的事情很简单,在 slot1 和 slot2 之间进行切换,如果原来是 slot2 则切换成 slot1 否则切换成 slot2,修改配置之后调用一下 reload 以触发配置更新,删除配置文件中的 currentslot 配置,重新运行示例,查看 http://localhost:5000/index.html,还是看到的 slot1 中的内容,然后我们调用一下 /redeploy 接口来动态切换一下配置,然后再次刷新页面就会看到 slot2 中的内容,再调用一下 redeploy 之后刷新页面就会变回 slot1 中的内容

more

这样一个简单的 dynamicfileprovider 的功能就完成了,我们就可以动态的切换静态资源的不同版本了。

如果实际使用的话可以考虑更新一下 redeploy 接口,把新的网站内容通过上传文件的形式上传到网站下,上传结束后触发配置的更新,而且可以保留最近几个版本的更新,这样部署历史也有了,也方便进行回滚。

可以思考一下,如果我们的站点是集群部署的,需要改造什么?

对于集群部署的场景,可能会有两个问题,一个是文件访问的问题, 我们可以使用一个自定义的文件提供者来访问文件服务器上的文件,如果使用容器部署的场景,那么我们使用同一个 volume 就可以实现统一的文件访问, 另一个问题是配置的管理和更新,对于集群部署的配置,通常我们需要使用配置中心来统一管理配置,这样就和上面的配置一样了,配置更新时也会触发更新。

总结

到此这篇关于asp.net core如何实现简单的静态网站滚动更新的文章就介绍到这了,更多相关asp.net core静态网站滚动更新内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

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