使用SqlScaleoutConfiguration的SignalR通知为Rebus提供随机异常.Transport.TransactionContext不可序列化

时间:2018-01-17 14:05:20

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

我用: SqlScaleoutConfiguration中的SignalR 2.2.2 Rebus 3.0.1

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

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

#include "Box.h"
int main() {
    Box b(42);
    Box2 b2;
    b2.PrintBox(b);
    return 0;
}

Rebus队列中的消息结果正确处理。

处理程序是这样的:

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()

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

我类似的问题是:

但我认为情况并非如此。

2 个答案:

答案 0 :(得分:1)

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

正如您所包含的链接中所述,Rebus在处理消息时以这种方式存储“环境事务”,允许所有自己的操作在同一工作单元中登记。

可以使用here解释的方法,其中以这样安全的方式临时删除交易上下文

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

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

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

我认为,如果我们想知道究竟发生了什么,那么对SignalR有很多了解的人就需要插手。

答案 1 :(得分:0)

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

线索是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();
相关问题