close

今天來寫寫工作上遇到的一個問題

在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

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 頁頁頁六滴 的頭像
    頁頁頁六滴

    人森很精彩,所以要把所有事情都記起來=ˇ=

    頁頁頁六滴 發表在 痞客邦 留言(0) 人氣()