今天來寫寫工作上遇到的一個問題
在ASP.NET的架構下,使用async算是一個很平常的事情
基本上什麼都可以用async來擠出一點效能
但是有一些坑要注意的... 就像是這次的問題
因為現在是在做server的team,需要和其他team的產品用網路的方式溝通
使用HttpWebRequest去發送request是滿平常的一件事情
參考連結:https://docs.microsoft.com/zh-tw/dotnet/api/system.net.httpwebrequest?view=netframework-4.8
好吧,或許你會說文件裡都說建議用HttpClient了,但在我看起來那又是另一個坑,有機會再來說說
HttpWebRequest算是一個相對輕量簡便的類別吧
前面說到async嘛,HttpWebRequest有個GetRequestAsync的方法,是用非同步的方式去獲得response的
然後HttpWebRequest有個Timeout的屬性,是用來設定request的timeout時間的
就在我以為設定這個屬性他就會自己timeout的時候,QA就敲了一個issue給我,說他根本就不會timeout
然後我就去找了下文件,發現這個timeout如果是15秒以內的話,因為這個timeout不包含查詢DNS的時間,所以可能會在15秒以後才timeout
我就問了下QA他設timeout是幾秒,他就有點不耐煩的打了個數字
300
300秒... 肯定是哪邊出了問題...
順帶一提,因為這段code是從另一個RD寫的module搬過來的,我只是大概看了一下內容,根本沒改動,所以我表示很驚慌
然後我又去看了下文件,才發現原來在Async的情況下,這個timeout是沒有作用的,你必須要自己實作一個timeout的機制
順帶一提,是真的完全沒有用,就連預設值也是等於沒有一樣,根本就不會timeout
那問題就來了,我要怎麼實作這個timeout呢
查了一下馬上就發現一個很暴力的方法,大概是這樣:
var requestTask = HttpWebRequest_.GetResponseAsync();
var finishTask = WhenAny(requestTask, Task.Delay(timeout));
先說這看起來就很危險,只是看一看而已
強迫中斷的話等於讓request的行為放水流,根本不知道會發生什麼事XD
接下來講一下我查到覺得比較可行的方法
參考連結:https://stackoverflow.com/questions/52390383/c-sharp-abort-async-httpwebrequest-after-timeout
code的話就貼底下了,複製過來留個紀錄而已
大概就是用一個cancelltoken,然後把request的Abort()註冊到cancelltoken的timeout裡面
這樣當cancelltoken timeout的時候,就會執行Abort(),也就是取消掉這次的request,包含中斷連線這樣的動作
雖然也算是半強迫的中斷連線,不過總比上面那個暴力的方式好吧XD
public static class Extensions
{
public static async Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest request, CancellationToken ct)
{
using (ct.Register(() => request.Abort(), useSynchronizationContext: false))
{
try
{
var response = await request.GetResponseAsync();
return (HttpWebResponse)response;
}
catch (WebException ex)
{
// WebException is thrown when request.Abort() is called,
// but there may be many other reasons,
// propagate the WebException to the caller correctly
if (ct.IsCancellationRequested)
{
// the WebException will be available as Exception.InnerException
throw new OperationCanceledException(ex.Message, ex, ct);
}
// cancellation hasn't been requested, rethrow the original WebException
throw;
}
}
}
}
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(1000);
var ct = cts.Token;
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://www.zzz.com/here");
var test = Extensions.GetResponseAsync(httpWebRequest, ct);
好啦大概就這樣,雖然我自己也沒試過
因為時程壓力的關係,就先直接用沒有async的版本了
之後套用的時候再實作看看吧,不知道會怎樣... XD
留言列表