背景
在做離線數據處理時,需要處理的數據量比較大,邏輯很複雜,需要的資源比較多,所以無法立即得到結果,並且客戶端也不需要立即得到結果。這種處理任務可以稱爲 後臺任務 或者 “長任務”。
在 .NetCore 2.0 以前,我們是無法通過Web 服務器來處理後臺任務的,要麼是控制檯程序,要麼是windows 後臺服務; 但是這種類型的應用互操作性比較差,無法很方便的通過http請求來發送指令,執行操作(當然也可以利用WCF實現,但是會陷入網絡通信開發的深坑)。
在 .NetCore 2.0 以後的版本中,微軟提供了IHostedService API ,可以用於開發在WebServer Host 上執行的後臺任務,並且可以非常方便的和AspNetCore 框架融合,充分利用Web 請求的互操作性。
一個 “長任務服務”的設計思路
- 一個“長任務” 分爲執行過程和調度過程
- 執行過程就是任務的處理邏輯,而調度過程就是啓動任務,將任務放到後臺隊列,當主機有空再處理。
- 需要知道任務開始、完成情況.
實現代碼
1. 隊列操作接口
public interface IBackgroundQueue
{
void QueueTask(Func<CancellationToken, Task> task);
Task<Func<CancellationToken, Task>> PopQueue(CancellationToken cancellationToken);
}
2. BackgroundQueue實現
public class BackgroundQueue : IBackgroundQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> Tasks;
private SemaphoreSlim signal;
public BackgroundQueue()
{
Tasks = new ConcurrentQueue<Func<CancellationToken, Task>>();
signal = new SemaphoreSlim(0);
}
public async Task<Func<CancellationToken, Task>> PopQueue(CancellationToken cancellationToken)
{
await signal.WaitAsync(cancellationToken);
Tasks.TryDequeue(out var task);
return task;
}
public void QueueTask(Func<CancellationToken, Task> task)
{
Tasks.Enqueue(task);
signal.Release();
}
}
3、使用BackgroundService 類 作爲HostedService 的實現
public class QueueService : BackgroundService
{
private IBackgroundQueue _queue;
public QueueService(IBackgroundQueue queue)
{
_queue = queue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (stoppingToken.IsCancellationRequested == false)
{
var task = await _queue.PopQueue(stoppingToken);
await task(stoppingToken);
}
}
}
4. 客戶端調用
public async Task<string> ImportAccountObjectByTimeScale([FromBody] TimeScaleRequest request)
{
_queue.QueueTask(async token =>
{
// 將 任務添加到後臺隊列.
await IDataPipeline.Invoke();
});
//throw new System.NotImplementedException("");
}