Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StackExchange.Redis - Is it possible to prioritize Endpoints?

My setup:

  • 4 Windows servers
  • A Redis Node and a Sentinel Process on each of theses servers
  • The same webapplication deployed on each of these servers
  • The webapplication connects to the redis servers via the StackExchange.Redis driver

Everything works great but I wonder if it is possible for read operations to always try to use the redis node that is locally available first. This would enhance performance quite a bit because there would be fewer hops for all read operations.

As far as I see one can prioritize slaves over masters für specific commands via the Command Flags property. But is there a way to prioritize specific endpoints?

PS:

Used DLL: [email protected]

Redis Server Version: 3.2.100

EDIT:

Here is my connection code. The reason why I did not use the recommended Lazy getter is because I wanted to Connect/Reconnect on Failure of one of the nodes, which works very well with my solution.

internal class RedisConnector
{
    private readonly ConfigurationOptions _currentConfiguration;
    internal ConnectionMultiplexer Connection;

    internal RedisCacheStore Store;

    internal RedisConnector(ConfigurationOptions configuration)
    {
        _currentConfiguration = configuration;
        Connect();
    }

    internal IDatabase Database
        => Connection.GetDatabase(RedisCacheConfiguration.Instance.Connection.DatabaseId);

    internal IServer Server => Connection.GetServer(Database.IdentifyEndpoint());

    private void Connect()
    {
        Connection = ConnectionMultiplexer.Connect(_currentConfiguration);
        if (Connection == null || !Connection.IsConnected)
            throw new CacheNotAvailableException();
        Connection.ConnectionFailed += OnConnectionFailed;
        Connection.ConnectionRestored += OnConnectionRestored;
        Store = new RedisCacheStore(Database);
    }

    private void Reconnect()
    {
        if (Connection != null && !Connection.IsConnected)
            Connection.Dispose();
        Connect();
    }

    private void OnConnectionFailed(object sender, ConnectionFailedEventArgs args)
    {
        lock (_currentConfiguration)
        {
            if (_currentConfiguration.EndPoints.Contains(args.EndPoint))
            {
                _currentConfiguration.EndPoints.Remove(args.EndPoint);
                Reconnect();
            }
        }
    }

    private void OnConnectionRestored(object sender, ConnectionFailedEventArgs args)
    {
        lock (_currentConfiguration)
        {
            if (!_currentConfiguration.EndPoints.Contains(args.EndPoint))
            {
                _currentConfiguration.EndPoints.Add(args.EndPoint);
                Reconnect();
            }
        }
    }
}
like image 376
phil Avatar asked Nov 08 '22 21:11

phil


1 Answers

in this case.

Let say you have this case, 3VM with 3 apps & 3 redis instances.
enter image description here The best option for APP runningin VM1 is to use Redis in VM1. The best option for APP runningin VM2 is to use Redis in VM2. The best option for APP runningin VM3 is to use Redis in VM3.

You can implements some rules like so :

private static Lazy<ConfigurationOptions> configOptions
    = new Lazy<ConfigurationOptions>(() => 
    {
        var configOptions = new ConfigurationOptions();
        configOptions.EndPoints.Add("x.x.x.1:6379");          
        configOptions.EndPoints.Add("x.x.x.2:6379");
        configOptions.EndPoints.Add("x.x.x.3:6379");

        configOptions.ClientName = "LeakyRedisConnection";
        configOptions.ConnectTimeout = 100000;
        configOptions.SyncTimeout = 100000;
        return configOptions;
    });



private static string getIP()
{
    var host = Dns.GetHostEntry(Dns.GetHostName());
    foreach (var ip in host.AddressList)
    {
        if (ip.AddressFamily == AddressFamily.InterNetwork)
        {
            return ip.ToString();
        }
    }
    throw new Exception("ip not found!");
}


private static Lazy<ConfigurationOptions> getOptionsForIp(string myip)
        {
            var configOptions = new ConfigurationOptions();
            configOptions.EndPoints.Add(myip);
            configOptions.ClientName = "LeakyRedisConnectionDirectVM";
            configOptions.ConnectTimeout = 100000;
            configOptions.SyncTimeout = 100000;
            return configOptions;
        });


private static ConnectionMultiplexer conn;

private static ConnectionMultiplexer LeakyConn
{
    get
    {
        if (conn == null || !conn.IsConnected){               
            string myIP = getIP();

            conn = ConnectionMultiplexer.Connect(getOptionsForIp(myIP).Value);
            if(conn == null || !conn.IsConnected){
                conn = ConnectionMultiplexer.Connect(configOptions.Value);
            }

        }
        return conn;
    }
}

How to use this code:

 var db = LeakyConn.GetDatabase();
 db.StringSet(key, i);
 db.StringGet(key);
like image 187
jeorfevre Avatar answered Nov 14 '22 21:11

jeorfevre