結合ASP.NET Core聊一聊取消機制緩解web後臺性能壓力

背景

  已經有很多文章記錄了 web程序中採用異步編程的優勢和.Net異步編程的用法, 異步編程雖然不能解決查詢數據庫的瓶頸, 但是利用線程切換,能最大限度的彈性利用工作線程, 提高了web服務的響應能力。

  【 9012年了,再不會異步編程你是真老了】

       本文要說的是利用取消機制緩解後臺的查詢瓶頸開發者只需在 MVC/WebAPI查詢方法體內關注CancellationToken並適時取消異步任務, 這將大大提高應用的響應能力。

頭腦風暴

  想象你請求某網站頁面,該頁面正閃着菊花試圖努力綻放(正在加載),最終你忍不了:

① F5刷新

② 轉向其他頁面

③ 點擊瀏覽器“停止”按鈕 

對於可憐的服務器,用戶快速刷新5次,服務器將被迫接受 5倍的工作量,這是因爲即使用戶刷新了瀏覽器(或點擊停止按鈕), 雖然取消了原始瀏覽器請求,但是Web服務器並不Care,仍然按部就班處理進入HTTP pipeline的請求(MVC/WebAPI 中默認行爲),其他②③場景類似。

異步編程中能向任務發出Cancellation信號,停止web服務器一切後端查詢行爲。

(此處更正,不應強調異步編程,能否發送取消信號是客戶端決定,服務端要做的是準備好信號接收器CancellationToken, 也就是說本文的取消機制對異步、 同步均有效)

在.NET中,這是使用CancellationToken完成的。

  • 取消令牌的實例傳遞到異步任務

  • 異步任務監視令牌,以查看請求是否已經被取消。

  • 如果請求取消,則應停止執行正在執行的操作。.NET中的大多數異步方法將具有接受取消令牌的重載。

本文所說的請求是,耗時長的服務端讀取查詢(返回數據但不修改數據的查詢)。取消已修改數據的請求對於用程序可能不是一個好的選擇:

    -  是否真的要因用戶導航到應用程序中的另一個頁面而取消保存?也許可以,但也可能不會。

   -  除了數據問題,這也不會提高性能,因爲數據庫服務器將需要回滾該事務,這可能是一項昂貴的操作。

ASP.NET Core實踐

監測CancellationToken令牌

  訪問 MyReallySlowReport頁面,等待5s,最終他們放棄了,去了其他頁面:

 所有正在進行的請求都將被取消。

MVC/WebAPI能接受到取消請求的信號。開發者只需要在Controller Action中添加CancellationToken參數,並在後續行爲中監測該取消信號。

瀏覽器取消請求時,ASP.NET Core根據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支持取消異步操作; 對於自定義的長耗時查詢行爲,可以使用CancellationToken的原生觸發用法:

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);
}

從以上代碼也可推斷出: CancellationToken 適用於同步方法。

處理取消操作拋出的異常 

 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);
        }
    }
}

SPA應用

以上是後端程序員利用 取消機制 緩解後臺性能壓力,針對的是瀏覽器觸發的取消操作。 從web請求的產生來源分析,除了瀏覽器能發起請求, 編程也可以發起請求

>  想想日益常見的SPA程序(單頁面程序),絕大部分頁面請求都是ajax請求,你點擊應用的另外一個“頁面(JS代碼維護頁面導航),瀏覽器不會自動取消請求。

在SPA應用中,前端發起的ajax請求, 若要取消,是需要以編程方式取消的。

所以我牆裂建議, 在2C的SPA應用中,切換“頁面“”時請 終止原頁面的所有ajax請求

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

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

 

 

總結輸出  

1. 利用取消機制的前置條件 在於 請求能夠發出取消指令
2. 利用取消機制的關鍵在於監測取消令牌,並作出取消操作
3. 請求不但可以從瀏覽器發起,ajax也可以發起, 在SPA程序中極其常見,故取消機制需要關注SPA應用的ajax取消

That‘s all ,前後端程序猿通力配合, 應用的吞吐量和響應能力極大提升, CTO要給各位加薪了。

 

https://www.cnblogs.com/JulianHuang/p/10678887.html

https://www.cnblogs.com/JulianHuang/p/10572840.html

原文出處:https://www.cnblogs.com/JulianHuang/p/11881367.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章