为什么我的代码显然调用了LocalDataStoreMgr.GetNamedDataSlot?

时间:2014-01-16 10:15:11

标签: c# asp.net multithreading profiling newrelic

当我们的ASP.NET网站负载很重的时候,其中一台服务器会疯狂地使用100%CPU而根本没有响应。当我在正在运行的进程上运行stackdump.exe时,我发现几乎所有线程都以方法LocalDataStoreMgr.GetNamedDataSlot结束。有些堆栈看起来好像我们自己的代码调用GetNamedDataSlot,其他堆栈显示HttpApplication.ExecuteStepHttpWriter.Write这样做。这是堆栈转储的片段:

OS Thread Id:9232
  System.LocalDataStoreMgr.GetNamedDataSlot(System.LocalDataStoreMgrN/A)
  System.Web.HttpApplication.ExecuteStep(ASP.global_asaxSystem.Web.HttpApplication+AsyncEventExecutionStepSystem.Boolean&)
  System.Web.HttpApplication+PipelineStepManager.ResumeSteps(System.Web.HttpApplication+PipelineStepManagerN/A)
  System.Web.HttpApplication.BeginProcessRequestNotification(N/AN/AN/A)
  System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.HttpRuntimeSystem.Web.Hosting.IIS7WorkerRequestSystem.Web.HttpContext)
  System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(N/ASystem.IntPtrN/AN/A)
  System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(N/AN/AN/AN/A)
  System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(N/AN/AN/AN/A)
  System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(N/AN/AN/AN/A)

OS Thread Id:15308
  System.LocalDataStoreMgr.GetNamedDataSlot(System.LocalDataStoreMgrN/A)
  System.Web.HttpWriter.Write(System.Web.HttpWriterSystem.String)
  System.Web.HttpWriter.Write(N/AN/A)
  ASP.views_shared_advertcat_setadvertinformation_ascx.__Render__control1(N/AN/AN/A)
  System.Web.UI.Control.RenderChildrenInternal(N/AN/AN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Control.RenderChildrenInternal(N/AN/AN/A)
  System.Web.Mvc.ViewPage.Render(System.Web.Mvc.ViewUserControl+ViewUserControlContainerPageN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Page.ProcessRequestMain(System.Web.Mvc.ViewUserControl+ViewUserControlContainerPageSystem.BooleanSystem.Boolean)
  System.Web.UI.Page.ProcessRequest(System.Web.Mvc.ViewUserControl+ViewUserControlContainerPageN/ASystem.Boolean)
  System.Web.UI.Page.ProcessRequest(N/AN/AN/A)
  System.Web.UI.Page.ProcessRequest(System.Web.Mvc.ViewUserControl+ViewUserControlContainerPage)
  System.Web.UI.Page.ProcessRequest(N/AN/A)
  System.Web.Mvc.ViewPage.ProcessRequest(N/AN/A)
  System.Web.Mvc.ViewUserControl+ViewUserControlContainerPage.ProcessRequest(N/AN/A)
  System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper+<>c__DisplayClass4.<Wrap>b__3(N/A)
  System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper.Wrap(N/A)
  System.Web.HttpServerUtility.ExecuteInternal(System.Web.HttpServerUtilitySystem.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapperN/AN/AN/ASystem.Web.VirtualPathN/AN/ASystem.ExceptionSystem.String)
  System.Web.HttpServerUtility.Execute(N/AN/AN/AN/AN/A)
  System.Web.HttpServerUtility.Execute(N/AN/AN/AN/A)
  System.Web.Mvc.ViewPage.RenderView(N/AN/A)
  System.Web.Mvc.ViewUserControl.RenderView(N/AN/A)
  System.Web.Mvc.ViewResultBase.ExecuteResult(N/AN/A)
  System.Web.Mvc.ControllerActionInvoker+<>c__DisplayClass1a.<InvokeActionResultWithFilters>b__17(N/A)
  System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(Funda.Web.Mvc.Controllers.Shared.AdvertControllerSystem.Web.Mvc.ResultExecutingContextN/A)
  System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(N/AN/AN/AN/A)
  System.Web.Mvc.Async.AsyncControllerActionInvoker+<>c__DisplayClass25.<BeginInvokeAction>b__22(System.Web.Mvc.Async.AsyncControllerActionInvoker+<>c__DisplayClass25N/A)
  System.Web.Mvc.Controller+<>c__DisplayClass1d.<BeginExecuteCore>b__18(N/AN/A)
  System.Web.Mvc.Async.AsyncResultWrapper+<>c__DisplayClass4.<MakeVoidDelegate>b__3(N/AN/A)
  System.Web.Mvc.Controller.EndExecuteCore(Funda.Web.Mvc.Controllers.Shared.AdvertControllerN/A)
  System.Web.Mvc.Async.AsyncResultWrapper+<>c__DisplayClass4.<MakeVoidDelegate>b__3(N/AN/A)
  System.Web.Mvc.MvcHandler+<>c__DisplayClass8.<BeginProcessRequest>b__3(System.Web.Mvc.MvcHandler+<>c__DisplayClass8N/A)
  System.Web.Mvc.Async.AsyncResultWrapper+<>c__DisplayClass4.<MakeVoidDelegate>b__3(N/AN/A)
  System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper+<>c__DisplayClass4.<Wrap>b__3(N/A)
  System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper.Wrap(N/A)
  System.Web.HttpServerUtility.ExecuteInternal(System.Web.HttpServerUtilitySystem.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapperN/AN/AN/ASystem.Web.VirtualPathN/AN/ASystem.ExceptionSystem.String)
  System.Web.HttpServerUtility.Execute(N/AN/AN/AN/AN/A)
  System.Web.HttpServerUtility.Execute(N/AN/AN/AN/A)
  System.Web.Mvc.Html.ChildActionExtensions.ActionHelper(N/AN/AN/AN/AN/A)
  ASP.views_homepage_homepage_aspx.__Render__control5(N/AN/AN/A)
  System.Web.UI.Control.RenderChildrenInternal(N/AN/AN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  ASP.views_masterpages_homepage_master.__Render__control1(ASP.views_masterpages_homepage_masterSystem.Web.UI.HtmlTextWriterASP.views_masterpages_homepage_master)
  System.Web.UI.Control.RenderChildrenInternal(N/AN/AN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Control.RenderChildrenInternal(N/AN/AN/A)
  System.Web.Mvc.ViewPage.Render(ASP.views_homepage_homepage_aspxN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Control.RenderControl(N/AN/AN/A)
  System.Web.UI.Page.ProcessRequestMain(ASP.views_homepage_homepage_aspxSystem.BooleanSystem.Boolean)
  System.Web.UI.Page.ProcessRequest(ASP.views_homepage_homepage_aspxN/ASystem.Boolean)
  System.Web.UI.Page.ProcessRequest(N/AN/AN/A)
  System.Web.UI.Page.ProcessRequest(ASP.views_homepage_homepage_aspx)
  System.Web.UI.Page.ProcessRequest(N/AN/A)
  System.Web.Mvc.ViewPage.ProcessRequest(N/AN/A)
  System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper+<>c__DisplayClass4.<Wrap>b__3(N/A)
  System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper.Wrap(N/A)
  System.Web.HttpServerUtility.ExecuteInternal(System.Web.HttpServerUtilitySystem.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapperN/AN/AN/ASystem.Web.VirtualPathN/AN/ASystem.ExceptionSystem.String)
  System.Web.HttpServerUtility.Execute(N/AN/AN/AN/AN/A)
  System.Web.HttpServerUtility.Execute(N/AN/AN/AN/A)
  System.Web.Mvc.ViewPage.RenderView(N/AN/A)
  System.Web.Mvc.ViewResultBase.ExecuteResult(N/AN/A)
  System.Web.Mvc.ControllerActionInvoker+<>c__DisplayClass1a.<InvokeActionResultWithFilters>b__17(N/A)
  System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(Funda.Web.Mvc.Attributes.DataForGoogleAnalyticsAttributeSystem.Web.Mvc.ResultExecutingContextN/A)
  System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(Funda.Web.Mvc.Attributes.SetLanguageFromProfileAttributeSystem.Web.Mvc.ResultExecutingContextN/A)
  System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(Funda.Web.Mvc.Attributes.DataForLoginAttributeSystem.Web.Mvc.ResultExecutingContextN/A)
  System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(Funda.Web.Mvc.Attributes.DataForNotifyAttributeSystem.Web.Mvc.ResultExecutingContextN/A)
  System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(Funda.Web.Mvc.Controllers.HomepageControllerSystem.Web.Mvc.ResultExecutingContextN/A)
  System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(N/AN/AN/AN/A)
  System.Web.Mvc.Async.AsyncControllerActionInvoker+<>c__DisplayClass25.<BeginInvokeAction>b__22(System.Web.Mvc.Async.AsyncControllerActionInvoker+<>c__DisplayClass25N/A)
  System.Web.Mvc.Controller+<>c__DisplayClass1d.<BeginExecuteCore>b__18(N/AN/A)
  System.Web.Mvc.Async.AsyncResultWrapper+<>c__DisplayClass4.<MakeVoidDelegate>b__3(N/AN/A)
  System.Web.Mvc.Controller.EndExecuteCore(Funda.Web.Mvc.Controllers.HomepageControllerN/A)
  System.Web.Mvc.Async.AsyncResultWrapper+<>c__DisplayClass4.<MakeVoidDelegate>b__3(N/AN/A)
  System.Web.Mvc.MvcHandler+<>c__DisplayClass8.<BeginProcessRequest>b__3(System.Web.Mvc.MvcHandler+<>c__DisplayClass8N/A)
  System.Web.Mvc.Async.AsyncResultWrapper+<>c__DisplayClass4.<MakeVoidDelegate>b__3(N/AN/A)
  System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute(N/A)
  System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute(N/A)
  System.Web.HttpApplication.ExecuteStep(ASP.global_asaxN/ASystem.Boolean&)
  System.Web.HttpApplication.ExecuteStep(N/AN/AN/A)
  System.Web.HttpApplication+PipelineStepManager.ResumeSteps(System.Web.HttpApplication+PipelineStepManagerN/A)
  System.Web.HttpApplication.BeginProcessRequestNotification(N/AN/AN/A)
  System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.HttpRuntimeSystem.Web.Hosting.IIS7WorkerRequestSystem.Web.HttpContext)
  System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(N/ASystem.IntPtrN/AN/A)
  System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(N/AN/AN/AN/A)
  System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(N/AN/AN/AN/A)
  System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(N/AN/AN/AN/A)


OS Thread Id:25392
  System.LocalDataStoreMgr.GetNamedDataSlot(System.LocalDataStoreMgrN/A)
  Funda.App.Caching.CacheManager.GetFromCache(Funda.App.Caching.CacheManagerSystem.StringSystem.Func`1<Funda.Common.ServiceLayer.KoppelenEngineService.KlantMijnMakelaarKoppeling>)
  Funda.Web.Mvc.Mappers.Shared.MijnMakelaarMapper.GetMijnMakelaarKoppeling(N/AN/A)
  Funda.Web.Mvc.Mappers.SharedViewModelMapper.MapToLoginViewModel(N/AN/AN/AN/AN/AN/A)
  Funda.Web.Mvc.Attributes.DataForLoginAttribute.OnActionExecuted(N/AN/A)
  ...

我想我理解线程本地存储是什么,但我不明白为什么这个调用显然来自这些地方。我们没有明确地在任何地方调用Thread.GetNamedDataSlot。这些日志所指示的方法中的BCL代码也没有(如果我可以信任ILSpy)。

为什么这种方法经常被调用? 我应该怎么做才能防止这种情况(看起来像是锁定热点)?


更新

GetFromCache方法如下所示。 _instrumentation字段是静态的。该类的实现也涉及到线程感知代码。

public T GetFromCache<T>(string key, Func<T> fetchAction, bool stampedeProtection, Func<T, Boolean> needsStoring)
{
    // argh?
    key = AddCacheVersionNumberToKey(key);

    long startTime = 0;
    long endTime = 0;
    int timesSlept = 0;
    if (_usingCounters)
    {
        NativeMethods.QueryPerformanceCounter(ref startTime);
    }
    _log.Debug("GetFromCache: [" + _durationGroup + "] " + key);

    object obj = null;
    while (obj == null)
    {
        obj = _store.Get(key);
        if(obj is NotImplementedException)
        {
            // this is a nasty case where the store is not able to deserialize without knowing the type
            obj = _store.Get<T>(key);
        }
        if (obj is NullValue)
        {
            return default(T);
        }
        if (CacheDisabledFromUrl(key) || obj is String && string.IsNullOrEmpty(obj.ToString()))
        {
            obj = null;
        }
        if (obj == null)
        {
            // Niet gevonden
            if (timesSlept > Settings.Default.StampedeMaxSleepCycles)
            {
                _log.WarnFormat("Waiting for {0} x {1} ms for a locked cache key. Some other thread is taking very long to fill {2}. We will try it ourselves.", Settings.Default.StampedeMaxSleepCycles, Settings.Default.StampedeSleepTime, key);

                stampedeProtection = false;
            }
            if (stampedeProtection)
            {
                // special case: we want to make sure that only one thread does the fetch after the miss.
                if (_store.AcquireLock(key))
                {
                    if (_usingCounters)
                    {
                        // log a miss only when we acquire the lock: other cases are just a slow hit
                        NativeMethods.QueryPerformanceCounter(ref endTime);
                        _instrumentation.RegisterMiss(_durationGroup, endTime - startTime, key);
                        startTime = endTime;
                    }

                    try
                    {
                        // We have to do the work
                        obj = fetchAction.Invoke();
                        if (_usingCounters)
                        {
                            NativeMethods.QueryPerformanceCounter(ref endTime);
                            _instrumentation.RegisterFetch(_durationGroup, obj, endTime - startTime, key);
                        }
                        if (obj == null)
                        {
                            _store.Store(key, new NullValue());

                            return default(T);
                        }
                        else if (needsStoring((T)obj))
                        {

                            if (_usingCounters)
                            {
                                NativeMethods.QueryPerformanceCounter(ref startTime);
                            }
                            _store.Store<T>(key, (T)obj);
                            if (_usingCounters)
                            {
                                NativeMethods.QueryPerformanceCounter(ref endTime);
                                _instrumentation.RegisterStore(_durationGroup, obj, endTime - startTime, key);
                            }
                        }
                    }
                    finally
                    {
                        _store.ReleaseLock(key);
                    }
                }
                else
                {
                    // Wait and try again
                    // after 500 ms, hopefully, another thread will have filled the cache
                    Thread.Sleep(500);
                    timesSlept++;
                }
            }
            else
            {
                // Standard flow for miss: fetch and store
                if (_usingCounters)
                {
                    // log a miss
                    NativeMethods.QueryPerformanceCounter(ref endTime);
                    _instrumentation.RegisterMiss(_durationGroup, endTime - startTime, key);
                    startTime = endTime;
                }
                obj = fetchAction.Invoke();
                if (_usingCounters)
                {
                    // log a fetch
                    NativeMethods.QueryPerformanceCounter(ref endTime);
                    _instrumentation.RegisterFetch(_durationGroup, obj, endTime - startTime, key);
                }
                if (obj == null)
                {
                    _store.Store(key, new NullValue());

                    return default(T);
                }
                if (needsStoring((T)obj))
                {
                    if (_usingCounters) NativeMethods.QueryPerformanceCounter(ref startTime);
                    _store.Store<T>(key, (T)obj);
                    if (_usingCounters)
                    {
                        NativeMethods.QueryPerformanceCounter(ref endTime);
                        _instrumentation.RegisterStore(_durationGroup, obj, endTime - startTime, key);
                    }
                }
            }
        }
        else
        {
            // Get is (uiteindelijk) gelukt. We loggen een Hit
            if (_usingCounters)
            {
                NativeMethods.QueryPerformanceCounter(ref endTime);
                _instrumentation.RegisterHit(_durationGroup, obj, endTime - startTime, key);
            }

            if (HttpContext.Current != null && HttpContext.Current.Request.Url.Query.Contains("queryinfo") && QueryTraceHttpContextAppender.Current != null)
            {
                string storeName = _store.GetType().Name.Replace("Store", "");
                QueryTraceHttpContextAppender.Current.AppendDirect(String.Format("{0}: [Key] {1}", storeName, key));
            }
        }
    }
    return (T)obj;
}

更多extensive code snippet here


结论

事实证明这个奇怪的电话确实是由New Relic Agent引起的。案例中的方法都是仪表化方法。但是,在禁用New Relic的情况下,性能问题并没有消失。尽管如此,堆栈信息更丰富。所以问题回答了。 @jessehouwing,如果你把你的评论重新表述为答案,我很乐意接受。

1 个答案:

答案 0 :(得分:1)

我找到了这个解释

“线程使用本地存储机制来存储特定于线程的数据。公共语言运行库在创建时为每个进程分配一个多槽数据存储阵列。线程可以在数据存储中分配一个数据槽,在插槽中存储和检索数据值,并在线程到期后释放插槽以供重用。每个线程的数据插槽是唯一的。没有其他线程(甚至子线程)都无法获取该数据。

如果指定的插槽不存在,则分配新的插槽。命名数据槽是公共的,任何人都可以操纵。 “

来源是:http://msdn.microsoft.com/en-us/library/system.threading.thread.getnameddataslot%28v=vs.110%29.aspx

在本文中解释了可以重写此行为

http://msdn.microsoft.com/en-us/library/6sby1byh%28v=vs.110%29.aspx

我认为问题是.net正在重复使用相同的名称来保存插槽,这会导致性能不佳。

是可行的,而不是一个解决方案是清理运行时可调用包装器。

我发现了这个:

http://msdn.microsoft.com/en-us/library/system.threading.thread.disablecomobjecteagercleanup%28v=vs.100%29.aspx