《进击吧!Blazor!》系列入门教程 第一章 8.部署

《进击吧!Blazor!》是本人与张善友老师合作的Blazor零基础入门教程视频,此教程能让一个从未接触过Blazor的程序员掌握开发Blazor应用的能力。
视频地址://space.bilibili.com/483888821/channel/detail?cid=151273
Blazor WebAssembly 是单页应用 (SPA) 框架,用于使用 .NET 生成交互式客户端 Web 应用,采用 C# 代替 JavaScript 来编写前端代码
本系列文章基于《进击吧!Blazor!》编写,但因篇幅有限,省略了部分代码,完整示例代码://github.com/TimChen44/Blazor-ToDo

作者:陈超超
Ant Design Blazor 项目贡献者,拥有十多年从业经验,长期基于.Net技术栈进行架构与开发产品的工作,现就职于正泰集团。
邮箱:[email protected]
欢迎各位读者有任何问题联系我,我们共同进步。

部署是开发的最后一部,我们这次就来聊聊Blazor的部署方法。

发布

VS已经为我们预制了多个目标的发布工具,使用这些工具可以很容易的完成发布和部署。

1.首先在ToDo.Server项目上右键,选择发布...功能
在这里插入图片描述

2.接着选择要发布的目标,常用的环境都支持,我们就以Azure为例
在这里插入图片描述
3.根据目标不同填写一些配置,VS也会帮我们保存配置信息便于下次使用。
在这里插入图片描述
4.配置一些依赖,比如本例中需要配置数据库,最后点击”发布“按钮,接着喝杯茶等待一下。
在这里插入图片描述
5.经过VS后台的一顿操作,发布完成,并且会自动打开发布的站点,效果如下
在这里插入图片描述
到这里,这篇文章结束了,各位再见!😜

这个😅,我们本着“搞事情”的精神,”没有困难制造困难”的理念来重构我们的发布,各位走起~~~~

首先可以确定上面VS为我们提供的发布工具确实非常方便,比较适合小团队开发一些小规模的系统,但是通常企业中使用会遇到以下的问题

  • 负责发布的运维工程师与负责编码的开发工程师不是同一拨人,不可能让运维工程师用VS开着源码进行发布,代码安全,生产环境安全双双无法保证。
  • 自动化的IC/CD普及度的增加,生成和发布过程挪到了各DevOps平台,所以也不适合在VS中操作。

广告时间:推荐大家尝试一下微软的Azure DevOps,支持本地部署,支持所有语言,预制各平台发布流程等等,配置一个项目的IC/CD过程犹如前面的操作一样,鼠标点点就完事,懒人的福音。

综上述,我们可以采用比较灵活的的做法,先将项目发布成文件,然后将这些文件部署到不同的环境中。这也符合自动化的IC/CD的常规流程,先集成再部署。

如果使用Docker,可以在IC时直接Build出Image,也可以在CD时Build出Image,我觉得没有标准,适合自己就行。

发布到文件夹

那么我们继续使用VS的发布工具将项目打包

1.选择发布到文件夹选项,并设定输出位置
在这里插入图片描述
2.点击发布完成编译并输出。
在这里插入图片描述
3.点击上面的”铅笔“图标可以进行详细配置
在这里插入图片描述
目标框架 设置使用那个.net框架进行编译
部署模式 它分为框架依赖独立两个选共享
框架依赖 模式下生成的文件需要.net运行时才能运行,优点体积较小,程序本省不依赖目标运行时,方便移植
独立 模式下编译器会把.net运行时与程序打包在一起,运行平台无需.net运行时,缺点是体积较大,编译的程序依赖于目标运行时
目标运行时 选择针对那个平台编译,可以是win、linux、osx系统,以及x64、x86、arm指令集
生成单个文件 勾选后可以把程序打包在一个文件中,运行的时候程序会自动解包并运行
裁剪未使用的程序集 将一些项目中引用但是代码中并未使用的程序集剔除以减少程序包的体积

我们的示例程序使用配置 部署模式依赖框架目标运行时可移植

如果编译过程使用自动化工具进行打包,除了打包命令中额外增加参数外,我们也可以在.csproj文件中做以下配置来实现上面的参数

<PropertyGroup>
	<PublishSingleFile>True</PublishSingleFile><!-- 生成单个文件  -->
	<PublishTrimmed>True</PublishTrimmed><!-- 裁剪未使用的程序集  -->
	<SelfContained>true</SelfContained><!-- 框架依赖:独立  -->
	<RuntimeIdentifier>win-x86</RuntimeIdentifier><!-- 目标运行时  -->
</PropertyGroup>

4.程序以文件形式输出在我们给定的路径

部署

使用 ASP.NET Core 进行托管部署

如果使用ASP.NET Core的Kestrel Web 服务器托管程序,我们只需运行我们上面发布输出的ToDo.Server.exe文件即可
在这里插入图片描述
到这里有个小问题,就是我们不想使用默认的5000和5001端口,这个我们就需要人为的给定端口。

修改ToDo.ServerProgram.cs文件

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder
            .UseUrls("//*:4000", "//*:4001")
            .UseStartup<Startup>();
        });

我们使用.UseUrls来给定Url地址,这里*代表适配所有IP或域名,端口分别设定为40004001

这里又冒出一个小问题,目前端口硬编码在程序中,发布后无法修改,遇到这个问题,我们可以通过启动参数、配置文件等多个方法实现发布后修改,在这里我贴上通过启动参数修改端口的代码。

public static IHostBuilder CreateHostBuilder(string[] args)
{
    var configuration = new ConfigurationBuilder().AddCommandLine(args).Build();
    return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder
            .UseUrls($"//*:{configuration["httpport"] ?? "5000"}", $"//*:{configuration["httpsport"] ?? "5001"}")
            .UseStartup<Startup>();
        });
}

启动的时候附上对应参数实现修改端口的目的。
在这里插入图片描述

使用 IIS 进行托管部署

我们也可以使用IIS来托管部署我们的程序。

  1. 需要在部署的服务器上安装ASP.NET Core Hosting Bundle,这个可以让IIS支持.Net Core应用。下载地址://dotnet.microsoft.com/download/dotnet/5.0

  2. 添加名为ToDo的站点,选择发布的文件所在目录,配置端口等
    在这里插入图片描述

  3. 将应用程序池ToDoNET CLR版本设置为无托管代码
    在这里插入图片描述

  4. 启动站点就能看到自己部署的程序了。
    在这里插入图片描述

与ASP.Net Core的区别

看到这里,各位觉得Blazor和ASP.Net Core在部署上有什么区别吗?答案是:没区别😉。

前后端分开部署

当遇到规模不小的项目时,我们会考虑到性能、安全等因素时,可能会选择前后端分别部署在不同的服务器上,这时我们只需要新建一个空项目,然后引用Blazor项目即可,操作如下

  1. 添加名为ToDo.BlazorHost的ASP.NET Core空项目
    在这里插入图片描述

  2. 引用ToDo.Client项目

  3. 安装Microsoft.AspNetCore.Components.WebAssembly.Server

  4. 修改launchSettings.json文件,配置应用端口默认为4000

    "iisExpress": {
      "applicationUrl": "//localhost:4000",
      "sslPort": 0
    }
  1. 修改Startup.cs文件,让项目支持Blazor应用
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseBlazorFrameworkFiles();//配置应用程序从根“/”提供Blazor框架文件
    app.UseStaticFiles();//提供静态文件支持
    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapFallbackToFile("index.html");//配置默认路由地址
    });
}
  1. 修改ToDo.Client项目Program.cs文件,设置HttpClient默认请求地址为ToDo.Server项目的地址
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri($"//localhost:5000") });
  1. 修改ToDo.Server项目launchSettings.json文件,配置IIS Express调试端口默认为5000
      "iisExpress": {
        "applicationUrl": "//localhost:5000",
        "sslPort": 0
      }
  1. 修改Startup.cs文件,添加跨域支持,此处为了演示方便,我开启了所有跨域设置
app.UseCors(config => config.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials().WithOrigins("//localhost:4000"));
  1. 删除ToDo.ServerToDo.Client的引用,以及Blazor相关的设置,ToDo.Server作为一个存粹的WebAPI站点存在。

  2. 为了验证前端请求的数据是那个服务返回,我们做一个反馈服务器地址的接口给前端调用。

修改AuthController.cs文件

[HttpGet]
public string GetHost()
{
    return HttpContext.Request.Host.ToString();
}

修改Login.razor文件

<p>前端地址:<span>@ClientHost</span></p>
<p>API服务地址:<span>@ServerHost</span></p>
string ClientHost;
string ServerHost;
protected override async Task OnInitializedAsync()
{

    ClientHost = WebAssemblyHostBuilder.CreateDefault().HostEnvironment.BaseAddress;
    ServerHost = await Http.GetStringAsync($"api/Auth/GetHost");
    await base.OnInitializedAsync();
}

同时启动ToDo.ServerToDo.BlazorHost项目的调试,看到以下效果
在这里插入图片描述

网关部署

当用户多了,获作为关键服务时,单点部署就有点不合时宜了,这时需要高可用,负载均衡等需求,因此我们就要拿出我们的网关,把他放在最前面,负责请求分发。

  1. 添加名为ToDo.BlazorHost的ASP.NET Core空项目作为网关ToDo.Gateway

  2. 添加Ocelot组件,Ocelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由、请求聚合、服务发现、认证、鉴权、限流熔断、并内置了负载均衡器与Service Fabric、Butterfly Tracing集成。

在这里插入图片描述

  1. 修改launchSettings.json文件,将访问端口改成5500
    "iisExpress": {
      "applicationUrl": "//localhost:5500",
      "sslPort": 0
    }
  1. 添加Ocelot.json文件,配置我们网关的策略
{
  "Routes": [
    {
      // - 上游服务配置
      "UpstreamPathTemplate": "/api/{url}",
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],

      // - 下游服务配置
      "DownstreamPathTemplate": "/api/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5000
        }
      ],

      // - LoadBalancer将决定负载均衡的算法,三种取值
      // RoundRobin:轮流发送
      // LeastConnection:将请求发往最空闲的那个服务器
      // NoLoadBalance:总是发往第一个请求或者是服务发现
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    },
    {
      // - 上游服务配置
      "UpstreamPathTemplate": "/{url}",
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],

      // - 下游服务配置
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5100
        }
      ],

      // - LoadBalancer将决定负载均衡的算法,三种取值
      // RoundRobin:轮流发送
      // LeastConnection:将请求发往最空闲的那个服务器
      // NoLoadBalance:总是发往第一个请求或者是服务发现
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "//api.mybusiness.com"
  }
}

上面Ocelot.json文件中注释只是为了介绍常用属性,实际使用中json文件中不能包含注释,否则会造成解析失败,切记。

  1. 修改Program.cs文件,添加载入Ocelot.json文件的代码
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureAppConfiguration((hostContext, config) =>
            {
                config.AddJsonFile("Ocelot.json");
            })
            .UseStartup<Startup>();
        });
  1. 修改Startup.cs文件,添加Ocelot服务
public void ConfigureServices(IServiceCollection services)
{
    services.AddOcelot();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseOcelot();
}
  1. 修改ToDo.Client项目的Program.cs文件,配置HttpClient默认地址为网关地址
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri($"//localhost:5500") });
  1. 修改ToDo.Server项目的Startup.cs文件,追加跨域请求地址“//localhost:5500
app.UseCors(config => config.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials().WithOrigins("//localhost:4000", "//localhost:5500"));

同时调试ToDo.BlazorHostToDo.GatewayToDo.Server三个项目,打开地址//localhost:5500
我们看到前端地址是localhost:5500,请求的地址也是localhost:5500,但是为我们返回数据的API服务地址是localhost:5000,说明网关已经帮我做了转发。
在这里插入图片描述

最后

感谢有读者能看到这里,这是本人第一次写专栏,文章可能会存在一些瑕疵,我会在将来尽力改进。

《进击吧!Blazor!》系列入门教程 第一章到这里就结束了,后续我会继续为大家带来与.Net和Blazor有关的原创文章,文章内容争取做到易读、高质量。

谢谢大家!