AspNetCore应用注意这一点,CTO会对你刮目相看
背景
已經(jīng)有很多文章記錄了Web程序中采用異步編程的優(yōu)勢(shì)和.Net異步編程的用法, 異步編程雖然不能解決查詢數(shù)據(jù)庫的瓶頸, 但是利用線程切換,能最大限度的彈性利用工作線程, 提高了web服務(wù)的響應(yīng)能力。
??????????9012年了,再不會(huì)異步編程你是真老了?????
?????本文要說的是利用異步編程中的取消機(jī)制緩解數(shù)據(jù)庫的查詢瓶頸,開發(fā)者只需在MVC/WebAPI查詢方法體內(nèi)關(guān)注CancellationToken并適時(shí)取消異步任務(wù), 這將大大提高應(yīng)用的響應(yīng)能力。
頭腦風(fēng)暴
想象你請(qǐng)求某網(wǎng)站頁面,該頁面正閃著菊花試圖努力綻放(正在加載),最終你忍不了:
① F5刷新
② 轉(zhuǎn)向其他頁面
③ 點(diǎn)擊瀏覽器“停止”按鈕?
對(duì)于Web服務(wù)器,用戶快速刷新5次,服務(wù)器將被迫接受 5倍的工作量,這是因?yàn)榧词褂脩羲⑿铝藶g覽器(或點(diǎn)擊停止按鈕), 雖然取消了原始瀏覽器請(qǐng)求,但是Web服務(wù)器并不Care,仍然按部就班處理進(jìn)入HTTP pipeline的請(qǐng)求(MVC/WebAPI 中默認(rèn)行為,②③場(chǎng)景類似)
在異步編程中能向任務(wù)發(fā)出Cancellation信號(hào),停止web服務(wù)器后端查詢行為。在.NET中,這是使用CancellationToken完成的:
取消令牌的實(shí)例傳遞到異步任務(wù)
異步任務(wù)監(jiān)視令牌,以查看請(qǐng)求是否已經(jīng)被取消。
如果請(qǐng)求取消,則應(yīng)停止執(zhí)行正在執(zhí)行的操作。.NET中的大多數(shù)異步方法將具有接受取消令牌的重載。
tip
本文取消的請(qǐng)求,指的是耗時(shí)長(zhǎng)的服務(wù)端讀取請(qǐng)求(返回?cái)?shù)據(jù)但不修改數(shù)據(jù)的查詢),取消已修改數(shù)據(jù)的請(qǐng)求對(duì)于用程序可能不是一個(gè)好的選擇:
①?是否真的要因?yàn)橛脩魧?dǎo)航到應(yīng)用的另一個(gè)頁面而取消保存?也許可以,但也可能不會(huì)。
② 提高了復(fù)雜性,因?yàn)閿?shù)據(jù)庫服務(wù)器可能需要回滾事務(wù),這是一項(xiàng)昂貴的操作。
AspNetCore實(shí)踐
訪問MyReallySlowReport頁面,等待5s,最終他們放棄去了其他頁面:
所有正在進(jìn)行的請(qǐng)求都將被取消。
P1 監(jiān)測(cè)CancellationToken令牌
MVC/WebAPI能收到取消請(qǐng)求的信號(hào)。開發(fā)者只需要在Controller Action中添加CancellationToken參數(shù),并在后續(xù)行為中監(jiān)測(cè)該取消信號(hào)。
public?async?Task<ActionResult>?MyReallySlowReport(CancellationToken?cancellationToken){
????List<ReportItem>?items;
????using?(ApplicationDbContext?context?=?new?ApplicationDbContext())
????{
????????items?=?await?context.ReportItems.ToListAsync(cancellationToken);
????}
????return?View(items);
}
上述EF的調(diào)用api支持取消異步操作,故很容易取消SQL的查詢行為;對(duì)于自定義的長(zhǎng)耗時(shí)查詢行為,可以使用CancellationToken的原生觸發(fā)用法:
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服務(wù)器觸發(fā)取消信號(hào),一般會(huì)向上會(huì)拋出OperationCanceledException或TaskCancellationException,所以為了記錄這種非常規(guī)異常,建議采用獨(dú)立的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的大拇指,繼續(xù)思考吧
以上是后端程序員利用取消機(jī)制緩解異步查詢瓶頸的操作,從web應(yīng)用全流程角度思考,這個(gè)優(yōu)化還能提升嗎??????
> 以上是傳統(tǒng)的網(wǎng)頁請(qǐng)求場(chǎng)景,在取消請(qǐng)求時(shí),瀏覽器幫助我們發(fā)起了Cancellation信號(hào)。
>?想想日益常見的SPA程序(單頁面程序),絕大部分頁面請(qǐng)求都是Ajax請(qǐng)求,你點(diǎn)擊應(yīng)用的另外一個(gè)“頁面”(JS代碼維護(hù)頁面導(dǎo)航),瀏覽器不會(huì)自動(dòng)取消請(qǐng)求。所以在SPA應(yīng)用中要前端自己發(fā)出取消請(qǐng)求的信號(hào):
var?xhr?=?$.get("/api/myslowreport",?function(data){??//show?the?data
});
//If?the?user?navigates?away?from?this?page
xhr.abort()
前后端程序猿通力配合,應(yīng)用的吞吐量和響應(yīng)能力極大提升,CTO要給各位加薪了。
總結(jié)
以上是生活随笔為你收集整理的AspNetCore应用注意这一点,CTO会对你刮目相看的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 王炸吐血整理60个Redis面试题,全网
- 下一篇: ASP.NET Core如何限制请求频率