Open RedwoodForest opened 1 year ago
If it's not supported, then I'd like support to be added for returning streaming responses from the client to the server.
Can you outline the scenario? We've discussed it but the implementation is tricky so motivating examples would help.
@RedwoodForest SignalR has Streams https://learn.microsoft.com/en-us/aspnet/core/signalr/streaming?view=aspnetcore-7.0 probably this is what you looking for
@KSemenenko this is about server to client streaming (which we don't support), initiated by the server.
Now I get it, I thought for a while that these two-way streams. Anyway @davidfowl I have some examples for you:
Stock flow, client read somwhere this flow, maybe process it and send it to serverside. like 15k-50k messages per second.
Audio/video stream from client side to server. I did it once (I may be wrong. It was a couple of years ago and it didn't work very well), I sent audio buffer over regualr method, and play sound from Stream.
But perhaps these are all examples for which websockets are better without additional wrappers for better performance. Although once a stream has been created, there probably isn't much overhead anymore.
Stock flow, client read somwhere this flow, maybe process it and send it to serverside. like 15k-50k messages per second.
I don't understand this one.
Audio/video stream from client side to server. I did it once (I may be wrong. It was a couple of years ago and it didn't work very well), I sent audio buffer over regualr method, and play sound from Stream.
You can send anything really over SignalR now. Of course, there are optimizations you can make if you do it all yourself but the latest hub protocol is pretty good at transfering pretty much anything, since it support both binary and a text (JSON) protocol.
The benefit of client results if really keeping the state machine in a single async flow vs breaking the flow and state up into multiple calls over client and server that need to be correlated.
In the first case, I was referring to stock trading, forex, or crypto. for example, clients read a stream of quotes from exchanges, then aggregate a it bit and then send it to the server. And you need a good speed there, also this is an endless stream of data.
Or, as an option, files can be transferred via signalR.
Someone in my team tried to do this. But something went wrong. =)
But as you can see, this is not a good examples. each case requires a special solution and imposes its own limitations...
@davidfowl
If it's not supported, then I'd like support to be added for returning streaming responses from the client to the server.
Can you outline the scenario? We've discussed it but the implementation is tricky so motivating examples would help.
In our case we have a .NET-based client and server for monitoring devices at remote sites and are using SignalR to add a feature to the client that allows occasional remote access via the user's browser to the device administration web pages for configuration by proxying HTTP traffic from the server to the client. (Access is limited to specific devices the user has permission to access remotely.)
There are other approaches to this one could take such as creating an SSH reverse proxy, but after exploring and prototyping a number of options SignalR will be the easiest to integrate into our existing client/server apps and the performance seems acceptable for our use case.
Streaming client results are not required for 95% of our use cases, as most HTTP responses can be handled by choosing an appropriate max message size, but some devices support things like downloading dumps of troubleshooting information which can be into the tens or hundreds of MB, and it would be nice to support this use case.
The benefit of client results if really keeping the state machine in a single async flow vs breaking the flow and state up into multiple calls over client and server that need to be correlated.
As you mention above, one workaround is to send the response from the client as a separate one-way streaming call and correlating it with the request on the server. We used this approach in our prototype before client results were supported, and it definitely works. The main downside is the additional complexity in waiting for the response and doing the correlation.
@KSemenenko this is about server to client streaming (which we don't support), initiated by the server.
This is a feature that I exactly wanted !!!!! I really hope SignalR could become into a more general framework for remote communication.
If it's not supported, then I'd like support to be added for returning streaming responses from the client to the server.
Can you outline the scenario? We've discussed it but the implementation is tricky so motivating examples would help.
For example, In IOT scenario , sometime server need to initiate request to device:
So, clients need to communicate with server in two-way real-time communication (best if request can be initiated by both side). There are many alternative options:
Client,User,Group
. The missing is, not support two-way stream and server can't initiate stream, which means some fuctions , like Retrieval client's files/data
, send files/data to client
, can't be done in a single async flow
.//Client can do this
var channel = Channel.CreateBounded<string>(10);
await connection.SendAsync("UploadStream", channel.Reader);
await channel.Writer.WriteAsync("some data");
await channel.Writer.WriteAsync("some more data");
channel.Writer.Complete();
//Client can do this
var cancellationTokenSource = new CancellationTokenSource();
var channel = await hubConnection.StreamAsChannelAsync<int>(
"Counter", 10, 500, cancellationTokenSource.Token);
// Wait asynchronously for data to become available
while (await channel.WaitToReadAsync())
{
// Read all currently available data synchronously, before waiting for more data
while (channel.TryRead(out var count))
{
Console.WriteLine($"{count}");
}
}
Console.WriteLine("Streaming completed");
public interface ITestHub
{
Task<ChannelReader<string>> RequestClientToUploadStream();
//Or Task<IAsyncEnumerable<string>> RequestClientToUploadStream();
Task RequestClientToDownloadStream(ChannelReader<string> stream);
//Or Task RequestClientToDownloadStream(IAsyncEnumerable<string> stream);
}
//Server can't do this
var stream = await _hubContext.Clients.Client(someConnectionId).RequestClientToUploadStream();
await foreach (var item in stream.ReadAllAsync())
{
//...
}
//Server can't do this
var channel = Channel.CreateBounded<string>(10);
var stream = await _hubContext.Clients.Client(someConnectionId).RequestClientToDownloadStream(channel.Reader);
await channel.Writer.WriteAsync("some data");
await channel.Writer.WriteAsync("some more data");
channel.Writer.Complete();
@BrennanConroy how much work would it be to support streaming with client results? We hit a ton of gotchas implementing single results that would only be amplified by this (there are lots of foot guns), but I agree if they are strong scenarios that warrant it, we can investigate. If we did this, we'd start with single connections only, not groups etc.
For example, In IOT scenario , sometime server need to initiate request to device:
Retrieval device's status or files (such as logs,images,videos,recorded data,etc) Command device and return execution results Upgrade device's firmware
For sure, I have an example of command and control using client results https://github.com/davidfowl/CommandAndControl. These things were of course always possible before but was made trivial but adding client results. Still, seeing motivating, non-contrived examples (preferably with existing implementations) would be the best way to motivate this work.
For sure, I have an example of command and control using client results https://github.com/davidfowl/CommandAndControl. These things were of course always possible before but was made trivial but adding client results. Still, seeing motivating, non-contrived examples (preferably with existing implementations) would be the best way to motivate this work.
CommandAndControl is really cool! and it's pattern is really suit for IOT or game scenario. That's why I think SignalR is better(easy to use) than gRPC or something else, Mainlly becase it can initiate request from server, while orther frameworks can't. The best practices in my imagination is:
Orleans
+
SignalR (with streaming initiated by the server)
+
Orleans based SignalR backplane
[+Protobuf Serializer ]
\=
Will be able to be used in all scenarios 😂
Orleans + SignalR (with streaming initiated by the server) + Orleans based SignalR backplane [+Protobuf Serializer ] = Will be able to be used in all scenarios 😂
try our library, we even made support for "server calls client method and get result" https://github.com/managedcode/Orleans.SignalR
My example is a variable group of worker services. Each one processes data and stores some metadata about what it is doing in a local DB. The hub is a Blazor Server App that provides a central place to look at all the results of any or all workers. It is much like the command and control example, except the clients are returning tables that exceed the recommended message size by an unknown and growing amount. So I can't just increase the SignalR message size and pretend like it won't overrun some day.
I am currently streaming the data on a different connection because I can't stream in a client response. Then I return a simple confirmation in the client response. This gets me the client response flow I want and the streaming data to workaround the size limitation on the client response. It just seems odd. (You could argue that I am in this position due to architecture decisions but I don't want to roll a full distributed DB sync for this project at this point).
NOTE: I am using TypedSignalR.Client
due to the lack of strongly typed client to hub communication.
/// A "client results" method in my client looks like this:
public async Task<int?> GetJobList()
{
int? numberOfJobs = null;
try
{
var jobList = _dbClient.GetAllThe<JobProgress>();
numberOfJobs = jobList.Count;
// I am recieving the SetCompleteJobList in a hub method that writes to _theJobList
await _strongHub.SetCompleteJobList(jobList.ToAsyncEnumerable());
}
catch (Exception e)
{
_logger.Error(e, "Failed to send job list to hub!");
}
return numberOfJobs;
}
// From hub example:
var expectedJobCount = heyClient.GetJobList();
if (expectedJobCount == _theJobList.Count)
Console.WriteLine("This is what success looks like.")
Is there an existing issue for this?
Is your feature request related to a problem? Please describe the problem.
I am trying to combine client results with streaming in SignalR to send a request message from the server to the client and have the client respond with a stream of response messages. Is this currently supported?
For example can I create a
Hub<IClient>
and IClient methodTask<IAsyncEnumerable<PartialContent>> GetContent()
and call it from the server on a client?All the streaming examples I've found in the documentation are initiated by the client rather than the server.
Describe the solution you'd like
If it's not supported, then I'd like support to be added for returning streaming responses from the client to the server.
Additional context
No response