Request/Reply vs. Pub/Sub
I am having an interesting discussion in email with Evan Hoff, about the benefits of pub/sub architecture vs. request/reply. I want to state upfront that I see a lot of value in async one way messaging for a lot of the interactions in an application and across services. However, I am still skeptic about the push for pub/sub all the way.
To me, it seems like the request/reply model is a very natural one in many common scenarios. In most backend processing systems, I would tend to use pub/sub with sagas as both the easiest and most scalable solution. But in almost all cases that end up in the UI, this just doesn't make sense. Even if I want to fire several requests and then wait for all their reply, the model is still more natural than a pub/sub approach.
Note that the following code sample use imaginary API.
Let us go back to the displaying the available candidates. Using the request/reply model, I would have:
public void AvailableCandidates()
{ var specifications = BuildSpecifications(); var msg = bus.Process(new FindAvailableCandidateMessage(specifications)); DisplayResults(msg.Results); }
All of those are using the request / reply model.
The pub/sub model will look somewhat like this:
public void AvailableCandidates() { var specifications = BuildSpecification(); bus.Publish(new FindAvailableCandidatesMessage(specifications)) .WaitForCallback(delegate(AvailableCandidatesMessage msg) { DisplayResults(msg.Results); }); }
Or probably something like:
public void AvailableCandidates() { var specifications = BuildSpecification(); Guid correlationId = Guid.NewGuid(); ManualResetEvent waitForReply = new ManualResetEvent(false); bus.SubscribeOnce<AvailableCandidatesMessage>(correlationId, delegate(AvailableCandidatesMessage msg) { DisplayResults(msg.Results); waitForReply.Set(); }); bus.Publish(new FindAvailableCandidatesMessage(correlationId, specifications)); waitForReply.WaitOne(); }
Notice that I am not saying anything about the way those are handled once they leave the client. In fact, some of the reasons that were mentioned to favor the pub/sub model is the flexibility that you get when adding more functionality. Such as adding auditing, or redirecting messages to different services based on the type of candidate, etc.
The problem is that I see not inherent need for pub/sub to make this work. In fact, I think that anything that publish a message and than waits for the callback message is a big API issue, because this is request/reply, but with harder to use API. Even if you are doing the wait in async manner, I still think that this is a bad idea.
In the context of a request/reply interaction, we can handle everything the way we would handle any other message:
In fact, from the perspective of the infrastructure, there is little need to distinguish those. A message going out will go out either via a request/reply channel or a one way output channel, with a reply maybe arrived through a one way input channel. We don't really care about that.
From my point of view, it makes a lot more sense than to wait for a callback.
Thoughts?
Comments
Ayende, the thing that first comes to mind is the cost of a request / reply model in a lot of scenarios.
In your example of the request / reply sure that code is simplier, but it comes at a huge overhead if it has to be run over and over. Let's say you had a system that was suppose to "alert" end users when they had a new order with a ghost window similar to Outlook when new mail arrives.
If you use a request / reply model you are going to have to create a timer or something to run that same code over and over. Not only can that create more load on the system in general but it will create more network traffic for the request to be sent and the reply to come back.
In a true sub/pub model, there is a lot less overhead. One would setup a listener to "listen" for a particular event to happen. Only when that event did happen would data traverse over the wire. This is a lot less overhead all the way around.
The request / reply model fails completely when it has to be used in a polling situation whereby a client has to ask repeatedly, any data yet? Any data yet? Any data yet? The flip side of pub/sub is "tell me when you have something I care about, otherwise, leave me alone.".
Maybe I missed your point completely but that's what comes to mind.
I think you missed the point, because I am not saying that pub/sub is not useful in UI scenarios.
I am saying that in almost any "get data" scenario (and getting updates to data is a different thing), you want request/reply.
Is there a big push for pub/sub ONLY message exchange patterns to be used in a system? To me that seems pretty unnatural as well, even a little crazy - if you want to make a simple request of one and only one service (even if your MOM chooses the specific service using some kind of routing) and the response doesn't represent some business event that is potentially interesting to the system at large but just some set of data you need at the moment I can't see how pub/sub would add any benefit and would only add noise and network traffic. However I also don't see any reason to block your thread or avoid a callback. You're probably going to make your remote call on a thread other than the UI anyway so as to leave the UI responsive if it takes some time to complete so instead of spawning a background thread that blocks while waiting for a synchronous remote call to return, only to ultimately use a callback to update the UI anyway, why not use the bus as your background thread and then you can keep using a single queue based transport for everything, instead of one kind of transport that natively supports synchronous request/response for req/resp calls and a queue based transport for other types of calls.
Request/reply are not necessarily synchronous.
I am not talking about the infrastructure, I am talking about the interface that you use
Let me be clear in saying that I'm not pushing for pub/sub all the way--just avoiding request/response for every scenario possible. I'll get a response written after my UG meeting tonight.
"...with a reply maybe arrived through a one way input channel. We don't really care about that."
Sometimes we do care about reply from the infrastructure. If you use MS Exchange, you should be familiar with the message "This message has not yet been delivered. Microsoft Exchange will continue to try delivering the message on your behalf." normally coming when you aready got a call from your party complaining that you din't send what you promised.
No, that is handled by a timeout on the reply, which you should somehow handle.
Timeout is a Fault, I can handle it. But my example was a notification that infrastructure is working ok, just didn't deliver message yet.
Isn't pub/sub VS request/response an infrastructure issue? Or at least an architectural one. It sounds like your talking about Event Driven Architecture. Isn't the question - should a messaging system consist entirely of pub/sub channels, or is it also appropriate to have request/response channels for certain types of calls? If the discussion is purely about coding style, (wrap the async call with a sync api or use a callback) it probably doesn't make a bit of difference in the end and isn't really about pub/sub or request/response. If it's more than an API /coding style discussion though I simply agree with you, I think, in that for certain types of communication eventing-only will make the overall system rather more complicated that necessary regardless of how you design the endpoint API. I haven't spent too much time studying EDA concepts but if EDA enthusiasts promote only pub/sub channels and nothing but it seems extreme to me, too.
Alex,
If this is an option, you wouldn't use request/reply. You would use fire & forget (pub/sub).
As I understand the difference between pub/sub and request/response. In request/responce you know that response will come (maybe in the form of fault). In pub/sub, you have no idea if event you subscribed to will ever fire.
@Ayende
Yes, I don't need response from the service but I need response from infrastructure. It can be an event if we will consider infrastructure as a service, but I wouldn't like it.
Nathan,
Pub/Sub & Req/Resp are both code issues and architectural issues.
There seem to be a push to model everything using pub/sub, and I think that for a lot of scenarios, this is the wrong approach.
Async call vs. sync call is a different matter, you can do req/resp in async manner, but it is still different than the idea of publishing a message and getting a callback.
Alex,
Yes, that is a major difference between the two.
Alex,
Getting a response from the infrastructure is problematic. Because most often, the channel you are using will have the send & forget semantics.
You can ask for an ACK from the receiving side, but that is strange.
For most web applications, I see two models being useful, but the majority would be request/reply.
If a controller needs data to update the view, most of the time that is a synchronous operation. So the controller can issue a request and wait for the reply (using a correlation id scheme on a common response queue). By using Castle, you can stuff the listener into the container and share it amongst all instances of the controller.
You can also make your Request method return an IAsyncResult, having the queue reader set your async wait event when the reply arrives. Standard timeout stuff here works, and you can scale this within ASP.NET since your async waits go outside the regular thread pool.
For pub/sub, I see this as the async update portion of the system. You setup a long running (comet style) request from the browser to the web server, waiting on anything that happens to arrive. Once something arrives (at the subscriber), you respond to the browser and repost the call to the server. That way, as notifications arrives at the subscription endpoint, the response can be immediately returned to the browser. Good user experience.
You can also return faults from a remote service by capturing any exceptions, returning a fault message as the reply, and having your application handle the fault message by correlation id. This way, everything is nice and linked up to the original request.
Just my thoughts, but that's how we've tried to do things in our work-in-progress messaging library MassTransit.
User Interfaces do not really weigh into a discussion on SOA. UIs sit behind the service boundary. They are an implementation detail of the service. So all discussions as to whether we use asynchronous or synchronous messaging or whether we use publish/subscribe or not at the UI is not an SOA discussion.
There is no issue exposing a CRUD interface for use by a smart client application inside the service boundary. The exposed interface is a private endpoint, that doesn't appear on the public service contract. This is described in detail here:
http://bill-poole.blogspot.com/2008/03/services-and-user-interfaces.html
As far as asynchronous request-reply vs. publish-subscribe, request-reply makes use of command messages, which introduce coupling between services. For a full explanation of why we prefer publish-subscribe over command messages in general, read the following blog entries:
http://bill-poole.blogspot.com/2008/04/avoid-command-messages.html
http://bill-poole.blogspot.com/2008/04/soa-and-reuse.html
http://bill-poole.blogspot.com/2008/04/when-event-messages-are-not-ideal.html
Looking at your diagram, I'd say you've adopted my position about how the messages will be exchanged.
We haven't talked about the api via email, so I'm not sure where that came from.
How is your proposed solution different from what I proposed in email? The api design is important, but the message exchange pattern is even more important. How is your proposed message exchange pattern different from what I proposed?
I don't care about the message exchange pattern, that is infrastructure for this point of view.
What I do care about is how I use it, and what the semantics that the code is exposed to
The difficulty you appear to be having is that you're trying to change the underlying communications style for the same overall design and user interaction.
For a smart client scenario, consider the following:
Let's rephrase the requirement that the user wants to see the list of available candidates for a position, but that that list should be updated to show the current status at all times. In other words, if a new candidate joins the system 10 seconds after the user has received the previous results, the list should update "itself" to show that new candidate without requiring any action on the part of the user.
Now, I'm sure that you'll see the necessity of using pub/sub.
Not only that, but I think that you'll agree that the overall user experience is much improved.
I would argue that the Request/Response model allows us to achieve a greater level of parallelism far easier then the publish subscribe model which often depends on a single thread in order to publish messages.
That said I do believe there is a place for pub/sub in the UI model. Look at the fincacial markets and stock trackers for a prime example of a UI that 100% depends on pub/sub. I dont think you could achieve this using request response without exponentionally increasing requests.
I disagree, because this requires the system to start maintaining a lot of extra state.
Let us say I want candidates over 18. The system now needs to check all new data for this and publish it.
If I needed this for a short scan, this is extra work that the system needs to do.
From my readings into Udi/Bill's work, the system doesn't do any extra work.
It always publishes a message when a candidate is modified/updated. It is your particular client that receives the update message and discards it if the candidate is not over 18 or takes the appropriate action otherwise.
That is my limited understanding.
You can't handle this scenario when you have a lot of listening clients.
Just the update events would saturate the network. And most of them would be ignored.
@ayende
Regarding network saturation:
Couldn't you just use [insert your favourite ESB here]: s ability to filter subscriptions based on data?
In this case: bus.Subscribe("Candidate updates where age > 18") would cause the ESB to filter the events and thereby not saturate the network with events that should be discarded by the clients anyway
@ Ayende
The performance of your publish-subscribe messaging depends on your infrastructure. Some solutions map a pub-sub topic to a multicast IP address, so this makes the number of subscribers less important.
Also, you would be surprised what kind of load these messaging systems can handle. You'd have to be talking some very serious volume before you had a problem.
I've been using this approach now for a long time and haven't yet come up against any message volume issues.
pub/sub or event-based system is more generic based on one way message exchange pattern.
request/reply can be composed with one way message exchange pattern. it's client's behavior to subscribe once and to wait for the response.
Look at Event Broker in Composite UI (CAB), it's every useful for cross module communication/interaction in a single composite application which is based on event pub/sub.
inside single module, event bubbling/tunneling, and request/reply are suitable.
Cross module/application/system, event based (pub/sub) is more suitable.
My speculation:
Event Broker for cross module inside an application,
NServiceBus for cross application inside a system,
ESB for cross systems.
Is there anyway this requirement can be done for an aspx page
The requirement is that the user wants to see the list of available candidates for a position, but that that list should be updated to show the current status at all times. In other words, if a new candidate joins the system 10 seconds after the user has received the previous results, the list should update "itself" to show that new candidate without requiring any action on the part of the user.
Alex,
You have to hit the server for that.
A background updater using ajax would do the trick
Comment preview