SignalR takes a long time to respond to a heartbeat - c#

I created a very simple ASP.NET Core app with SignalR with Visual Studio using a Web App MVC application template with the following customization:
added a reference to #microsoft/signalr library via libman,
referenced <script src="~/lib/microsoft-signalr/signalr.min.js"></script> in _Layout.cshtml,
added the required SignalR services in Startup.cs and created an empty Hub, exposed in the following way:
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<MyHub>("hub/remote");
// ... MVC router definitions
}
created the SignalR connection in JS:
const connection =
new signalR.HubConnectionBuilder()
.withUrl("/hub/remote")
.configureLogging(signalR.LogLevel.Trace)
.withAutomaticReconnect()
.build();
connection.start().then(() => console.log("Connected."));
Then I launched the MVC app and everything started without an error.
However, I took a closer look at SignalR log messages:
I believe this is the SignalR's internal heartbeat that keeps the connection alive.
I wonder why does it take 4-5s between sending the message and receiving the response?
I also tried using SignalR in a more complex application and from time to time I even started receiving "Reconnecting" events, as the load was significantly larger there.
That makes me feel that I do something wrong while configuring the connection, but no idea what exactly.

"Connection Slow" isn't an event in ASP.NET Core SignalR.
The heartbeats are not directly related to each other, so the gaps between client and server pings are normal.

Related

Self hosted GRPC server

I am attempting to transition away from WCF to move our codebase across to .NET Core. Our services are all hosted as Windows services at present so am trying to self-host the gRPC service as well (rather than building AspNetCore applications). I have successfully built a server using Grpc.Core.Server, and the client side as well with Grpc.Net.Client.GrpcChannel, see the code snippets below for reference.
Server:
var builder = ServerServiceDefinition.CreateBuilder();
// Binder is a small class ripped from the CodeFirst example
var binder = new Binder();
binder.Bind(builder, serviceType, service: serv);
var serverServiceDefinition = builder.Build();
var server = new Grpc.Core.Server
{
Services = { serverServiceDefinition },
Ports = { new ServerPort(host, port, ServerCredentials.Insecure) }
};
server.Start();
Client:
var channel = GrpcChannel.ForAddress(Uri, new GrpcChannelOptions()
{
//HttpHandler = new GrpcWebHandler(new System.Net.Http.HttpClientHandler())
});
var service = channel.CreateGrpcService<TService>();
However because our applications are still running in .Net Framework 4.8 I get the runtime exception when testing out this code:
System.PlatformNotSupportedException : gRPC requires extra configuration on .NET implementations that don't support gRPC over HTTP/2. An HTTP provider must be specified using GrpcChannelOptions.HttpHandler.The configured HTTP provider must either support HTTP/2 or be configured to use gRPC-Web. See https://aka.ms/aspnet/grpc/netstandard for details.
That leads me to add in the Grpc.Net.Client.Web.GrpcWebHandler on the client side to switch over to Grpc-web as per the link in the error.
However, I am now struggling to do the equivalent for the server to support Grpc-web. The guide here suggests to either (1) use Grpc.AspNetCore.Web or (2) use "Envoy proxy" to get the server supporting it. The problem with (1) is that I'm not using AspNetCore so I don't think this solution is appropriate, and I can't find any lightweight/easy way to do (2) in a simple C# solution.
Without the server-side support added, I get this exception:
Grpc.Core.RpcException : Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request. WebException: The server committed a protocol violation. Section=ResponseStatusLine", DebugException="System.Net.Http.HttpRequestException: An error occurred while sending the request.
Which I assume is obviously related to the fact the server isn't supporting the Grpc-web requests. So I am at a bit of a dead end with regards to this now. I feel I need to work out how to self-host AspNetCore servers and move to that instead of Grpc.Core.Server, which will open up option (1), but I am finding little to no evidence that is actually possible.
So I guess my main question is: Is there any way to support Grpc-web clients in a server hosted via Grpc.Core.Server?
And if the answer is no --> How can I self-host a GRPC server that will support Grpc-web clients?
As per this getting started guide I have discovered protobuf-net.Grpc.Native which appears to solve the problem I have at the moment. I also discovered I was missing a default constructor for my [DataContract], which I think was unrelated to the errors I was receiving but may have been contributing.

.NET Core MVC SignalR hub methods not running concurrently

I am having an issue with the latest version of SignalR in .NET Core 3.1. It seems that one of my hub methods are blocking a second hub method.
The hub methods are called correctly on the client side. There is no delay after the call to GetPriceHistoryDifferentialAsync, which is a LINQ query on the server, before also logging that the second GetPriceHistoryAsync, which is a quick button click, was called.
client console log
invoke: GetPriceHistoryAsync
invoke: GetPriceHistoryDifferentialAsync
invoke: GetPriceHistoryAsync
Next is the debug output in Visual Studio on the client side.
server log
Stocks.Web.Hubs.StocksHub: Information: GetPriceHistoryAsync A UKhqAp6VQheTCrxYWeyjbQ
Stocks.Web.Hubs.StocksHub: Information: GetPriceHistoryDifferentialAsync UKhqAp6VQheTCrxYWeyjbQ
There is a delay until GetPriceHistoryDifferential completes and then any remaining calls to GetPriceHistory complete.
Stocks.Web.Hubs.StocksHub: Information: GetPriceHistoryAsync AAN UKhqAp6VQheTCrxYWeyjbQ
There was previously no delay in returning data with the button click GetPriceHistory method calls while the GetPriceHistoryDifferential query method call was running. I think updating to .NET Core 3.1 and the latest SignalR is when this issue began.
Does anyone know what caused this problem or how to make SignalR hub methods run concurrently again? I am using the async/await model, and both hub methods await Clients.Caller.SendCoreAsync.

Net Core 3.1 IHttpClientFactory/HttpClient Slow on First Request

I have a .Net Core 3.1 app. In the app, I use the IHttpClientFactory to create an HttpClient. When I make a call using SendAsync, the first request takes over 2 seconds whereas subsequent requests take less than 100 ms. This is not acceptable performance for a production application.
I have also noticed that it happens if I don't make any requests for a while. I came across the PooledConnectionIdleTimeout property, which defaults to 2 minutes, and I can extend that time, but that would only work for pooled connections that already exist, not when needing to create a new one.
I configure the HttpClient in my Startup.cs as such:
services.AddHttpClient("HttpClient",
h =>
{
h.BaseAddress = new Uri(Configuration["PythonUrl"]);
});
Use the HttpClient like this:
var client = httpClientFactory.CreateClient("HttpClient");
var res = await client.GetAsync(nameof(Accounts).ToLower() + "/" + id.ToString() + "/");
when the "Configuration["PythonUrl"]" contains PC name,like this :
{
"AllowedHosts": "*",
"PythonUrl": "http://PC202003261059:8000/",
"url": "http://*:5000"
}
The HttpClient's first request becomes very slow. Can anything be done to avoid this?
I had a similar problem and found a solution that works well for me.
Note that this solution will only work for IIS hosted WebApps.
When hosting an application in IIS, the AppPool is responsible for actually running it. However, it only starts the application when it receives the first request. This is fine in most situations and is therefore the default setting, but this leads to a slow first request.
In IIS manager, right click on the Application Pool that runs your
app and select 'Advanced Settings'. Set 'Start Mode' to 'Always
Running'.
Rick-Click your Site and under 'Manage Website' select 'Advanced
Settings'. Set 'Preload Enabled' to 'true'.
This should make the first request faster and won't put your AppPool to sleep after some time. It should also be noted that this may impact the performance of other apps hosted on the same server.
This article helped me with this.
Disclaimer: the first request might still be slower due to several reason (some of which may be client-side like DNS requests, or network-related lost packets, poor internet connection etc...).

Using ASP.NET Core SignalR in .NET Console Client App to Receive Messages from Azure SignalR

I am learning SignalR but have hit a road block.
I have an Azure function which is successfully posting to Azure SignalR Hosted Service (configured in Serverless Mode)
I've been following this quickstart:
Quickstart: Create a chat room with Azure Functions and SignalR Service using C#
What I would like to achieve is to essentially receive messages from Servers into my client application. To prototype this I have created a Console Application.
I have added the following Nuget Packages
Microsoft.AspNetCore.SignalR.Client -Version 1.1.0
Microsoft.Azure.WebJobs.Extensions.SignalRService
All of the infrastructure seems to be working fine - I am basing that assumption on the face that I can run the demo website at the following address, point it at my local instance (or my instance hosted in Azure)
https://azure-samples.github.io/signalr-service-quickstart-serverless-chat/demo/chat-v2/
Messages posted by my AzureFunction publish directly into the Chat Window.
How can I get these messages to print to a console?
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.ReadKey();
var connection = new HubConnectionBuilder().WithUrl("http://localhost:7071/api").Build();
connection.On<SignalRMessage>("newMessage", (message) =>
{
Console.WriteLine(message.Arguments);
});
connection.On("newMessage", (string server, string message) =>
{
Console.WriteLine($"Message from server {server}: {message}");
}
);
await connection.StartAsync();
Console.ReadKey();
}
}
}
I strongly suspect my problem is related the
connection.On<...>
statements. They never fire. The Connection.StartAsync() seems to work fine and establishes a connection to Azure SignalR instance.
Am I missing some fundamental point? I am just thrashing about at this point.
In Short - can someone please give me a pointer to RECEIVING and WRITING messages to my console window - much in the same way as messages are printed to the web browser in the web chat demo (see second link above).
The messages are simple broadcast messages that I want to go to all connected clients.
Nearly all examples are in Javascript.
Thanks in Advance.
Once I discovered how to add logging to SignalR I could see that it could not resolve the type which was being sent.
It worked once I changed my connection.On to the correct type such as
connection.On<CorrectType>("newMessage", (message) =>
{
Console.WriteLine(message.stringproperty);
});
My thinking was mislead by looking at the article Azure Functions development and configuration with Azure SignalR Service
Where they "seemingly" (atleast in my mind) add a message of type "SignalRMessage" to SignalR. When in fact they were adding a message type of "CorrectType"
CorrectType message
signalRMessages.AddAsync(
new SignalRMessage
{
// the message will only be sent to these user IDs
UserId = "userId1",
Target = "newMessage",
Arguments = new [] { message }
});
I managed to get around it by passing in 'object' when calling 'connection.On' instead of having to create the CorrectType class and let .net figure out of how the object is going to look like.
It was indeed the resolving of the type that prevented the .On from firing on a windows client.

Multiple website server side Clients.All.receiveNotification challenge with SignalR

So I have two websites that I am using SignalR and I have the following in both my Global.asax files:
HubConfiguration hubConfig = new HubConfiguration();
hubConfig.EnableCrossDomain = true;
RouteTable.Routes.MapHubs(hubConfig);
I have a server side event side and I get the hub context to send a message to all listening clients:
var signalrContext = GlobalHost.ConnectionManager.GetHubContext<Notifications.OfferHub>();
signalrContext.Clients.All.receiveNotification("hello world");
The same event happens server side on both websites and I would like to broadcast this cross-domain to all listening clients. I am thinking this is not possible server side because I will not be able to get the HubContext for both websites change the hub.url server side.
Unless anyone has any other suggestions?
Well, it took me a minute to realize what do here. Thanks to David Fowler for his help and SignalR awesomeness! I ended up adding a ServiceReference in each website to a web service in the other website and calling a "Broadcast" WebMethod. The web service webmethod just gets the current SignalR context and broadcasts my message to all clients listening on the respective website.

Categories

Resources