在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("");
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章