1. 程式人生 > >.NET Core 開車記:Data Protection Key 過期問題與登入頁面訪問慢

.NET Core 開車記:Data Protection Key 過期問題與登入頁面訪問慢

K8s 船還沒修好,.net core 車又出了問題,開著 k8s 豪華郵輪、飈著 .net core 極品飛車的好事真是多磨。

自從我們用上 .net core ,就一直被 .net core 的一個慢性病所折磨,這個病叫 Data Protection Key 新陳代謝綜合症,通常3-6個月發作一次。發作時的症狀是新登入使用者在登入後依然是未登入狀態。病因是 Data Protection Key 預設3個月重新整理一次,在這個重新整理的新陳代謝階段,新舊 key 並存,新的登入 cookie 用新的 key 進行加密/解密,舊的登入 cookie 用舊的 key 解密,但有時某種未知情況會造成新陳代謝時出現功能性紊亂,新 key 加密的 cookie 卻用舊 key 解密失敗,造成解密失敗,登入 cookie 失效。

昨天下班的時候這個新陳代謝綜合症又發作了,幸好我們及時發現,立即採取急救措施,重啟所有應用恢復了正常。在急救時,我們犯了一個錯,忘了重啟了檔案上傳應用,結果造成一段時間無法上傳圖片。

Keys have a 90-day lifetime by default. When a key expires, the app automatically generates a new key and sets the new key as the active key. As long as retired keys remain on the system, your app can decrypt any data protected with them. (From docs.microsoft.com)

新陳代謝綜合症急救好之後,開車沒多久,昨天晚上又出現了新的病情,使用者登入站點突發急性消化不良症,訪問登入頁面響應速度很慢。排查後發現大量 http get 請求湧向登入頁面,對應容器的 CPU 佔用一直 150% 左右(平時不到 20%)。雖然源於大量請求,但是讓人想不通的是這個登入頁面只是顯示一下 mvc 檢視,沒有耗資源的操作,沒有任何資料庫訪問操作,即使這麼大請求也應該能撐住。嘗試給登入頁面加上 ResponseCache ,問題依舊。嘗試啟動更多容器處理請求,問題依舊。

進一步排查中發現這個突發急性症狀起源於一篇博文被設定為登入後才能訪問,大量訪問這篇博文的請求都跳轉到了登入頁面(這些請求本身也比較異常,可能是機器請求),於是在登入頁面的 mvc action 中遮蔽這些請求,但病情沒有絲毫改善,這時可以確認效能瓶頸不在 mvc action 。

知道藥用錯地方後,立即跳出當前 mvc action ,放眼整個請求處理管線上的那些 middeware 。望著日誌中不斷出現的大量錯誤,一個 middlware 立馬映入眼簾,它就是用於給 serilog 日誌提供更多上下文資訊的 LogEnrichmentMiddleware ,在這個 middleware 中加上遮蔽異常請求的程式碼後立馬藥到病除,急性消化不良症就這樣完成了急救。

public class LogEnrichmentMiddleware
{
    private static readonly ILogger Logger = Log.ForContext<LogEnrichmentMiddleware>();
    private readonly RequestDelegate _next;

    public LogEnrichmentMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        // 遮蔽異常請求
        if (httpContext.Request.Query.TryGetValue("returnUrl", out var returnUrl) &&
            WebUtility.UrlDecode(returnUrl) == "***")
        {
            httpContext.Response.StatusCode = StatusCodes.Status404NotFound;
            return;
        }

        var properties = new ILogEventEnricher[]
        {
            new PropertyEnricher("RequestUrl", httpContext.Request.GetDisplayUrl()),
            new PropertyEnricher("RequestMethod", httpContext.Request.Method),
            new PropertyEnricher("UserAgent", httpContext.Request.Headers[HeaderNames.UserAgent].ToString()),
            new PropertyEnricher("Ip", httpContext.Connection.RemoteIpAddress.ToString())
        };

        using (LogContext.Push(properties))
        {
            await _next(httpContext);

            var statusCode = httpContext.Response.StatusCode;
            if (statusCode >= 400 && statusCode != 404)
                Logger.Warning("Unsuccessful response {StatusCode}", httpContext.Response.StatusCode);
        }
    }
}

抱歉,這2個問題給您帶來麻煩了,請您諒解。我們會進一步分析病因,爭取根治這2個病症,讓 .net core 這輛車飈得更穩。