【问题标题】:MVC5 hangs on MapSignalR when reconnecting after AppPool cycles在 AppPool 循环后重新连接时,MVC5 在 MapSignalR 上挂起
【发布时间】:2015-07-28 12:10:27
【问题描述】:

我的 Startup.SignalR.cs 中有以下代码:

using Microsoft.AspNet.SignalR;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Admin
{
    public partial class Startup
    {
        public void ConfigureSignalR(IAppBuilder app)
        {
            var Config = new HubConfiguration()
            {
                EnableDetailedErrors = false,
                Resolver = new DefaultDependencyResolver()
            };

#if DEBUG
            Config.EnableDetailedErrors = true;
#endif
            // Any connection or hub wire up and configuration should go here
            app.MapSignalR();

            GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(600);
        }
    }
}

在应用程序的初始加载期间,一切正常,但是当线程被允许休眠(大约 10 分钟)时,AppPool 被回收并且线程卡在这一行:

app.MapSignalR();

IDE 卡在绿色箭头和This is the next statement to execute when this thread returns from the current function. 上。但是我不知道在这种情况下current function 是什么。

尝试检查任何局部变量会导致 Cannot evaluate expression because a native frame is on top of the call stack. 但调用堆栈显示:Admin.dll!Admin.Startup.ConfigureSignalR(Owin.IAppBuilder app) Line 25 作为顶部框架...

代码永远不会从此状态恢复,必须通过重新启动调试会话来完全重新启动 AppPool。

有人对这种情况有什么解释吗?

附加信息: 启用 Debug => Windows => 并行堆栈后,我看到了更详细的堆栈跟踪:

[Managed to Native Transition]  
mscorlib.dll!Microsoft.Win32.RegistryKey.OpenSubKey(string name, bool writable)
mscorlib.dll!Microsoft.Win32.RegistryKey.OpenSubKey(string name)
System.dll!System.Diagnostics.PerformanceCounterLib.FindCustomCategory(string category, out System.Diagnostics.PerformanceCounterCategoryType categoryType)
System.dll!System.Diagnostics.PerformanceCounterLib.IsCustomCategory(string machine, string category)
System.dll!System.Diagnostics.PerformanceCounter.InitializeImpl()
System.dll!System.Diagnostics.PerformanceCounter.PerformanceCounter(string categoryName, string counterName, string instanceName, bool readOnly)
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Infrastructure.PerformanceCounterManager.LoadCounter(string categoryName, string counterName, string instanceName, bool isReadOnly)
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Infrastructure.PerformanceCounterManager.LoadCounter(string categoryName, string counterName, bool isReadOnly)
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Infrastructure.PerformanceCounterManager.SetCounterProperties()
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Infrastructure.PerformanceCounterManager.Initialize(string instanceName, System.Threading.CancellationToken hostShutdownToken)
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hosting.HostDependencyResolverExtensions.InitializePerformanceCounters(Microsoft.AspNet.SignalR.IDependencyResolver resolver, string instanceName, System.Threading.CancellationToken hostShutdownToken)
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hosting.HostDependencyResolverExtensions.InitializeHost(Microsoft.AspNet.SignalR.IDependencyResolver resolver, string instanceName, System.Threading.CancellationToken hostShutdownToken)
Microsoft.AspNet.SignalR.Core.dll!Owin.OwinExtensions.UseSignalRMiddleware<Microsoft.AspNet.SignalR.Owin.Middleware.HubDispatcherMiddleware>(Owin.IAppBuilder builder, object[] args)
Microsoft.AspNet.SignalR.Core.dll!Owin.OwinExtensions.RunSignalR(Owin.IAppBuilder builder, Microsoft.AspNet.SignalR.HubConfiguration configuration)
Microsoft.AspNet.SignalR.Core.dll!Owin.OwinExtensions.MapSignalR.AnonymousMethod__0(Owin.IAppBuilder subApp)
Microsoft.Owin.dll!Owin.MapExtensions.Map(Owin.IAppBuilder app, Microsoft.Owin.PathString pathMatch, System.Action<Owin.IAppBuilder> configuration)
Microsoft.Owin.dll!Owin.MapExtensions.Map(Owin.IAppBuilder app, string pathMatch, System.Action<Owin.IAppBuilder> configuration)
Microsoft.AspNet.SignalR.Core.dll!Owin.OwinExtensions.MapSignalR(Owin.IAppBuilder builder, string path, Microsoft.AspNet.SignalR.HubConfiguration configuration)
Microsoft.AspNet.SignalR.Core.dll!Owin.OwinExtensions.MapSignalR(Owin.IAppBuilder builder, Microsoft.AspNet.SignalR.HubConfiguration configuration)
Microsoft.AspNet.SignalR.Core.dll!Owin.OwinExtensions.MapSignalR(Owin.IAppBuilder builder)
Admin.dll!Admin.Startup.ConfigureSignalR(Owin.IAppBuilder app) Line 25

【问题讨论】:

标签: c# asp.net asp.net-mvc asp.net-mvc-5 signalr


【解决方案1】:

另一个解决方法,来自这篇博文: http://www.zpqrtbnk.net/posts/appdomains-threads-cultureinfos-and-paracetamol

app.SanitizeThreadCulture();

public static void SanitizeThreadCulture(this IAppBuilder app)
{
        var currentCulture = CultureInfo.CurrentCulture;

        // at the top of any culture should be the invariant culture,
        // find it doing an .Equals comparison ensure that we will
        // find it and not loop endlessly
        var invariantCulture = currentCulture;
        while (invariantCulture.Equals(CultureInfo.InvariantCulture) == false)
            invariantCulture = invariantCulture.Parent;

        if (ReferenceEquals(invariantCulture, CultureInfo.InvariantCulture))
            return;

        var thread = Thread.CurrentThread;
        thread.CurrentCulture = CultureInfo.GetCultureInfo(thread.CurrentCulture.Name);
        thread.CurrentUICulture = CultureInfo.GetCultureInfo(thread.CurrentUICulture.Name);
}

【讨论】:

    【解决方案2】:

    正如 Pawel 所提到的,我能够使用此处记录的虚拟性能计数器解决我的问题:https://github.com/SignalR/SignalR/issues/3414

    // Global.asmx
    var tempCounterManager = new TempPerformanceCounterManager();
    GlobalHost.DependencyResolver.Register(typeof (IPerformanceCounterManager), () => tempCounterManager);
    
    [....]
    
    // Helper Class
    public class TempPerformanceCounterManager : IPerformanceCounterManager
    {
        private readonly static PropertyInfo[] _counterProperties = GetCounterPropertyInfo();
        private readonly static IPerformanceCounter _noOpCounter = new NoOpPerformanceCounter();
    
        public TempPerformanceCounterManager()
        {
            foreach (var property in _counterProperties)
            {
                property.SetValue(this, new NoOpPerformanceCounter(), null);
            }
        }
    
        public void Initialize(string instanceName, CancellationToken hostShutdownToken)
        {
        }
    
        public IPerformanceCounter LoadCounter(string categoryName, string counterName, string instanceName, bool isReadOnly)
        {
            return _noOpCounter;
        }
    
        internal static PropertyInfo[] GetCounterPropertyInfo()
        {
            return typeof(TempPerformanceCounterManager)
                .GetProperties()
                .Where(p => p.PropertyType == typeof(IPerformanceCounter))
                .ToArray();
        }
        public IPerformanceCounter ConnectionsConnected { get; set; }
        public IPerformanceCounter ConnectionsReconnected { get; set; }
        public IPerformanceCounter ConnectionsDisconnected { get; set; }
        public IPerformanceCounter ConnectionsCurrentForeverFrame { get; private set; }
        public IPerformanceCounter ConnectionsCurrentLongPolling { get; private set; }
        public IPerformanceCounter ConnectionsCurrentServerSentEvents { get; private set; }
        public IPerformanceCounter ConnectionsCurrentWebSockets { get; private set; }
        public IPerformanceCounter ConnectionsCurrent { get; private set; }
        public IPerformanceCounter ConnectionMessagesReceivedTotal { get; private set; }
        public IPerformanceCounter ConnectionMessagesSentTotal { get; private set; }
        public IPerformanceCounter ConnectionMessagesReceivedPerSec { get; private set; }
        public IPerformanceCounter ConnectionMessagesSentPerSec { get; private set; }
        public IPerformanceCounter MessageBusMessagesReceivedTotal { get; private set; }
        public IPerformanceCounter MessageBusMessagesReceivedPerSec { get; private set; }
        public IPerformanceCounter ScaleoutMessageBusMessagesReceivedPerSec { get; private set; }
        public IPerformanceCounter MessageBusMessagesPublishedTotal { get; private set; }
        public IPerformanceCounter MessageBusMessagesPublishedPerSec { get; private set; }
        public IPerformanceCounter MessageBusSubscribersCurrent { get; private set; }
        public IPerformanceCounter MessageBusSubscribersTotal { get; private set; }
        public IPerformanceCounter MessageBusSubscribersPerSec { get; private set; }
        public IPerformanceCounter MessageBusAllocatedWorkers { get; private set; }
        public IPerformanceCounter MessageBusBusyWorkers { get; private set; }
        public IPerformanceCounter MessageBusTopicsCurrent { get; private set; }
        public IPerformanceCounter ErrorsAllTotal { get; private set; }
        public IPerformanceCounter ErrorsAllPerSec { get; private set; }
        public IPerformanceCounter ErrorsHubResolutionTotal { get; private set; }
        public IPerformanceCounter ErrorsHubResolutionPerSec { get; private set; }
        public IPerformanceCounter ErrorsHubInvocationTotal { get; private set; }
        public IPerformanceCounter ErrorsHubInvocationPerSec { get; private set; }
        public IPerformanceCounter ErrorsTransportTotal { get; private set; }
        public IPerformanceCounter ErrorsTransportPerSec { get; private set; }
        public IPerformanceCounter ScaleoutStreamCountTotal { get; private set; }
        public IPerformanceCounter ScaleoutStreamCountOpen { get; private set; }
        public IPerformanceCounter ScaleoutStreamCountBuffering { get; private set; }
        public IPerformanceCounter ScaleoutErrorsTotal { get; private set; }
        public IPerformanceCounter ScaleoutErrorsPerSec { get; private set; }
        public IPerformanceCounter ScaleoutSendQueueLength { get; private set; }
    }
    
    internal class NoOpPerformanceCounter : IPerformanceCounter
    {
        public string CounterName
        {
            get
            {
                return GetType().Name;
            }
        }
        public long Decrement()
        {
            return 0;
        }
        public long Increment()
        {
            return 0;
        }
        public long IncrementBy(long value)
        {
            return 0;
        }
        public long RawValue
        {
            get { return 0; }
            set { }
        }
        public void Close()
        {
        }
        public void RemoveInstance()
        {
        }
        public CounterSample NextSample()
        {
            return CounterSample.Empty;
        }
    }
    

    【讨论】:

    【解决方案3】:

    我在这里找到了解决方案:github.com/SignalR/SignalR/issues/3414 截图示例:

    public void Configuration(IAppBuilder app)
    {
        var task = Task.Run(() => app.MapSignalR());
        task.Wait(300);
        //try again if it fails just to be sure ;)
        if (task.IsCanceled) Task.Run(() => app.MapSignalR()).Wait(300);
    }
    

    【讨论】:

    • 我最初尝试过这种方法,但它没有帮助,我在第一个 Task.Run() 期间仍然挂起,即使它有 300 毫秒的等待超时。
    猜你喜欢
    • 1970-01-01
    • 2018-01-25
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    相关资源
    最近更新 更多