今天要介紹的是使用 WebJob
和 Logic App
製作定時排程器,本來想和爬蟲一起寫,不過爬蟲內容太多,寫完後發現篇幅太長,所以最後決定分成兩篇,這篇先介紹 WebJob 和 Logic App,下一篇在把爬蟲結合進來。
Σ( ° △ °|||)
首先建立 Webjob 專案,並介紹 .NET Core 3.0 的 Generic Host 寫法
接著將專案部屬到 Azure 上,這裡會使用 Pipelines 和網站一同部屬,不會因為加入了 WebJob,就破壞了原來自動化 CI/CD 的部分
最後使用 Logic App 呼叫 WebJob,完成定時排程器功能
WebJob 是 App Service 的一個子功能,可以用來執行背景程式,有三種執行方式,手動觸發
、Http 觸發
、排程觸發
,本來我想直接使用排程功能,不過免費方案的 App Service 幾分鐘無人使用,資源就會被回收,WebJob 也會跟著一起,所以其實只剩 Http 觸發 這個選項可選。
免費方案的 App Service 因為無法啟用 always on 模式,網站幾分鐘無人使用,資源就會被回收
Logic App 是 Azure 上的一項雲端服務,可以用來做 排程
、流程控制
、自動化
、系統間的協調
,等等...功能非常強大,介面也做得很好,推薦大家使用看看。
不過不是免費的,採用執行次數計費,費用可以參考:
https://azure.microsoft.com/zh-tw/pricing/details/logic-apps/
評估後如果每天只執行一次,費用相當便宜,所以最後決定使用 Logic App 搭配 WebJob,等有空再找其它免費服務替代。
這篇內容會接續之前介紹的東西,想了解完整過程的讀者可以先看。
[Day03] 將 Line Bot 部屬到 Azure 上 - 使用 Azure DevOps Pipelines
新增專案 iBot.Crawler,類型選擇 主控台應用程式 (.NET Core)
開發 Webjob 用一般的 Console 程式就可以
需要安裝的套件 (Nuget):
程式使用 .NET Core 3.0 的 Generic Host 寫法,可以參考: .NET 泛型主機
首先在 Program 內,使用 DI 註冊上一篇建立的 CoreDbContext 類別
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostContext, config) =>
{
//將 appsettings.json 加入 Configuration
config.AddJsonFile("appsettings.json", optional: true);
})
.ConfigureServices((hostContext, services) =>
{
//將 CoreDbContext 註冊 DI
services.AddDbContext<CoreDbContext>(options =>
{
options.UseMySQL(hostContext.Configuration.GetConnectionString("SQLConnectionString"));
});
services.AddHostedService<Worker>();
});
}
新增 Worker.cs 繼承 IHostedService,程式邏輯會寫在這裡。
IHostedService 介面有兩個方法需要實做:
我們只會用到第一個方法,第二個回傳 CompletedTask 就好。
public class Worker : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
//程式
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
好,到這邊設定完成,接下來要開始寫程式了!!
目前專案目錄
※ 專案內的 appsettings.json 是空的,後面會使用 Pipelines 在部屬時,將 iBotTest 內的 appsettings.json 複製到 iBot.Crawler,這樣才方便管理。
接下來寫一段程式,測試資料庫是否可以正常連線,將取得的結果輸出到 Log 上
Webjob 可以使用 Console.WriteLine()
輸出 Log
public Task StartAsync(CancellationToken cancellationToken)
{
Task.Run(() =>
{
//使用新執行續執行
ExecuteAsync().ConfigureAwait(false).GetAwaiter().GetResult();
//結束後關閉視窗
_lifeTime.StopApplication();
});
return Task.CompletedTask;
}
public async Task ExecuteAsync()
{
var iTHome = await _db.ITHomes.FirstOrDefaultAsync();
Console.WriteLine($"{iTHome.Name} {iTHome.Url}");
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
結果的部分要等部屬後才能看。
Task.Run()
開一個新的執行續?其實可以不用,不過這麼做是個好習慣,為什麼這麼說呢?
回來看 IHostedService 內的兩個方法:
雖然說 StopAsync 會在程式結束時執行,不過前提是 StartAsync 已執行完畢。
也就是說,假設程式的工作量很大,我們設計了一個取消機制,使用者關閉程式後,會取消未完成的工作。
如果沒有另開執行續,會導致 StartAsync 一直無法結束,後果就是 StopAsync 內取消工作的邏輯永遠不會被執行。
所以比較好的作法是,另外開一個執行續執行我們的程式,讓 StartAsync 可以馬上返回,不過程式內我並沒有設計取消機制,只是借題和大家分享這個概念。
詳情可以參考:
在微服務中使用 IHostedService 和 BackgroundService 類別實作背景工作
.ConfigureAwait(false).GetAwaiter().GetResult();
在 Console 專案下,await 會用新的執行續返回 (假設方法內有新開執行續),而 WPF 或 WinForm 這類的專案,則會用 UI 執行續返回。
設定 ConfigureAwait(false)
則一律不使用 UI 執行續返回,這樣可以避免死結、互相等待,等情況發生,不過因為 Console 沒有 UI 執行續,所以這裡加與不加都可以。
GetAwaiter().GetResult()
用來將非同步方法轉成同步。
想深入研究的讀者可以參考這篇:
.NET 非同步程式小技巧:GetAwaiter().GetResult() 與 Result 的差異
如果 App Service 部屬有使用 CI/CD,Azure 介面上的新增功能會無法使用,如下圖
本來想偷懶,現在只好認命去研究如何使用 Pipelines 部署了。 Σ( ° △ °|||)
如有設定部署表單原始檔控制,即無法從入口網站新增 WebJob。
※ 這邊會接續第三篇的設定。
[Day03] 將 Line Bot 部屬到 Azure 上 - 使用 Azure DevOps Pipelines
首先開啟 Azure DevOps Pipelines 的 Builds 功能頁。
原來 Builds 的設定如下:
如果使用介面手動新增,會看到 Webjob 是部屬在 App_Data\jobs\Triggered
目錄下。
D:\home\site\wwwroot\App_Data\jobs\Triggered>
知道位置後,思路就清楚了,只要在部屬過程中,想辦法將 Webjob 程式放進指定目錄下,就能同時部署兩者。
**/*.csproj 改成 **/iBotTest.csproj
這樣才能在下一步將 Webjob 放進指定目錄。
--configuration $(BuildConfiguration) --output "$(build.artifactstagingdirectory)\App_Data\jobs\Triggered\dotnetcore-webjob"
整理一下:
紅色為新增或修改部分。
Delete files (Webjob): 刪除專案內空的 appsettings.json
Copy files (Webjob): 將暫存區的 appsettings.json 複製到專案目錄下
Build: 建置專案
Publish: 發行專案
Publish (Webjob): 發行專案
Zip: 將發行結果壓縮成 .zip 檔
部屬成功後可以看到清單內多了 dotnetcore-webjob。
※ 補充說明:
為什麼最後需要將發行結果,壓縮成 zip?
開啟 Releases 管理畫面,找到發行設定,可以看到 Pipelines 會去發行資料夾下找 .zip 副檔名的檔案,用這個檔案去部屬。
如何查看發行結果?
這個功能還蠻方便的,可以在建置結束後查看發行目錄。
選擇 View contents 可以看到檔案目錄。
部屬成功後測試執行結果如何。
開啟 Webjob 視窗,點選 執行
按紐,成功後可以看到完成狀態。
點選旁邊的 紀錄
,可以看到剛剛 Console.WriteLine 的內容。
正如開頭所說,App Service 免費方案無法啟動 Webjob 排程功能,因此我另外找了 Logic App 服務替代。
新增 Logic Apps
,被歸類在物聯網分類。
開啟 邏輯應用程式設計工具,新增三個項目。
本來想直接呼叫 Webjob 省略第二步,可是發現只呼叫 Webjob 不會讓 MySQL 啟動,必需透過主網站才行。
1. 週期
如果選擇時區,則時間後不加 Z,反之,
例如: 如果我不選擇時區,時間需設為 2019-12-17T00:00:00Z。
這裡的 Z 指的是航海時間,詳情可以參考微軟文件:
https://docs.microsoft.com/zh-tw/azure/connectors/connectors-native-recurrence
2. HTTP
這裡我特別開了一個 API 用來喚醒網站。
[HttpGet("wakeUp")]
public string WakeUp()
{
return "I wake up!!";
}
3. HTTP
方法選擇 POST
,驗證選擇 基本
,接著填入 Webjob 相關連線資訊。
連線資訊可以在 Webjob 的 屬性
視窗中找到。
Logic Apps 設定完後記得儲存。
選擇 回合觸發程序
,執行成功可以看到綠色勾勾。
「定時排程器」 完成 ~ 。:.゚ヽ(*´∀`)ノ゚.:。
本來已經發文了,後來覺得內容太多,無法聚焦,忍痛將一篇分成兩篇,重寫了一部份,希望對閱讀舒適度有幫助。
幫大家回顧一下這篇的內容:
下一篇會完成整個功能,今天就到這裡,感謝大家觀看。
╰ ( ´ ▽ ` ) ╯
Dotnet Core 2.1 Webjob (.net core) Azure DevOps CI CD Pipeline
[NETCore] ASP.NET Core 建立排程服務 - 使用 Generic Host 搭配 Quartz.Net - Part 1
Generic Host - 微軟文件
ASP.NET Core 3 系列 - 程式生命週期 (Application Lifetime)
.NET 非同步程式小技巧:GetAwaiter().GetResult() 與 Result 的差異
谈谈.NET Core中基于Generic Host来实现后台任务
Building .NET Core WebJob Console App CI/CD Using Azure DevOps Pipelines