it-swarm.dev

asp.net core 2의 컨트롤러 작업에서 백그라운드 작업 실행

Asp.net core 2.0에서 C #을 사용하여 REST Api로 웹 응용 프로그램을 개발 중입니다.

내가 달성하고자하는 것은 클라이언트가 엔드 포인트에 요청을 보낼 때 클라이언트 요청 컨텍스트와 분리 된 백그라운드 작업을 실행하여 작업이 성공적으로 시작되면 종료됩니다.

HostedService가 있다는 것을 알고 있지만 문제는 서버가 시작될 때 HostedService가 시작되고 컨트롤러에서 HostedService를 수동으로 시작할 수있는 방법이 없다는 것입니다.

다음은 질문을 보여주는 간단한 코드입니다.

[Authorize(AuthenticationSchemes = "UsersScheme")]
public class UsersController : Controller
{

    [HttpPost]
    public async Task<JsonResult> StartJob([FromForm] string UserId, [FromServices] IBackgroundJobService backgroundService) {

           //check user account
           (bool isStarted, string data) result = backgroundService.Start();

           return JsonResult(result);
    }
}
15
Waxren

IHostedService와 함께 백그라운드 작업의 기반으로 BlockingCollection을 (를) 계속 사용할 수 있습니다.

BlockingCollection에 대한 랩퍼를 작성하여 싱글 톤으로 삽입 할 수 있습니다.

public class TasksToRun
{
    private readonly BlockingCollection<TaskSettings> _tasks;

    public TasksToRun() => _tasks = new BlockingCollection<TaskSettings>();

    public Enqueue(TaskSettings settings) => _tasks.Add(settings);

    public Dequeue(CancellationToken token) => _tasks.Take(token);
}

그런 다음 작업에 대해 IHostedService "listen"을 구현하고 작업이 "도착"할 때 실행합니다.
BlockingCollection는 수집이 비어 있으면 실행을 중지하므로 while 루프는 프로세서 시간을 소비하지 않습니다.
.Take 메소드는 cancellationToken을 인수로 승인합니다. 토큰을 사용하면 응용 프로그램이 중지 될 때 다음 작업에 대한 "대기"를 취소 할 수 있습니다.

public class BackgroundService : IHostedService
{
    private readonly TasksToRun _tasks;

    private CancellationTokenSource _tokenSource;

    private Task _currentTask;

    public BackgroundService(TasksToRun tasks) => _tasks = tasks;

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        while (cancellationToken.IsCancellationRequested == false)
        {
            try
            {
                var taskToRun = _tasks.Dequeue(_tokenSource.Token);

                // We need to save executable task, 
                // so we can gratefully wait for it's completion in Stop method
                _currentTask = ExecuteTask(taskToRun);               
                await _currentTask;
            }
            catch (OperationCanceledException)
            {
                // execution cancelled
            }
        }
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        _tokenSource.Cancel(); // cancel "waiting" for task in blocking collection

        if (_currentTask == null) return;

        // wait when _currentTask is complete
        await Task.WhenAny(_currentTask, Task.Delay(-1, cancellationToken));
    }
}

컨트롤러에서 컬렉션에 실행할 작업을 추가하기 만하면됩니다.

public class JobController : Controller
{
    private readonly TasksToRun _tasks;

    public JobController(TasksToRun tasks) => _tasks = tasks;

    public IActionResult PostJob()
    {
        var settings = CreateTaskSettings();

        _tasks.Enqueue(settings);

        return Ok();
    }
}

수집을 차단하기위한 래퍼는 의존성 주입을 위해 싱글 톤으로 등록해야합니다

services.AddSingleton<TasksToRun, TasksToRun>();

백그라운드 서비스 등록

services.AddHostedService<BackgroundService>();
21
Fabio

Microsoft는 https://docs.Microsoft.com/en-us/aspnet/core/fundamentals/Host/hosted-services?view=aspnetcore-2.1 에 동일한 내용을 문서화했습니다.

Controller에서 할당 된 작업을 가져 오는 BackgroundTaskQueue를 사용하고 작업은 BackgroundService에서 파생 된 QueueHostedService에 의해 수행됩니다.

3
skjagini