I’m currently using ServiceStack.NET SSE to implement always-connected, agent-like Windows service. The client (agent) needs to return data based on event request from the server (hub).
Currently, SSE implementation does not provide a way to return data from the client in a RPC-like manner.
So I had to implement my own way of doing it using SSE event for requests and REST call for responses.
It works. However, my implementation requires me to wrap each specific request and response data into two new separate messages that contains an event ID that I can access from my code.
This approach breaks very nice messaging architecture that ServiceStack provides based on “Request : IReturn(Response)” pattern. In additional it adds a lot of meaningless message wrapper classes to otherwise an elegant design.
SSE implementation already provides an event ID in the event (request) message, but it is not accessible from my (external) code. In addition SSE implementation does not provide any generic REST handler for handling response data for that event ID.
Would this kind of functionality interest other framework users? If yes, would it be possible to add this to the framework? I’m willing to help if needed.
Something like this API would work:
public class MyResponse
{}
public class MyRequest : IReturn<MyResponse>
{}
public class MyWorker
{
public IServerEvents ServerEvents { get; set; }
public async Task DoStuffAsync()
{
var request = new MyRequest();
var response = await ServerEvents.PostToUserIdAsync(userId, selector, request)
. . .
}
}
ServiceStack’s goal is to maintain light, DRY APIs that map 1:1 over the domain it’s abstracting, we definitely don’t want to try abstract multiple async operations over different communication channels behind an opinionated Synchronous API requiring multiple moving parts. It would be too fragile and would encourage programming against a false API that’s contra to its async one-way flow impl of how it works behind-the-scenes.
We should look at providing the primitives which you can use to build your prescriptive solution on top. Currently the id of the message is incrementally maintained individually for each subscription, so there isn’t really an Id we could return, as each notification can be sent to multiple subscribers, each which would have a different Id. We could return a dictionary that maps subscription to message id, but I’m not sure how useful that would be? We’d need to see a valid use-case where this would be useful as creating a dictionary everytime a notification is sent adds unnecessary overhead given that most times it would be ignored. But we could return the message id in NotifySubscrption() as that would only be sent to 0 or 1 subscription.
IMO it would just be better if you just added your own Correlation Id to the Server Event Message that’s sent where you want to be able to correlate a response back.
It’s a fair statement. This clearly does not map to the “fire-and-forget” event pattern.
I know (from web search) that other people are looking for similar solutions in ServiceStack and SignalR frameworks, so I thought that this functionality may be quite useful if implemented within a framework itself.
It seems to me that the easiest way to provide access to the message ID is in a hook method similar to EventSubscription.OnPublish that is exposed as ServerEventsFeature.OnPublish() (see ServiceStack code below).
public class EventSubscription : SubscriptionInfo, IEventSubscription
{
private long msgId;
public void Publish(string selector, string message)
{
var msg = message ?? "";
var frame = "id: " + Interlocked.Increment(ref msgId) + "\n"
+ "data: " + selector + " " + msg + "\n\n";
PublishRaw(frame);
}
public void PublishRaw(string frame)
{
WriteEvent(response, frame);
OnPublish?.Invoke(response, frame);
}
}
I can use existing OnPublish() hook to parse the message ID out. However, having a BeforePublish(id, selector, message) hook or something similar that exposes the ID directly, may provide a cleaner way to get the ID without parsing the frame string.
@mythz, sorry for this question but I can’t find any answer for it online. Is there a way to download nightly build? I don’t have VS2017 so I can’t build the source that uses new project format.
@smatsevilo I am doing the exact same thing as you, however I am basing DTOs to the IReceiver on a AckRequiredCmd
which contains a CorrelationId, this Id is a Guid that is generated before being passed to ServerEvents.NotifySubscription()
The response comes in to a /sse/ack endpoint, with a DTO containing the CorrelationId and a Data object.
I feel this doesn’t feel like too bad a trade off, especially when compared to how well SSE works compared to SignalR in these types of integrations. I felt that SignalR just felt a little too cumbersome compared to SSE, plus the added benefit is that the service sending my NotifySubscription is actually in another process, not my actual API service instance, it just spins up a ServerEvents object connected to the shared Redis and it all works very well.