当有很多调用Trace.WriteLine()时,在Linux容器中运行的ASP.NET Core 2.0应用程序会遇到锁定问题

Ala*_*ark 11 docker .net-core asp.net-core

我们的应用程序是部署在Linux Docker容器中并在Kubernetes中运行的ASP.NET Core 2.0 WebAPI.

在负载测试期间,我们发现CPU使用率出现间歇性峰值,而我们的应用程序永远无法从中恢复.

我们使用perfcollect从容器中收集痕迹,以便我们可以将成功的测试和测试与CPU峰值进行比较.我们发现,失败测试中大约75%的CPU时间花费在JIT_MonRelaibleEnter_Protable锁定操作的接口上.打电话的是System.Diagnostics.TraceSource.dll.

我们的应用程序是从.NET Framework移植而来的,包含很多调用System.Diagnostics.Trace.WriteLine().当我们删除所有这些时,我们的CPU /内存使用量减少了50%以上,我们不再看到CPU峰值了.

我想了解这个问题的原因.

在corefx repo中,我可以看到在TraceInternal.cs中设置了一个默认的跟踪侦听:

public static TraceListenerCollection Listeners
    {
        get
        {
            InitializeSettings();
            if (s_listeners == null)
            {
                lock (critSec)
                {
                    if (s_listeners == null)
                    {
                        // In the absence of config support, the listeners by default add
                        // DefaultTraceListener to the listener collection.
                        s_listeners = new TraceListenerCollection();
                        TraceListener defaultListener = new DefaultTraceListener();
                        defaultListener.IndentLevel = t_indentLevel;
                        defaultListener.IndentSize = s_indentSize;
                        s_listeners.Add(defaultListener);
                    }
                }
            }
            return s_listeners;
        }
    }
Run Code Online (Sandbox Code Playgroud)

我可以看到DefaultTraceListener.cs调用Debug.Write():

private void Write(string message, bool useLogFile)
    {
        if (NeedIndent) 
            WriteIndent();

        // really huge messages mess up both VS and dbmon, so we chop it up into 
        // reasonable chunks if it's too big
        if (message == null || message.Length <= InternalWriteSize)
        {
            Debug.Write(message);
        }
        else
        {
            int offset;
            for (offset = 0; offset < message.Length - InternalWriteSize; offset += InternalWriteSize)
            {
                Debug.Write(message.Substring(offset, InternalWriteSize));
            }
            Debug.Write(message.Substring(offset));
        }

        if (useLogFile && !string.IsNullOrEmpty(LogFileName))
            WriteToLogFile(message);
    }
Run Code Online (Sandbox Code Playgroud)

Debug.Unix.cs中,我可以看到有一个对SysLog的调用:

 private static void WriteToDebugger(string message)
        {
            if (Debugger.IsLogging())
            {
                Debugger.Log(0, null, message);
            }
            else
            {
                Interop.Sys.SysLog(Interop.Sys.SysLogPriority.LOG_USER | Interop.Sys.SysLogPriority.LOG_DEBUG, "%s", message);
            }
        }
Run Code Online (Sandbox Code Playgroud)

我没有很多使用Linux的经验,但我相信我可以通过在容器中运行以下命令来模拟对SysLog的调用:

logger --socket-errors=on 'SysLog test'
Run Code Online (Sandbox Code Playgroud)

当我运行该命令时,我得到以下响应:

socket/dev/log:没有这样的文件或目录

所以看起来我无法从容器中成功调用SysLog.如果这确实是我打电话时发生的事情Trace.WriteLine(),为什么它会在我的应用程序中导致锁定问题?

据我所知,EnvVar_DebugWriteToStdErr未在我的容器中设置,所以它不应该尝试写入StdErr.

and*_*bin 1

解决方案可能是 rsyslog 未运行。它安装在您的容器中吗?使用内置 rsyslog 的基础映像。

此链接也可以提供帮助。