113/03/12
This commit is contained in:
22
Hangfire.drawio
Normal file
22
Hangfire.drawio
Normal file
File diff suppressed because one or more lines are too long
76
HangfireExample.WebService/Controllers/JobController.cs
Normal file
76
HangfireExample.WebService/Controllers/JobController.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using Hangfire;
|
||||||
|
using HangfireExample.WebService.Services;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace HangfireExample.WebService.Controllers
|
||||||
|
{
|
||||||
|
[Route("Job")]
|
||||||
|
[ApiController]
|
||||||
|
public class JobController : ControllerBase, IJobService
|
||||||
|
{
|
||||||
|
public JobController() { }
|
||||||
|
|
||||||
|
[HttpPost("Continuation", Name = "Continuation"), Produces("application/json")]
|
||||||
|
public string Continuation(string message, string jobId)
|
||||||
|
{
|
||||||
|
return BackgroundJob.ContinueJobWith(jobId, () => ContinuationJob(message, jobId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
[DisplayName("Continuation, Job Id = {1}")]
|
||||||
|
public static void ContinuationJob(string message, string jobId)
|
||||||
|
{
|
||||||
|
Console.WriteLine("{0}: {1}", DateTimeOffset.Now, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("Delay", Name = "Delay"), Produces("application/json")]
|
||||||
|
public string Delay(string message, int seconds, int minutes = 0, int hours = 0, int days = 0)
|
||||||
|
{
|
||||||
|
TimeSpan delay =
|
||||||
|
seconds > 0 ? TimeSpan.FromSeconds(seconds) :
|
||||||
|
minutes > 0 ? TimeSpan.FromMinutes(minutes) :
|
||||||
|
hours > 0 ? TimeSpan.FromHours(hours) :
|
||||||
|
days > 0 ? TimeSpan.FromDays(days) : throw new Exception("未指定延遲時間");
|
||||||
|
return BackgroundJob.Schedule(() => DelayJob(message, seconds, minutes, hours, days), delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
[DisplayName("Delay, Day = {4}, Hour = {3}, Minute = {2}, Second = {1}")]
|
||||||
|
public static void DelayJob(string message, int seconds, int minutes = 0, int hours = 0, int days = 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("{0}: {1}", DateTimeOffset.Now, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("Fire", Name = "Fire"), Produces("application/json")]
|
||||||
|
public string Fire(string message)
|
||||||
|
{
|
||||||
|
return BackgroundJob.Enqueue(() => FireJob(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
[DisplayName("Fire")]
|
||||||
|
public static void FireJob(string message)
|
||||||
|
{
|
||||||
|
Console.WriteLine("{0}: {1}", DateTimeOffset.Now, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("Recurring", Name = "Recurring"), Produces("application/json")]
|
||||||
|
public string Recurring(string message, string jobId, string expression)
|
||||||
|
{
|
||||||
|
RecurringJob.RemoveIfExists(jobId);
|
||||||
|
|
||||||
|
RecurringJob.AddOrUpdate(jobId, () => _RecurringJob(message, jobId, expression), expression);
|
||||||
|
|
||||||
|
return jobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
[DisplayName("Recurring, JobId = {1}, Cron expression = {2}")]
|
||||||
|
public static void _RecurringJob(string message, string jobId, string expression)
|
||||||
|
{
|
||||||
|
Console.WriteLine("{0}: {1}", DateTimeOffset.Now, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using Hangfire;
|
using Hangfire;
|
||||||
|
using Hangfire.Dashboard;
|
||||||
using Hangfire.Storage.SQLite;
|
using Hangfire.Storage.SQLite;
|
||||||
|
using HangfireExample.WebService.Filters;
|
||||||
|
|
||||||
namespace HangfireExample.WebService.Extensions
|
namespace HangfireExample.WebService.Extensions
|
||||||
{
|
{
|
||||||
@@ -18,13 +20,37 @@ namespace HangfireExample.WebService.Extensions
|
|||||||
// 將 Hangfire 加入服務,使用 SQLite 作為儲存區
|
// 將 Hangfire 加入服務,使用 SQLite 作為儲存區
|
||||||
services.AddHangfire(configuration => configuration
|
services.AddHangfire(configuration => configuration
|
||||||
.UseSQLiteStorage("HangfireExample.db"));
|
.UseSQLiteStorage("HangfireExample.db"));
|
||||||
|
// 將 Hangfire Server 加入服務
|
||||||
|
services.AddHangfireServer(options =>
|
||||||
|
{
|
||||||
|
// 指定 Hangfire Server 的名稱
|
||||||
|
options.ServerName = "HangfireExample";
|
||||||
|
// 指定 Hangfire Server 執行佇列的 Tags
|
||||||
|
options.Queues = ["default", "wb",];
|
||||||
|
});
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebApplication UseHangfireDashboard(this WebApplication app, IConfiguration configuration)
|
public static WebApplication UseHangfireDashboard(this WebApplication app, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
// Hangfire Dashboard 預設路由為 /Hangfire
|
// Hangfire Dashboard 預設路由為 /Hangfire
|
||||||
app.UseHangfireDashboard(configuration.GetValue<string>("DashboardRoot"));
|
app.UseHangfireDashboard(configuration.GetValue<string>("DashboardRoot"), new DashboardOptions
|
||||||
|
{
|
||||||
|
Authorization = [new DashboardAuthorizationFilter()],
|
||||||
|
// 指定 Dashboard 指定讀取,不能操作,如重新執行、再次加入排程與刪除任務等
|
||||||
|
IsReadOnlyFunc = (DashboardContext context) => true,
|
||||||
|
// 指定 Back to site 的連結
|
||||||
|
AppPath = "/swagger",
|
||||||
|
});
|
||||||
|
// 設定第二資料來源的 Dashboard
|
||||||
|
//app.UseHangfireDashboard(configuration.GetValue<string>("DashboardRoot"), new DashboardOptions
|
||||||
|
//{
|
||||||
|
// Authorization = [new DashboardAuthorizationFilter()],
|
||||||
|
// // 指定 Dashboard 指定讀取,不能操作,如重新執行、再次加入排程與刪除任務等
|
||||||
|
// IsReadOnlyFunc = (DashboardContext context) => true,
|
||||||
|
// // 指定 Back to site 的連結
|
||||||
|
// AppPath = "/swagger",
|
||||||
|
//}, new SQLiteStorage("HangfireExample2.db"));
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Hangfire.Annotations;
|
||||||
|
using Hangfire.Dashboard;
|
||||||
|
|
||||||
|
namespace HangfireExample.WebService.Filters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Hangfire Dashboard 的認證過濾中介。
|
||||||
|
/// </summary>
|
||||||
|
public class DashboardAuthorizationFilter : IDashboardAuthorizationFilter
|
||||||
|
{
|
||||||
|
public bool Authorize([NotNull] DashboardContext context)
|
||||||
|
{
|
||||||
|
// 認證時,直接回傳成功(無驗證)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,8 +12,4 @@
|
|||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Controllers\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Binary file not shown.
@@ -1,4 +1,24 @@
|
|||||||
{
|
{
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "http://localhost:5185"
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "hangfire",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
"iisSettings": {
|
"iisSettings": {
|
||||||
"windowsAuthentication": false,
|
"windowsAuthentication": false,
|
||||||
@@ -7,25 +27,5 @@
|
|||||||
"applicationUrl": "http://localhost:1922",
|
"applicationUrl": "http://localhost:1922",
|
||||||
"sslPort": 0
|
"sslPort": 0
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"applicationUrl": "http://localhost:5185",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
13
HangfireExample.WebService/Services/IJobService.cs
Normal file
13
HangfireExample.WebService/Services/IJobService.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace HangfireExample.WebService.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Job 服務。
|
||||||
|
/// </summary>
|
||||||
|
public interface IJobService
|
||||||
|
{
|
||||||
|
public string Fire(string message);
|
||||||
|
public string Delay(string message, int seconds, int minutes = 0, int hours = 0, int days = 0);
|
||||||
|
public string Recurring(string message, string jobId, string expression);
|
||||||
|
public string Continuation(string message, string jobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"Hangfire": "HangfireExample.db"
|
||||||
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Hangfire;
|
using Hangfire;
|
||||||
|
using Hangfire.SqlServer;
|
||||||
using Hangfire.Storage.SQLite;
|
using Hangfire.Storage.SQLite;
|
||||||
|
|
||||||
// 第三方擴充: Hangfire.Storage.SQLite 0.4.1
|
// 第三方擴充: Hangfire.Storage.SQLite 0.4.1
|
||||||
|
|||||||
BIN
HangfireResources/BackToSite.jpg
Normal file
BIN
HangfireResources/BackToSite.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
127
README.md
127
README.md
@@ -66,6 +66,131 @@ GlobalConfiguration
|
|||||||
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
||||||
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
||||||
QueuePollInterval = TimeSpan.Zero,
|
QueuePollInterval = TimeSpan.Zero,
|
||||||
UseRecommendedIsolationLevel = true
|
UseRecommendedIsolationLevel = true,
|
||||||
|
DisableGlobalLocks = true, // Migration to Schema 7 is required
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hangfire SQLite
|
||||||
|
GlobalConfiguration
|
||||||
|
.Configuration
|
||||||
|
.UseSQLiteStorage("HangfireExample.db"));
|
||||||
|
```
|
||||||
|
|
||||||
|
## Server
|
||||||
|
|
||||||
|
啟動 Hangfire 伺服器
|
||||||
|
|
||||||
|
```
|
||||||
|
services.AddHangfireServer();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Dashboard UI
|
||||||
|
|
||||||
|
開啟 Dashboard 功能
|
||||||
|
|
||||||
|
```
|
||||||
|
app.UseHangfireDashboard();
|
||||||
|
```
|
||||||
|
|
||||||
|
* Configuring Authorization
|
||||||
|
|
||||||
|
設定自定義驗證
|
||||||
|
|
||||||
|
```
|
||||||
|
app.UseHangfireDashboard("hangfire", new DashboardOptions
|
||||||
|
{
|
||||||
|
Authorization = [new DashboardAuthorizationFilter()],
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Read-only view
|
||||||
|
|
||||||
|
設定 UI 為 ReadOnly
|
||||||
|
|
||||||
|
```
|
||||||
|
app.UseHangfireDashboard("hangfire", new DashboardOptions
|
||||||
|
{
|
||||||
|
IsReadOnlyFunc = (DashboardContext context) => true,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
* Change URL Mapping
|
||||||
|
|
||||||
|
修改 Dashboard UI 的預設根路由
|
||||||
|
|
||||||
|
```
|
||||||
|
app.UseHangfireDashboard("hangfire");
|
||||||
|
```
|
||||||
|
|
||||||
|
* Change Back to site Link
|
||||||
|
|
||||||
|
修改返回按鈕的路由
|
||||||
|
|
||||||
|
```
|
||||||
|
app.UseHangfireDashboard("hangfire", new DashboardOptions
|
||||||
|
{
|
||||||
|
AppPath = "/swagger", // 導向到 Swagger UI
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
* Multiple Dashboards
|
||||||
|
|
||||||
|
設定不同資料來源的 Dashboard
|
||||||
|
|
||||||
|
```
|
||||||
|
app.UseHangfireDashboard(configuration.GetValue<string>("DashboardRoot"), new DashboardOptions
|
||||||
|
{
|
||||||
|
Authorization = [new DashboardAuthorizationFilter()],
|
||||||
|
// 指定 Dashboard 指定讀取,不能操作,如重新執行、再次加入排程與刪除任務等
|
||||||
|
IsReadOnlyFunc = (DashboardContext context) => true,
|
||||||
|
// 指定 Back to site 的連結
|
||||||
|
AppPath = "/swagger",
|
||||||
|
}, new SQLiteStorage("HangfireExample2.db"));
|
||||||
|
```
|
||||||
|
|
||||||
|
## Background Methods
|
||||||
|
|
||||||
|
排程可以直接執行特定方法,Hangfire 會將方法(可使用非同步)、類型與參數序列化後保存到資料庫,Hangfire 依照排程類型,分為以下四種
|
||||||
|
|
||||||
|
* Fire-and-Forget jobs: 將任務丟入執行佇列,直接執行
|
||||||
|
|
||||||
|
```
|
||||||
|
BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-Forget"));
|
||||||
|
```
|
||||||
|
|
||||||
|
* Delayed jobs: 延遲指定時間後,將任務丟入執行佇列
|
||||||
|
|
||||||
|
```
|
||||||
|
BackgroundJob.Schedule(() => Console.WriteLine("Delayed"), TimeSpan.FromDays(1));
|
||||||
|
```
|
||||||
|
|
||||||
|
* Recurring jobs: 依照指定間隔,將任務丟入執行佇列
|
||||||
|
|
||||||
|
```
|
||||||
|
RecurringJob.AddOrUpdate("job_id", () => Console.Write("Recurring By CRON"), Cron.Daily);
|
||||||
|
|
||||||
|
RecurringJob.AddOrUpdate("job_id", () => Console.Write("Recurring By CRON expressions"), "0 12 * */2");
|
||||||
|
```
|
||||||
|
|
||||||
|
移除已存在的排程任務
|
||||||
|
|
||||||
|
```
|
||||||
|
RecurringJob.RemoveIfExists("job_id");
|
||||||
|
```
|
||||||
|
|
||||||
|
手動觸發已存在的排程任務
|
||||||
|
|
||||||
|
```
|
||||||
|
RecurringJob.Trigger("job_id");
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* Continuations: 指定的任務完成後,才丟入執行佇列
|
||||||
|
|
||||||
|
```
|
||||||
|
var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-Forget"));
|
||||||
|
|
||||||
|
BackgroundJob.ContinueJobWith(jobid, () => Console.WriteLine("Continuations"));
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user