【问题标题】:SignalR notification using SqlScaleoutConfiguration gives random exception for Rebus.Transport.TransactionContext not serializable使用 SqlScaleoutConfiguration 的 SignalR 通知为 Rebus.Transport.TransactionContext 提供随机异常不可序列化
【发布时间】:2018-01-17 14:05:20
【问题描述】:

我使用: SqlScaleoutConfiguration 中的 SignalR 2.2.2 Rebus 3.0.1

存储在 Rebus 中的一些事件由通知中心处理并使用 signalR 推送到客户端。

一切正常,但今天早上,发布新版本后,没有一个客户收到“新版本”消息,可能是因为以下异常:

10:39:04.586| |ERROR| |ProcessId=8196| |ThreadId=5| |SignalR.SqlMessageBus| |Stream 0 : Error starting SQL notification listener: System.Runtime.Serialization.SerializationException: Type 'Rebus.Transport.TransactionContext' in Assembly 'Rebus, Version=3.0.1.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

Server stack trace: 
   at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
   at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
   at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
   at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.SerializeMessageParts(ArrayList argsToSerialize)
   at System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage..ctor(IMethodCallMessage mcm)
   at System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage.SmuggleIfPossible(IMessage msg)
   at System.Runtime.Remoting.Channels.CrossAppDomainSink.SyncProcessMessage(IMessage reqMsg)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at System._AppDomain.CreateInstance(String assemblyName, String typeName)
   at System.Data.SqlClient.SqlDependency.CreateProcessDispatcher(_AppDomain masterDomain)
   at System.Data.SqlClient.SqlDependency.ObtainProcessDispatcher()
   at System.Data.SqlClient.SqlDependency.Start(String connectionString, String queue, Boolean useDefaults)
   at Microsoft.AspNet.SignalR.SqlServer.ObservableDbOperation.StartSqlDependencyListener()

Rebus 队列中的消息得到正确处理。

处理程序是这样的:

public async Task Handle(ApplicationVersionEvent message)
{
    await
        Clients.All.CheckApplicationVersion(new ApplicationCurrentVersionNotification
        {                    
            CurrentVersion = message.CurrentVersion
        });
}

通过重启解决了,但我需要了解发生了什么。

我类似的问题是:

但我认为情况并非如此。

【问题讨论】:

    标签: asp.net sql-server notifications signalr rebus


    【解决方案1】:

    除了你已经发现的之外,我真的很难告诉你这里发生了什么:SignalR 出于某种奇怪的原因 似乎想要序列化隐藏在当前执行上下文中的值,并且其中之一是 Rebus 当前的交易上下文。

    正如您在包含的链接中所解释的,Rebus 在处理消息时以这种方式存储“环境事务”,从而允许将其所有自己的操作纳入同一个工作单元。

    可以使用here 解释的方法,其中事务上下文以像这样的安全方式暂时删除

    public async Task Handle(SomeMessage message)
    {
        var transactionContext = AmbientTransactionContext.Current;
        AmbientTransactionContext.Current = null;
        try
        {
            JuggleWithAppDomainsInHere();
        }
        finally
        {
            AmbientTransactionContext.Current = transactionContext;
        }
    }
    

    可能将相关位分别移动到实现IDisposable 的类中的构造函数/Dispose 方法,从而使API 更流畅:

    using(new DismantleAmbientRebusStuff())
    {
        JuggleWithAppDomainsInHere();
    }
    

    我认为,如果我们要找出真正发生的事情,需要对 SignalR 有很多了解的人来插话。

    【讨论】:

    • 感谢您的回答。我将为通知中心处理程序实现一个装饰器,因为它可以发生在任何处理的消息中。无论如何,错误是随机的,幸运的是,它很少见,所以需要时间来判断答案是否解决了它。
    【解决方案2】:

    我忘记了这个问题,但稍后我通过解决方法解决了它。

    线索是 SqlMessageBus 在初始化时序列化上下文,这发生在第一次调用 GetHubContext 检索它时,所以我在执行任何命令之前强制其初始化。

        app.MapSignalR();
    
        var context = new OwinContext(app.Properties);
        var token = context.Get<CancellationToken>("host.OnAppDisposing");
        if (token != CancellationToken.None)
        {
            token.Register(() =>
            {
                log.Info("host.OnAppDisposing");
                // code to run when server shuts down
                BackendMessageBusConfig.DisposeContainers();
            });
        }
    
        // this code brings forward SignalR SqlMessageBus initialization 
        var forceInit = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
    
        BackendMessageBusConfig.Register();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-18
      • 2012-01-02
      • 1970-01-01
      • 2017-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多