내용 보기

작성자

관리자 (IP : 106.247.248.10)

날짜

2022-08-24 15:47

제목

[ASP.NET Core] 중복 요청 취소 처리하기


사용자가 브라우저를 통해 웹서핑을 하는데에 있어 네트워크 이상으로 일시적으로 느려지거나 서버측에 오래 걸리는 작업을 요청했을때
대부분 로딩중인 상태를 기다리지 못하고 연속적으로 새로고침을 누르거나 url요청을 다시 하게 됩니다.
이렇게 같은 요청이 중복으로 지속 요청된다면 웹 서버측은 모든 요청을 그대로 받아 들이고 WAS는 해당 요청을 중복으로 처리되어 상당히 비효율적인 결과가 나오게 됩니다.

ASP.NET Core 에서는 이렇게 연속적으로 중복 요청 되는 부분을 쉽게 ‘취소’ 처리할 수 있도록 System.Threading.CancellationToken 구조체를 자동 바인딩 처리를 제공합니다.
다음 예제는 약 1초의 시간이 걸리는 10번 작업을 하는 요청 시간이 오래 걸리는 예제 입니다.

[HttpGet("SlowTest")]
public async Task<string> SlowTest()
{
  Console.WriteLine("Starting to do slow work");

  for (var i = 0; i < 10; i++)
  {
    await Task.Delay(1_000);
  }
  var message = "Finished slow delay of 10 seconds.";
  return message;
}

브라우저에서 위 요청하고 연속 새로고침을 2번하면 총 3배의 작업 처리가 되는걸 확인할 수 있습니다.

image

CancellationTokens 사용

asp.net core는 System.Threading.CancellationToken 구조체를 자동 바인딩을 제공하고 이렇게 중복으로 요청될때 기존 요청건에 IsCancellationRequested 속성으로 취소 통보된 요청건인지 확인할 수 있습니다.

[HttpGet("SlowTest")]
public async Task<string> SlowTest(CancellationToken token)
{
  Console.WriteLine("Starting to do slow work");
  
  for (var i = 0; i < 10; i++)
  {
    await Task.Delay(1_000, token);
  }
  
  var message = "Finished slow delay of 10 seconds.";
  return message;
}

연속 중복 요청 결과

image

System.Threading.Tasks.Task.Delay 취소 토큰을 넘겨 System.Threading.Tasks.TaskCanceledException 익셉션을 발생시키도록 합니다.

취소된 요청 상태 확인

해당 요청건이 취소 요청인지 상태 확인은 System.Threading.CancellationToken 구조체의 IsCancellationRequested 속성으로 확인할 수 있습니다.

[HttpGet("SlowTest")]
public async Task<string> SlowTest(CancellationToken token)
{
  Console.WriteLine("Starting to do slow work");
  
  for (var i = 0; i < 10; i++)
  {
    if(token.IsCancellationRequested)
    {
      Console.WriteLine("Cancel");
    }
                
    Thread.Sleep(1_000);
  }

  var message = "Finished slow delay of 10 seconds.";
  return message;
}

위 코드처럼 요청 취소시 익셉션으로 처리 하지 않기 위해 Thread.Sleep로 바꾸고 IsCancellationRequested 속성으로 확인해 볼 수 있습니다.

image

Middleware로 취소 감지 처리

Middleware를 사용해서 통합적으로 System.Threading.Tasks.TaskCanceledException 예외를 깔끔하게 처리할 수 있습니다.
[CancelMiddleware.cs]

public class CancelMiddleware
{
  private readonly RequestDelegate _next;
  
  public CancelMiddleware(RequestDelegate next)
  {
    _next = next;
  }
  
  public async Task Invoke(HttpContext context)
  {
    try
    {
      await _next(context);
    }
    catch (OperationCanceledException)
    {
      Console.WriteLine("Request was cancelled");
      context.Response.StatusCode = 409;
    }
  }
}

[Program.cs]

using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

// middleware 추가
app.UseMiddleware<CancelMiddleware>();

app.MapControllers();

app.Run();
[HttpGet("SlowTest")]
public async Task<string> SlowTest(CancellationToken token)
{
  Console.WriteLine("Starting to do slow work");
  
  for (var i = 0; i < 10; i++)
  {
    if(token.IsCancellationRequested)
    {
      Console.WriteLine("Cancel");
      token.ThrowIfCancellationRequested();  // TaskCanceledException 예외 발생
    }
  
    Thread.Sleep(1000);
  }

  var message = "Finished slow delay of 10 seconds.";
  return message;
}

위 처럼 CancelMiddleware 에서 System.Threading.Tasks.TaskCanceledException 예외가 발생하면 409 Client Closed Request 응답으로 반환 처리 합니다.

image

출처1

https://andrewlock.net/using-cancellationtokens-in-asp-net-core-minimal-apis/

출처2