AspNetCore熟練應用CancellationToken,CTO會對你刮目相看
背景
已經有很多文章記錄了 web程序中採用異步編程的優勢和.Net異步編程的用法, 異步編程雖然不能解決查詢數據庫的瓶頸, 但是利用線程切換,能最大限度的彈性利用工作線程, 提高了web服務的響應能力。
【 9012年了,再不會異步編程你是真老了】
本文要說的是利用異步編程中的取消機制緩解數據庫的查詢瓶頸,開發者只需在 MVC/WebAPI查詢方法體內關注CancllationToken並適時取消異步任務, 這將大大提高應用的響應能力。
頭腦風暴
想象你請求某網站頁面,該頁面正閃着菊花試圖努力綻放(正在加載),最終你忍不了:
① F5刷新
② 轉向其他頁面
③ 點擊瀏覽器“停止”按鈕
對於可憐的服務器,用戶快速刷新5次,服務器將被迫接受 5倍的工作量,這是因為即使用戶刷新了瀏覽器(或點擊停止按鈕), 雖然取消了原始瀏覽器請求,但是Web服務器並不Care,仍然按部就班處理進入HTTP pipeline的請求(MVC/WebAPI 中默認行為)。其他②③場景類似。
在異步編程中能向任務發出Cancllation信號,停止web服務器一切後端查詢行為。在.NET中,這是使用CancellationToken完成的:
-
取消令牌的實例傳遞到異步任務
-
異步任務監視令牌,以查看請求是否已經被取消。
-
如果請求取消,則應停止執行正在執行的操作。.NET中的大多數異步方法將具有接受取消令牌的重載。
本文所說的請求是,耗時長的服務端讀取查詢(返回數據但不修改數據的查詢)。取消已修改數據的請求對於用程序可能不是一個好的選擇:
– 是否真的要因用戶導航到應用程序中的另一個頁面而取消保存?也許可以,但也可能不會。
– 除了數據問題,這也不會提高性能,因為數據庫服務器將需要回滾該事務,這可能是一項昂貴的操作。
AspNetCore實踐
P1 監測CancellationToken令牌
訪問 MyReallySlowReport頁面,等待5s,最終他們放棄了,去了其他頁面:
所有正在進行的請求都將被取消。
MVC/WebAPI能接受到取消請求的信號。開發者只需要在Controller Action中添加CancellationToken參數,並在後續行為中監測該取消信號。
瀏覽器取消請求時,AspNetCore根據自動將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()
That‘s all ,前後端程序猿通力配合, 應用的吞吐量和響應能力極大提升, CTO要給各位加薪了。
+
+
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益
※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象
※台灣寄大陸海運貨物規則及重量限制?
※大陸寄台灣海運費用試算一覽表
※南投搬家前需注意的眉眉角角,別等搬了再說!