Closed dasjestyr closed 6 years ago
The only way to get access to a channel created by the transport would be to create your own IRoutingTopology
implementation.
What are you trying to do with topic exchanges?
A number of things. In short, the fanout strategy used in the conventional topology is fairly limited in terms of routing.
It is somewhat hard to explain, but I tried to do so here: http://www.provausio.com/2018/09/21/bringing-rabbitmq-topics-to-nservicebus/
The blog there describes the situation that led me down the path of forking the repo and attempting a topic implementation. The actual scenario I'm dealing with now is piping progress back to a SignalR message hub so that the frontend client can provide feedback to the user. The problem there is I would have to subscribe to an unknown number of events for them to be picked up and forwarded to the client application. With topics, if every service had an event topic of it's own, I could gather them all into a single pipe by binding them to a singular topic to which my message hub could subscribe.
I'm not sure I fully understand the scenario, however wouldn't inheritance solve the problem here?
If all the events that could be pushed to clients via the SignalR Hub implement an interface such as ICanBeForwardedToUserInterface
you could have a message handler for a single type and broadcast/send them through the SignalR Hub.
@dasjestyr Reading through your article, I agree with Udi's comment there, I don't see how that approach reduces logical coupling.
Another thing to keep in mind is that NServiceBus has some specific requirements that a new routing topology would need to fulfill around handling event types that have an inheritance hierarchy or implement multiple interfaces. In the past when I've looked at topic exchanges, I wasn't able to come up with a design that would be able to handle all of that effectively.
Regardless, if the routing topology you're building works for you, and you're not using events that require inheritance or interfaces, then there is no need to fork the repo to use it. You can call the UseRoutingTopology
API to have your endpoints use it.
@mauroservienti I hadn't thought of that, so I will chew on that for a bit. I like the idea; will the transport currently create exchanges for all interfaces on an event like that?
@bording I replied to Udi's comment just now. It's a missing implementation detail. The extraction service gets the provider name from a configuration record in a different system. It doesn't know what it means; it's just passed along as a text value. If I were to use topics, it would just write that provider name into the routing key instead of passing it in the message payload itself so that the message would only be delivered to a service that was subscribed/bound with that in the routing key pattern.
I also had considered some other things with the topic topology with regards to how Sagas would even work since events would then essentially be inside of topics instead of in different exchanges. I'm somewhat aware of the implications of implementing something like topics with NSB and I know you hall had probably been through this already, which is why I hadn't yet brought it up. I do have some ideas, however.
@mauroservienti I hadn't thought of that, so I will chew on that for a bit. I like the idea; will the transport currently create exchanges for all interfaces on an event like that?
The direct routing topology has some limitations around that, which is one of the reasons we recommend using the conventional routing topology.
Take a look at https://github.com/Particular/NServiceBus.RabbitMQ/blob/develop/src/NServiceBus.Transport.RabbitMQ/Routing/ConventionalRoutingTopology.cs#L111-L117 if you want to see how the conventional routing topology handles interfaces.
Ah, so it does. Ok, I will explore this. I do really think the topic topology should be revisited though, despite its trickiness. It is powerful and quite popular. I'm still playing with my implementation. If I ever get to a point where I think it's worthy, I'll document it and bring it back up. Please feel free to share any of these requirements for me to think about while I'm giving this a shot :)
@mauroservienti I got the messages to all pipe through to the SignalR hub by subscribing to IUpdateUI, but it throws if I don't have the original event classes in that project:
NServiceBus.MessageDeserializationException: An error occurred while attempting to extract logical messages from incoming physical message 45afc8f5-6739-4f6f-9afd-a96d015f5f97 ---> System.Exception: Could not find metadata for 'Newtonsoft.Json.Linq.JObject'. Ensure the following:
- 'Newtonsoft.Json.Linq.JObject' is included in initial scanning.
- 'Newtonsoft.Json.Linq.JObject' implements either 'IMessage', 'IEvent' or 'ICommand' or alternatively, if you don't want to implement an interface, you can use 'Unobtrusive Mode'.
The interfaces are certainly present on the sender side, but the classes are non-existent on the receiver side. For other services, we actually share the messaging libraries to solve this problem, but I would rather not have to update this service every time we have to add a new event.
Is there a way manually associate event types to a class in the receiving project? For example, the Foo
event fired by Service A gets received in Service B, and service B deserializes Foo
into Bar
. Or if I can tell NSB to deserialize all messages in this particular service as dynamic
. Since I'm just forwarding messages over SignalR to a ReactJS application, I don't need any real type safety here. Or even add Newtonsoft.Json.Linq.JObject ???
I'm open to other suggestions as well.
Is this what I'm looking for? https://docs.particular.net/samples/serializers/change-message-type/
@dasjestyr correct, by default when a message arrives it is desierialized and thus the .NET type has to be present.
Yes, one option is to change the type you deserilize to, the type obviously needs to implement IUpdateUI
. The first thing that comes to my mind is to inherit from JObject
(or if sealed from DynamincObject
) and implement IUpdateUI
as well. At this point the change message type approach should work.
I'm getting some intermittent file load errors with the mutator approach, but the inheriting from JObject in the receiver seems to work great, thanks!
If you're interested in the file load exception, I have a repro here: https://github.com/dasjestyr/MutatorFileLoadProblem
It crashes immediately when Type.GetType() is called in the mutator. Not quite sure how that's possible. If it looks worth a look, let me know and I can open a separate issue for it.
Oh for what it's worth, I just noticed that CloudAMQP had this notice on my account which coincidentally aligns with my concern. We're green right now, but won't be in the near future when more events are introduced.
Oh sorry, I got so caught up with solving one specific problem that I lost sight of the original issue post. I would still like a way to get access to IChannel. The overarching problem is that NSB is a .Net solution and we have grown beyond being a strictly .Net shop. I had been interested in learning more about what NSB offers to help us bridge the messaging gap, and that has gone fairly well, but topology is still a bit of a blocker right now.
We have a polyglot microservice platform (.net, node, go) and we also have applications that were acquired from purchasing other companies, and they use topics. Even aside from that fact, we do have some cases where we'd prefer to use topics and right now, NSB is beginning to get in the way. For our .Net components, I like NSB and rather than remove NSB from our solution to fit the bigger scheme, I'd prefer to have the option of using a topic topology, but since that is unavailable, it would at least help to not have to create a separate connection to RMQ in order to at least publish to topics. I think by at least exposing some of the RMQ connection objects such as IChannel, that could help make that happen.
@dasjestyr You are free to create a topic-based topology that works for your scenarios with the public API we already provide.
I don't foresee us opening up access to the connections/channels we create internally, though. There's too much instability that could be introduced by allowing arbitrary use of the connections and channels we create.
Got it.
Alternative to the discuss forum, do you guys have a gitter or something for live convo? If I decide to roll my own topology, it could be helpful to have a forum for quick arbitrary questions as I'm developing. I'd like to keep as close to any Particular.net design philosophies as I can.
@dasjestyr We do have https://gitter.im/Particular/NServiceBus
Perfect. Thank you sir
I need to add topic exchange support in the short term, and I'll likely have to do this as a separate implementation to run along side NSB. Rather than create another connection in the same application, can I gain access to the IChannel created by this transport so that I can piggyback off of that?