在AspNetCore 中執行長任務--一個簡易的後臺服務隊列

背景

在做離線數據處理時,需要處理的數據量比較大,邏輯很複雜,需要的資源比較多,所以無法立即得到結果,並且客戶端也不需要立即得到結果。這種處理任務可以稱爲 後臺任務 或者 “長任務”。
在 .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("");
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章