【问题标题】:MassTransit: Message contracts, polymorphism and dynamic proxy objectsMassTransit:消息合约、多态性和动态代理对象
【发布时间】:2014-10-13 17:59:36
【问题描述】:

TL;DR 订阅合约时,如何获取原始消息内容或原始发布对象,而不是动态代理?

我在尝试创建基于 MassTransit 的模块化应用程序时遇到了麻烦。

我的想法是让 Websocket 服务器连接到队列,它从套接字读取事件并将其作为“连接请求”插入队列,并从队列中读取事件并将它们作为“连接事件”发送到套接字”。两者都有一个契约,允许 WS 服务器知道事件要连接到哪个连接,以及系统的其余部分来自哪里:

public interface IConnectionRequest
{
    String ConnectionId { get; set; }
}

public interface IConnectionEvent
{
    String ConnectionId { get; set; }
}

有一个对象维护会话和其他数据。此对象接受请求(如操作请求或订阅请求)并作为请求的结果或仅仅因为状态更改而推送事件。我想创建侦听特定事件或一组事件的对象,并对状态执行操作,所以我创建了这个合约:

public interface IConnectionRequestHandler<T> : Consumes<T>.Selected
    where T : class, IConnectionRequest
{
}

例如,我想创建一个处理程序,在服务器中创建实际会话,并在会话准备好时回复连接通知。我创建一个代表请求的对象,另一个代表事件和它自己的处理程序。

public class CreateSessionRequest : IConnectionRequest
{
    public String ConnectionId { get; set; }
}

public class CreatedSessionEvent : IConnectionEvent
{
    public String ConnectionId { get; set; }
    public Guid SessionId { get; set; }
}

public class CreateSessionEventHandler : IConnectionRequestHandler<CreateSessionRequest>
{
    IServiceBus _bus;

    public CreateSessionEventHandler(IServiceBus bus)
    {
        _bus = bus;
    }

    public bool Accept(CreateSessionRequest message)
    {
        return true;
    }

    public void Consume(CreateSessionRequest message)
    {
        // do stuff, create the session
        var evt = new CreatedSessionEvent() { SessionId =Guid.NewGuid(), ConnectionId = message.ConnectionId };
        _bus.Publish(evt, evt.GetType());
    }
}

现在出于测试目的,我创建了这个模拟场景的代码。基本上,它创建一个通信总线并订阅请求处理程序:

var bus = ServiceBusFactory.New(sbc =>
{
    sbc.ReceiveFrom("loopback://localhost/queue");
});

bus.SubscribeInstance<CreateSessionEventHandler>(new CreateSessionEventHandler(bus));

然后,模拟Websocket服务器,我编写从WS读取的部分,并将其发送到队列中:

IConnectionRequest e = new CreateSessionRequest() { ConnectionId = "myId" };
bus.Publish(e, e.GetType());

现在应该从队列中听到事件并将它们转发到适当的连接的部分:

bus.SubscribeHandler<IConnectionEvent>(evt => Console.WriteLine("Sending event '{0}' to connection: {1}",
                                                                evt.GetType().Name,  
                                                                evt.ConnectionId));

但是最后一部分没有按预期工作。我在订阅中得到的对象不是我的原始事件,它是一个动态代理DynamicImpl.IConnectionEvent,所以我不能在 JSON 中序列化这个对象,因为它只包含IConnectionEvent 的成员。

如果我在订阅中指定类型,它可以工作:

bus.SubscribeHandler<CreatedSessionEvent>(evt => Console.WriteLine("Sending event '{0}' to connection: {1}",
                                                                evt.GetType().FullName,  
                                                                evt.ConnectionId));

但这意味着对于每个新事件,我必须触摸 websocket 服务器来注册该新类型。

有没有办法避免这种情况?

【问题讨论】:

    标签: .net serialization masstransit dynamic-proxy event-driven-design


    【解决方案1】:

    TL;DR - MT 仅支持大多数序列化的合约数据。这有很多原因,但如果你想要非代理类型,那么你需要使用二进制序列化程序。

    所以 XML 和 JSON 序列化(XML 实际上只是在底层使用 JSON 序列化器,速度奇快)如果可以的话,会为所有请求生成一个代理。 subscribed 类型是您正在使用的合同,并且期望询问对象以获取合同中未包含的更多详细信息,这会导致大量复杂性。我们建议您避免这样做。

    因此,这意味着您需要访问 websocket 服务器来处理预期使用的每种类型的消息。如果您唯一要做的就是转发消息,那么有人做过的 SingalR 背板(https://github.com/mdevilliers/SignalR.RabbitMq 我认为是其中之一)可能比 MT 消费者更适合。

    【讨论】:

    • 对,二进制序列化器可以解决问题。老实说,我很难想象这将如何导致“大量复杂性”。可能这是我很快就会通过“痛苦之道”学到的东西,但是,你有什么例子可以让我开始吗?干杯。
    • 当然!因此,首先我们的合同更改变得脆弱,因为您在加载消费者后动态转换为类型。一开始你不会注意到这是一个问题,在消费者和错误叮咬中你会错过一些变化。如果你真的只有两个消费者(我已经看到了),这没什么大不了的,但仍然可能是个问题。接下来,如果在某些时候您必须出于负载或其他原因拆分 Consumer,您会注意到您现在可能需要克服一些耦合。而且我的空间快用完了。
    • 但是有几个例子可能会在以后咬你。如果您的应用程序从未达到一定的复杂程度或随着时间的推移没有太大变化,您可能永远不会发现它们。不过,即使知道这一点,有时也值得做出取舍——不管我告诉你什么。
    猜你喜欢
    • 2012-06-10
    • 2010-10-03
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多