SignalR: How to reacquire client info after app pool recycle - signalr

At connect time, I map the clientID to other client and subscription info which I build in a static variable. This works fine until the app pool recycles or I recompile and then I lose my mappings.
Client connections persist after the app pool recycle, but I am not seeing anything in either the OnConnectAsync() or OnReceivedAsync() methods.
I see in Firebug that a new POST is being submitted by my clients every 4 minutes. Is there some SignalR event that I can override to capture the incoming POST and rebuild my client mapping for the current persistent connections?
Thanks.

Make sure you're using SignalR 0.4. You can handle OnReconnectedAsync.
https://github.com/SignalR/SignalR/wiki/PersistentConnection
DISCLAIMER: If you store your application state in static variables it will never be able to scale out on a farm (but maybe you don't care).

Related

How to Intercept ScaleoutMessage Broadcast: (Edited: How to send message directly to ServiceBus SignalR Backplane)

I have following scenario:
User request for certain resource on server, This request is long running task and very like 2~3 seconds to 10 seconds. We issue a JobTicket to user, As our user want to wait.
On receiving request we store that request in persistence storage and issue a token to user as JobTicket (GUID).
User make connection with Hub to get information about that GUID.
In Background:
We have WAS Hosted as well as Windows Service to perform some operation on that request.
On complete, WAS Hosted/Windows Service call our Web Application that job has been completed.
From there based on job Ticket we identify which user and on its connection we let user know its job has been completed.
Now we have farm of servers, we are using Windows Server On Prem ServiceBus 1.1 which is working fine, But challenge we have is that we are not able to intercept ServiceBus based backplane message broadcast and message is going to all the client. As we have farm, user intermediately may have drop connection and connected to other server based on load balancer so we need to have scale out using Service Bus as its kind of seamless to integrate and we are also using for our internal purpose in our application so we don't want to user any other mix in complex solution.
I have tried using IHubPipelineModule but still Scale out message broadcast not passing thru that, I tried to hookup SignalR code directly and debug thru it but its taking long. I don't want to mess-up something arbitrary in actual code. As I can see that in OnReceive I can see message are coming, but not able to follow further. I just need small mechanism that I can intercept broadcast message and make sure that it goes to client it intended and not all the client by wasting resources, and security concern as well.
Please help me on this issue, it's kind of stuck from last 4 days and not able to come to any solution and same time I want to go with establish pattern and don't want to fork any special build for this kind of small issues which I am sure one of you expert knows how I can do that seamlessly.
Thanks,
Shrenik
After lots of struggling and not finding straight forward way, I have found the way as below for someone else in future it might help.
Scenario:
1. Web Farm: Host External User facing Web Pages
2. Backend Process: Which is mix of WebApi, SharePoint, Windows Service etc.
User from Web Page submit some request and get a unique id as return back. Internally on receiving request, we queue that request to Service Bus using TopicClient for processing.
There are pool of Windows Service watching on Message on Service Bus using SubscriptionClient and process that message. On completion of process which can run from 5 seconds to 30 seconds and some cases even more. We need to inform client that its job done if its waiting on web page or waiting for completion notification.
In this story, We are using SignalR to push job completion notification to client.
Now my earlier problem is How I let know from windows service to web application that job is done so send notification to client who submitted request.
One way is we hosted another hub internally in web application, Windows service act as client and call web application hosted hub, and in that hub method it will call external facing hub method to propagate message to specific client who submitted request, for which we are using Single user Group.
And as we have register service bus as backplane it will propagate to other servers and then appropriate client will get notification. So this is ideal solution and should work in most cases.
In above approach we have one limitation that, how Windows Service connect to Web Client, as we donot have windows auth, but we have openid based auth with ADFS. Now in such case Web Application required special code in which provide separate userid or password for windows service to communicate or have windows authentication also allowed for that hub for service account of windows service.
I was trying and trying how to remove all this hopes between interserver communication and again management of extra security.
So I did below with simplicity, though it tooks me whole night to find our internal of SignalR. But it works:
Approach is to send message directly to ServiceBus Backplane, and as all Web Server already hooked-up with ServiceBus backplane then they will get message.
Unfortunately SignalR doesn't provide such mechanism to send message directly to Backplane. I think its on pub/sub model so they don't want somebody to hack in their system :). or its violation of their pattern, but its make sense, in my case because of different roles and security, I have simplify code as below:
Create a ServiceBusMessageBus instance in my code, Same way as Below: Though I have created separate instance and store till lifetime of Windows Service, so I don't create instance every time:
ServiceBusMessageBus serviceBusBackplane = new ServiceBusMessageBus(new DefaultDependencyResolver(), new ServiceBusScaleoutConfiguration(connectionString, appName));
Create a ClientHubInvocation Object: This is a message which actually get created in SignalR infrastructure when Backplane based message broadcast:
ClientHubInvocation hubData = new ClientHubInvocation
{
Args = new object[] { msg },
Hub = "JobStatusHub",
Method = "onJobStatus",
State = null,
};
Create a Message object which accept by ServiceBusMessageBus.Publish, Yes, so this is a method which actually get called on base class ScaleoutMessageBus.Publish. This class is actually responsible for sending message to topic and other subscribers on other server nodes. Why not use that directly. Now to create Message Object, You need following code:
Message backplaneMessage = new Message(
sourceId,
"hg-JobStatusHub." + name,
new ArraySegment(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(hubData))));
In above second parameter is something interesting,
In case if you want to publish to all the client then syntax is "h-", in my case specific group user, so syntax is "hg-.. You can check the code here: https://github.com/SignalR/SignalR/blob/bc9412bcab0f5ef097c7dc919e3ea1b37fc8718c/src/Microsoft.AspNet.SignalR.Core/Infrastructure/PrefixHelper.cs
Publish your message to backplane directly as below:
await serviceBusBackplane.Publish(backplaneMessage);
I wish this PrefixHelper class have been public.
Remember: This is not recommended way and doent insulate from future upgrade for SignalR, as its internal they may change so any upgrade might come with small hazale to change this code. But in summary this works. Hope SignalR Team provide some mechanisam out of box to send message directly to backplane instead.
Thanks

Signalr webfarm with Backplane out of sync

We have a SignalR application that we built and tested for use on a single web server. The requirements have changed and we need to deploy the application to a webfarm. SignalR supports several backplanes, since the application already uses Sql Server that is what we have implemented. With the introduction of a second web node we ran into an issue with keeping the data that is cached within the Hub synced between the nodes.
Each hub has an internal cache in the form of a DataSet.
private static DataSet _cache; The cache gets populated when a client first requests data, and from there any interaction updates the local cache and the sql server, then notifies all connected clients of the changes.
The backplane handles the broadcast messages between the clients but the other node does not receive a message.
Our first thought was that there might be a method that we could wire up that would triggered from the backplane sending a message to a nodes clients, but we have not seen such a thing mentioned in documentation.
Our second thought was to create a .net client within the hub.
private async void ConnectHubProxy()
{
IHubProxy EventViewerHubProxy = _hubConnection.CreateHubProxy("EventViewerHub");
EventViewerHubProxy.On<string>("BroadcastAcknowledgedEvents", EventIDs => UpdateCacheIsAcknowledged(EventIDs));
EventViewerHubProxy.On<string>("BroadcastDeletedEvents", EventIDs => UpdateCacheIsDeleted(EventIDs));
ServicePointManager.DefaultConnectionLimit = 10;
await _hubConnection.Start();
}
Our questions:
How do we keep the cache in sync?
Is the first thought possible and we missed it in the documentation?
Are there any issues concerns with having a hub connect to itself?
The recommended way to have "state" in a scaleout scenario is to have a source of persistence. So in your case, if you're looking to have a "global-like" cache one way you can implement that is via a database.
By creating a database all your nodes can write/read from the same source and therefore have a global cache.
The reason why having an in-memory cache is not a good idea is because in a web farm, if nodes go down, they then lose all their in-memory cache. By having persistence, it doesn't matter if a nodes been up for days or has just recovered from a "shutdown" based failure, the persistence layer is always there.

ASMX Web Service Blocked by Sessions

How does IIS handles multiple simultaneous requests to a web service using the same session id when that web service relies on sessions?
If the first web service call has not finished processing, does IIS queue the second web service call temporarily until the first web service call has finished (since they both rely on the same session data).
Or does IIS allow both requests to go forward and any changes made to the session store by the first request immediately affect the second web service call in process even thought that may cause unexpected results.
I believe it is using the first option by queuing the second call until the first is completed due to session reader\writer locks but I have not been able to locate a definitive answer.
Note: In this case the web service requests are generated from the client browser asynchronously.
Yes, like anything else that's using session state, the session state module is going to block the request from going any further down the pipeline (i.e. executing your ASMX web method) until it can aquire the lock to the session. Session state is the enemy of concurrency, avoid it at all costs.

ASP.NET Web Service very slow when [WebMethod(EnableSession = true)]

I have created a ASMX Web Service which does some Active Directory stuff behind the scene.
As I wish to retain certain information within Web Services under user session, I have decided to put [WebMethod(EnableSession = true)] and start using Session variables.
However, when I turn that option on, the return time from app -> web service -> app has became ridiculously long. (about a minute or more).
If I remove [WebMethod(EnableSession = true)], it is fairly fast.
Anyone know what is going on?
Possible reasons:
Session state is stored out of process (state server/ SQL server) and getting/storing it taking a long time
You are making multiple concurrent requests (including service requests) under the same session. ASP.NET ensures that only one session-full (session read/write) request execute at a time and hence, multiple concurrent requests would queue up.
EDIT :
For #2, obvious solution is to avoid session state use - for example, can you put the relevant information into another store such as cache or database (expensive).
If you are only reading session state in web service then you may take advantage of read-only session state (see IReadOnlySessionState). Read-only session state allows concurrent read-only requests - read/write request will still block all other requests. Now, EnableSession from WebMethod attribute does not support this - it either provides no session or read/write session. So one of the workaround can be to implement your own handler implementing IReadOnlySessionState and then route asmx request to thi handler using a http-module and then switch the handler to default one later. Because your handler requires read-only session state, you will have the read-only session state - see this forum post where such http-module that switches the handler has been given.

SOAP Web Service on ASP.NET - Multiple Connection - Static Values - XMLRPC API

Have a SOAP Web Service that encapsulates calls to a 3rd party API... so our application can simply call my service and then my service handles all the various calls to the API. Works just fine.
However, we've hit a problem where the API we're connecting to allows a max of 10 connections at any given time for a given set of credentials.
Connections at most take a couple of seconds to process, but when we go live, we could in theory have users that max out this. So we've created multiple accounts (5) to the API giving us 50 connections across the 5 users.
How does ASP.NET handle connections to the Web service? I know it works asynchronously, but does it spawn multiple instances of my class or reuse the same class. Will variables persist across instances (i.e Will static variables work)?
What I need to do is if a call to the API fails on Client1, rollover to Client2 (or Clients[0], Clients[1]) etc... Sadly I have no way to detect if a given Client is out of connections at any given moment. I could poll it with a test call, but that would take time and be no guarantee the the client has a connection available when I make the call.
The API I'm calling is via XMLRPC Proxy class (CookComputing). Is the "connection" made when the Client is created or when the call is made, passing along the credentials?
public static IVoicestar GetClient(string userID, string password)
{
IVoicestar client = XmlRpcProxyGen.Create<IVoicestar>();
client.Credentials = new NetworkCredential(userID, password);
return client;
}
Seems from this that the credentials simply "ride along" until I make a call via Client.MethodCall() and then the connection is made.
If you are using ASP.NET Web Services (asmx) then it would spawn a new instance of your web service class for each request. In case WCF based web services, you can control the instancing /concurrency using attributes/configuartion (see this article) - you have three instancing modes possible - per call, per session and singleton.
Irrespective of what you are using, you can always implement your own pooling mechanism to pool your API connection. You already have a factory method to get the API client - just put call to pooling layer within method.
Normally Windows XP and Windows 7 have a limit of 10 concurrent TCP/IP connections. Maybe that's it. Be sure to work in a windows server version.

Resources