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 這輛車飈得更穩。