.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 这辆车飚得更稳。

posted @ 2020-01-09 13:47  博客园团队  阅读(...)  评论(...编辑  收藏