《進擊吧!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有關的原創文章,文章內容爭取做到易讀、高質量。

謝謝大家!