StackExchange Redis .NET client not getting messages when connections are reordered - c#

Using the StackExchange.Redis client in a C# application, I am attempting to use PubSub to perform cross-server communication between web servers running the same application. In this model, each server is running its own Redis server, and its connection multiplexer sets the local server as the first server in the list. In other words, SERVER_A and SERVER_B each run a Redis server on port 6379 and have connection strings as follows:
SERVER_A --> "SERVER_A:6379,SERVER_B:6379"
SERVER_B --> "SERVER_B:6379,SERVER_A:6379"
Using external clients to subscribe to each Redis server, I can see SERVER_A publishing messages to its local Redis server. However, SERVER_B is not picking up the message, despite having a connection via the multiplexer. When I rearrange the order of the connections so they match, then messages from A are picked up by B. Is there something in a setting or otherwise that I need to do so that the connection multiplexer will listen to subscriptions on all servers, or is this a bug in the StackExchange.Redis client?

You don't mention any replication in your description. When SE.Redis connects to multiple servers it is intended to be a related family of servers - for example, either:
two or more 2.* servers related via master/slave
three or more 3.* servers related via a formal cluster
Replication groups have distribution mechanisms for pub/sub. Two disconnected servers: do not.

Related

ServiceStack.Server: Redis server integrated withing ServiceStack lib?

I want to clarify if I understood the ServiceStack.Server functionality correctly.
On this page, ServiceStack.Server API is described, and it states that
Creates a Redis MQ Server that processes each message on its own background thread
Am I correct when I then assumed that this is a standalone Redis server, so if I start this with the code below, I am running a fully fledged Redis server, meaning, no need to install the Redis software, or Memurai or the likes?
Because, if I follow this example, and have no other running Redis server, I get
'No connection could be made because the target machine actively refused it. 127.0.0.1:6379'
This line
var redisFactory = new PooledRedisClientManager("localhost:6379");
starts a client and tries to connect to 6379 and naturally fails. The redisFactory is then used in the line creating the MqServer, which is odd to me:
var mqHost = new RedisMqServer(redisFactory, retryCount: 2);
If this is a standalone MQ server, then creating a client before creating and starting the server isn't going to work, which makes me think this is not an Redis MQ server?
Am I correct when I then assumed that this is a standalone Redis server
That's an incorrect conclusion, the Redis Server is a distributed in-memory data structure server which just like other distributed servers like an RDBMS or MQ Broker runs in its own isolated networked process.
All ServiceStack Redis Libraries connect to a Redis Server, Redis MQ is like all other distributed ServiceStack MQ providers which process messages sent to a broker, in this case Redis MQ uses the Redis Server as its broker. The statement:
Creates a Redis MQ Server that processes each message on its own background thread
Explains how Redis MQ processes the messages that it receives, where each different Message Type (i.e. Request DTO Type) processes messages in its own background thread.

Using Redis with SignalR

I have an ASP.NET MVC application that runs on server A and some web services that run on server B. I have implemented real-time notifications for which I have used SignalR on server A. But now I need server B to also be able to send messages to a View served from server A (the main web application). Hence, I am trying the tutorial here to involve Redis backplane.
In my startup in server A, I have added the following:
GlobalHost.DependencyResolver.UseRedis("localhost", 6379, string.Empty, "abc");
app.MapHubs();
Here, I assume that "myApp" indicates the channel and when I run publish abc "hello world" on the Redis console, I can see the subscriber count returned as 1, but I am not able to figure out how a SignalR hub interacts with the channel. Where do I receive the message on the server/view? Can we subscribe to only one redis channel? Can't we dynamically configure to subscribe to a particular channel?
EDIT: I can see messages sent from chat Application implemented using SignalR on redis console if I subscribe to abc.
Also for now I have implemented my own redis listener on server A which in receiving a message from redis channel, calls the signalR hub function. I am sure there must be a different way to do this and I am hoping redis backplane can help me but unsure how it works.
Backplane distributes messages between servers.
GlobalHost.DependencyResolver.UseRedis("localhost", 6379, string.Empty, "abc");
Here, abc is the redis channel, that means whichever server is connected to redis server with this channel, they will share messages. SignalR channel (group) is different than Redis channel. You can share also SignalR channel (group) messages.
Then just install the Microsoft.AspNet.SignalR.Redis NuGet to your servers.
Connect your servers to Redis like this:
GlobalHost.DependencyResolver.UseRedis("server", port, "password", "AppName");
app.MapSignalR();
Then, use your signalr as before. You don't have to do anything else.
When Server A sends a message to the clients, it will send the message first to Redis. Then Redis will share the message with all subscribers (servers A and B). Then, A and B will send the message to their clients. (Also viceversa is true, it will be same for if B sends a message).
Let's say A sends a message to the clients. _context.Clients.All.TestMessage("Hello");
This will go first to redis and redis will share this with A and B.
Then both A an B will send this message to their clients.
_context.Clients.All.TestMessage("Hello");
But you don't have to worry about these kind of things. I said before. Install package, conntect your servers to redis and use signalr as before.
If we come in your question. The answer is Yes. Server B can send messages to server A clients by Signalr Backplane.
This image summarizes what I told:

Client-Server architecture

This topic has been discussed million times before, but let me clarify my needs:
I need a single server which controls a system and includes the necessary functions. Furthermore, there will be "n" Clients which represents only the HI/GUI and call server side functions. The server itself should be able to send data back to the clients and call client-side functions too (like shutdown, exit and so on...)
I have heard about duplex services/contracts (http://msdn.microsoft.com/en-us/library/ms731064.aspx), but I'm not sure how far I'll come with that.
How would you handle this?
I recently made a proof of concept app that made both the server and the client host a WCF service each. The client connects to the server and then in a handshake call, gives the server the connection information to allow the server create a separate connection back to the client. It worked a treat with multiple clients on network links from local lan to 64k line on remote sites at the same time.
You could use WCF, and host the service on the server in IIS, in the application on the client and let the client register it's endpoint on the server.

How to Implement Exchange like availability monitoring of internal SQL Server

We have an internal app(Thick Client) that relies on our central SQL server. The app is a Desktop app that allows the users to work in "Offline" mode (e.g. Outlook). What I need to accomplish is a way to accurately tell if SQL is available or not.
What I have so far:
I currently use the following method -->
internal static void CheckSQLAvailability()
{
using (TcpClient tcpc = new TcpClient())
{
try
{
tcpc.Connect(Settings.Default.LiveSQLServer, Settings.Default.LiveSQLServerPort);
IsSQLAvailable = true;
}
catch
{
IsSQLAvailable = false;
}
}
}
I am not crazy about this approach for the following reasons.
Prone to false Negatives
Needs to be "manually" called
Seems "smelly" (the try/catch)
I had thought to use a timer and just call this every X(3??) minutes and also, if a negative result, try a second time to reduce the false negatives.
There is a similar question here -->Detecting if SQL server is running
but it differs from mine in these ways:
I am only checking 1 server
I am looking for a reactive way versus proactive
So in the end, is there a more elegant way to do this? It would all be "in-network" detection.
P.S. To offer some background as requested in an answer below: My app is a Basic CRUD app that can connect to our Central SQL Server or a local SQLExpress Server. I have a Merge Replication Module that keeps them in Sync and the DAL is bound to a User.Setting value. I can, already, manually flip them from Central to Local and back. I just want to implement a way to have it automatically do this. I have a NetworkChangeDetection class that works quite well but, obviously, does not detect the Remote SQL's.
Consider what the Windows Cluster monitor does for a SQL Server Cluster resource: it actually connects and runs a dummy query (SELECT ##version). This indicates that the SQL is running, is actively listening for requests, and is able to run a request and return a result. For the clustering monitor the response to this query is the 'heartbeat' of the server and if it fails to get a response, for whatever reason, it may initiate a cluster failover.
Only connecting to TCP has several drawbaks in my opinion:
it omits non-TCP protocols like local shared memory (LPC) or remote net pipes (SMB)
it requires hard codded TCP port number as opposed to let the instance port listening auto-discovery do its work (SQL Browser and friends)
it only establishes that the OS level socket can be established, it does not validate that the SQL Server itself is in a runnable state (non-yielding scheduler might block network IO requests acceptance, scheduler overload and worker starvation may do the same, memory resource exhaustion etc etc).
Unfortunately there is no way to get a notification from SQL Server itself of saying 'hey, I'm active, won't you send some requests?'. I don't know all the details of your fat client ('thick app'), but perhaps you should investigate a different metaphor: clients do all work locally, on SQL Express instances, and these instances synchronize the data when the server is available. Service Broker was designed specifically with this connection retry mode and it would hide the server availability due to its asynchronous loosely coupled programming API.

Does the TIBCO EMS server manage reconnection? or does the client?

The TIBCO EMS .NET reference guide says (pg 134)
To enable reconnection behavior and fault tolerance, the serverURL parameter must be a comma-separated list of two or more URLs. In a situation with only one server, you may supply two copies of that server’s URL to enable client reconnection (for example, tcp://localhost:7222,tcp://localhost:7222).
The TIBCO EMS user's guide (pg 292) talks about failover scenarios, client notification, and automatic transfer of clients to the backup server, but nothing specifically "reconnect" related.
In a "reconnect" scenario, does the server handle everything? or does the client have to do something with it's TIBCO.EMS.Connection instances?
Looks like from our testing that the there are settings on both the server and the client that enable this feature. On the client side, the SetReconnAttemptCount, Delay, Timeout govern the attempts the client tries to reconnect once its aware of a server failover / connection failover.
In our testing, we used a single server environment, listed the server twice in the connection string (using the trick you outlined above) and when that server was taken offline, we received a client notification of the failover process taking affect (we enabled Tibems.SetExceptionOnFTSwitch(true)) and when the server was brought back online, our client seemlessly reconnected without missing a beat. We didn't need to code anything, the internal reconnect logic worked its magic.
On the server side, fault tolerance needs to be enabled and I believe server-client and client-server heartbeats need to be enabled (though this has not yet been verified).
Hope this helps.

Categories

Resources