[干货]AspNetCore熟练应用CancellationToken,CTO会对你另眼相看

背景

  已经有不少文章记录了 web程序中采用异步编程的优点和.Net异步编程的用法, 异步编程虽然不能解决查询数据库的瓶颈, 可是利用线程切换,能最大限度的弹性利用工做线程, 提升了web服务的响应能力。前端

  【 9012年了,再不会异步编程你是真老了】git

       本文要说的是利用异步编程中的消机制缓解数据库的查询瓶颈开发者只需在 MVC/WebAPI查询方法体内关注CancllationToken并适时取消异步任务, 这将大大提升应用的响应能力。程序员

头脑风暴

  想象你请求某网站页面,该页面正闪着菊花试图努力绽开(正在加载),最终你忍不了:github

① F5刷新web

② 转向其余页面数据库

③ 点击浏览器“中止”按钮 编程

对于可怜的服务器,用户快速刷新5次,服务器将被迫接受 5倍的工做量,这是由于即便用户刷新了浏览器(或点击中止按钮), 虽然取消了原始浏览器请求,可是Web服务器并不Care,仍然循序渐进处理进入HTTP pipeline的请求(MVC/WebAPI 中默认行为)。其余②③场景相似。后端

在异步编程中能向任务发出Cancllation信号,中止web服务器一切后端查询行为。在.NET中,这是使用CancellationToken完成的:api

  • 取消令牌的实例传递到异步任务浏览器

  • 异步任务监视令牌,以查看请求是否已经被取消。

  • 若是请求取消,则应中止执行正在执行的操做。.NET中的大多数异步方法将具备接受取消令牌的重载。

本文所说的请求是,耗时长的服务端读取查询(返回数据但不修改数据的查询)。取消已修改数据的请求对于用程序可能不是一个好的选择:

    -  是否真的要因用户导航到应用程序中的另外一个页面而取消保存?也许能够,但也可能不会。

   -  除了数据问题,这也不会提升性能,由于数据库服务器将须要回滚该事务,这多是一项昂贵的操做。

AspNetCore实践

P1  监测CancellationToken令牌

  访问 MyReallySlowReport页面,等待5s,最终他们放弃了,去了其余页面:

 全部正在进行的请求都将被取消。

MVC/WebAPI能接受到取消请求的信号。开发者只须要在Controller Action中添加CancellationToken参数,并在后续行为中监测该取消信号。

浏览器取消请求时,AspNetCore根据CancellationTokenModelBinder自动将HttpContext.RequestAborted这个token绑定到Action的CancellationToken 参数,CancellationTokenModelBinder将会在调用AddMvc()或services.AddMvcCore()时被注入。​

public async Task<ActionResult> MyReallySlowReport(CancellationToken cancellationToken)
{
    List<ReportItem> items;
    using (ApplicationDbContext context = new ApplicationDbContext())
    {
        items = await context.ReportItems.ToListAsync(cancellationToken);
    }
    return View(items);
}

很容易取消SQL的查询行为,由于上述EF的调用api支持取消异步操做; 对于自定义的长耗时查询行为,可使用CancllationToken的原生触发用法:

public async Task<ActionResult> MyReallySlowReport(CancellationToken cancellationToken)
{
    List<ReportItem> items;
    using (ApplicationDbContext context = new ApplicationDbContext())
    {
        items = await context.ReportItems.ToListAsync(cancellationToken);
    }

    foreach (var item in items)
    {
        cancellationToken.ThrowIfCancellationRequested();
            // slow non-cancellable work
            Thread.Sleep(1000);
    }
    return View(items);
}

 P2  处理取消异步操做向上抛出的异常 

 Web服务器触发取消信号,通常会向上会抛出 OperationCanceledException 或者 TaskCancellationException,因此为了记录这种很是规异常,建议采用独立的ExceptionFilter记录。

public class OperationCancelledExceptionFilter : ExceptionFilterAttribute
{
    private readonly ILogger _logger;

    public OperationCancelledExceptionFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<OperationCancelledExceptionFilter>();
    }
    public override void OnException(ExceptionContext context)
    {
        if(context.Exception is OperationCanceledException)
        {
            _logger.LogInformation("Request was cancelled");
            context.ExceptionHandled = true;
            context.Result = new StatusCodeResult(400);
        }
    }
}

P3  想要获得CTO的称赞,可不是那么简单。

以上只是后端程序员利用取消机制缓解异步查询瓶颈的后端操做,从web应用全流程角度思考,这个优化还能提高吗?

> 以上是传统的网页请求场景,在取消请求时,浏览器帮助咱们发起了Cancellation信号。 

> 想一想日益常见的SPA程序(单页面程序),绝大部分页面请求都是Ajax请求,你点击应用的另一个“页面(JS代码维护页面导航),浏览器不会自动取消请求。

因此在SPA应用中,前端要本身发出取消请求的信号

var xhr = $.get("/api/myslowreport", function(data){
  //show the data
});

//If the user navigates away from this page
xhr.abort() 

先后端程序猿通力配合, 应用的吞吐量和响应能力极大提高, CTO要给各位加薪了。

相关文章
相关标签/搜索