Redis在.net中的使用(3)簡單的主從複製

龐順龍發表於2019-05-11

Redis在.net中的使用(3)簡單的主從複製

在前面的兩篇簡單的實現了redis的安裝和簡單專案中的運用 

Redis在.net中的使用學習(1)下載安裝Redis     Redis在.net中的使用學習(2).net專案中的Redis使用

在這裡我們簡單說說Redis的主從複製實現,Redis跟MySQL一樣擁有強大的主從複製功能,還支援一個master可以擁有多個slave,而一個slave又可以擁有多個slave,從而形成強大的多級伺服器叢集架構,如下圖:



Redis的主從複製功能是非同步進行的,不會影響master的執行,也不會降低Redis的處理效能。Redis的主從架構中,可以考慮關閉Master的資料持久化功能,而只讓Slave進行必要的持久化,這樣就能提高主伺服器的處理能力和效能,同時Slave設定為只讀模式,這樣可以避免Slave快取的資料被誤修改等。

1、配置安裝兩個Redis例項

處於測試考慮,我只在本機安裝兩個Redis例項來進行演示而已。複製一份redis檔案件,修改conf檔案的埠為6380,並啟動,如下圖:

注意:在同一臺電腦上測試,Master和Slave的埠不要一樣,否則是不能同時啟動兩個例項的。




這樣,本機就啟動了兩個Redis例項服務了。

2、主從Redis服務繫結

在Slave 例項,增加:slaveof 127.0.0.1 6379,如下圖:


配置完成之後,重新啟動這兩個例項,如果輸出如下內容,說明主從複製的架構已經配置成功了,如下圖:


這樣,Redis的主從複製就設定完成了。

3、主從複製測試

連線上Master伺服器和Slave 伺服器,然後在Master寫入一條測試快取,然後在Slave中讀取這條測試快取,如下圖:


4、C#中的呼叫測試


主從架構的Redis的讀寫其實和單一Redis例項的讀寫差不多,只是部分配置和讀取區分了主從,如果不清楚C#中如何使用Redis,請移步:Redis在.net中的使用學習(2).net專案中的Redis使用

不過我們需要注意的是:ServiceStack.Redis中GetClient()的這個方法,預設只能拿到Master Redis中獲取連線,而拿不到Slave的readonly連線。這樣Slave起到了冗餘備份的作用,讀的功能沒有發揮出來,如果併發請求太多的話,則Redis的效能會有影響。

因此,我們需要的寫入和讀取的時候做一個區分,寫入的時候,呼叫client.GetClient()來獲取writeHosts的Master的Redis連結。讀取的時候則呼叫client.GetReadOnlyClient()來獲取的readonlyHost的Slave的Redis連結,或者可以直接使用client.GetCacheClient()來獲取一個連線,他會在寫的時候呼叫GetClient獲取連線,讀的時候呼叫GetReadOnlyClient獲取連線,這樣可以做到讀寫分離,從而利用Redis的主從複製功能。

a、修改配置檔案

<!-- redis Start   -->
<add key="SessionExpireMinutes" value="180" />
<add key="redis_server_master_session" value="127.0.0.1:6379" />
<add key="redis_server_slave_session" value="127.0.0.1:6380" />
<add key="redis_max_read_pool" value="300" />
<add key="redis_max_write_pool" value="100" />
<!--redis end-->



b、Redis操作公用類RedisCacheHelper的優化修改


public class RedisCacheHelper
{
    private static readonly PooledRedisClientManager pool = null;
    private static readonly string[] writeHosts = null;
    private static readonly string[] readHosts = null;
    public static int RedisMaxReadPool = int.Parse(ConfigurationManager.AppSettings["redis_max_read_pool"]);
    public static int RedisMaxWritePool = int.Parse(ConfigurationManager.AppSettings["redis_max_write_pool"]);
    static RedisCacheHelper()
    {
        var redisMasterHost = ConfigurationManager.AppSettings["redis_server_master_session"];
        var redisSlaveHost = ConfigurationManager.AppSettings["redis_server_slave_session"];

        if (!string.IsNullOrEmpty(redisMasterHost))
        {
            writeHosts = redisMasterHost.Split(',');
            readHosts = redisSlaveHost.Split(',');

            if (readHosts.Length > 0)
            {
                pool = new PooledRedisClientManager(writeHosts, readHosts,
                    new RedisClientManagerConfig()
                    {
                        MaxWritePoolSize = RedisMaxWritePool,
                        MaxReadPoolSize = RedisMaxReadPool,

                        AutoStart = true
                    });
            }
        }
    }
    public static void Add<T>(string key, T value, DateTime expiry)
    {
        if (value == null)
        {
            return;
        }

        if (expiry <= DateTime.Now)
        {
            Remove(key);

            return;
        }

        try
        {
            if (pool != null)
            {
                using (var r = pool.GetClient())
                {
                    if (r != null)
                    {
                        r.SendTimeout = 1000;
                        r.Set(key, value, expiry - DateTime.Now);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            string msg = string.Format("{0}:{1}發生異常!{2}", "cache", "儲存", key);
        }

    }

    public static void Add<T>(string key, T value, TimeSpan slidingExpiration)
    {
        if (value == null)
        {
            return;
        }

        if (slidingExpiration.TotalSeconds <= 0)
        {
            Remove(key);

            return;
        }

        try
        {
            if (pool != null)
            {
                using (var r = pool.GetClient())
                {
                    if (r != null)
                    {
                        r.SendTimeout = 1000;
                        r.Set(key, value, slidingExpiration);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            string msg = string.Format("{0}:{1}發生異常!{2}", "cache", "儲存", key);
        }

    }

    public static T Get<T>(string key)
    {
        if (string.IsNullOrEmpty(key))
        {
            return default(T);
        }

        T obj = default(T);

        try
        {
            if (pool != null)
            {
                using (var r = pool.GetClient())
                {
                    if (r != null)
                    {
                        r.SendTimeout = 1000;
                        obj = r.Get<T>(key);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            string msg = string.Format("{0}:{1}發生異常!{2}", "cache", "獲取", key);
        }


        return obj;
    }

    public static void Remove(string key)
    {
        try
        {
            if (pool != null)
            {
                using (var r = pool.GetClient())
                {
                    if (r != null)
                    {
                        r.SendTimeout = 1000;
                        r.Remove(key);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            string msg = string.Format("{0}:{1}發生異常!{2}", "cache", "刪除", key);
        }

    }

    public static bool Exists(string key)
    {
        try
        {
            if (pool != null)
            {
                using (var r = pool.GetClient())
                {
                    if (r != null)
                    {
                        r.SendTimeout = 1000;
                        return r.ContainsKey(key);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            string msg = string.Format("{0}:{1}發生異常!{2}", "cache", "是否存在", key);
        }

        return false;
    }

    public static IDictionary<string, T> GetAll<T>(IEnumerable<string> keys) where T : class
    {
        if (keys == null)
        {
            return null;
        }

        keys = keys.Where(k => !string.IsNullOrWhiteSpace(k));

        if (keys.Count() == 1)
        {
            T obj = Get<T>(keys.Single());

            if (obj != null)
            {
                return new Dictionary<string, T>() { { keys.Single(), obj } };
            }

            return null;
        }

        if (!keys.Any())
        {
            return null;
        }

        IDictionary<string, T> dict = null;

        if (pool != null)
        {
            keys.Select(s => new
            {
                Index = Math.Abs(s.GetHashCode()) % readHosts.Length,
                KeyName = s
            })
            .GroupBy(p => p.Index)
            .Select(g =>
            {
                try
                {
                    using (var r = pool.GetClient(g.Key))
                    {
                        if (r != null)
                        {
                            r.SendTimeout = 1000;
                            return r.GetAll<T>(g.Select(p => p.KeyName));
                        }
                    }
                }
                catch (Exception ex)
                {
                    string msg = string.Format("{0}:{1}發生異常!{2}", "cache", "獲取", keys.Aggregate((a, b) => a + "," + b));
                }
                return null;
            })
            .Where(x => x != null)
            .ForEach(d =>
            {
                d.ForEach(x =>
                {
                    if (dict == null || !dict.Keys.Contains(x.Key))
                    {
                        if (dict == null)
                        {
                            dict = new Dictionary<string, T>();
                        }
                        dict.Add(x);
                    }
                });
            });
        }

        IEnumerable<Tuple<string, T>> result = null;

        if (dict != null)
        {
            result = dict.Select(d => new Tuple<string, T>(d.Key, d.Value));
        }
        else
        {
            result = keys.Select(key => new Tuple<string, T>(key, Get<T>(key)));
        }

        return result
            .Select(d => new Tuple<string[], T>(d.Item1.Split('_'), d.Item2))
            .Where(d => d.Item1.Length >= 2)
            .ToDictionary(x => x.Item1[1], x => x.Item2);
    }

}


至此,基本的Redis主從複製就簡單實現了,歡迎拍磚~


請喊我大龍哥最後編輯於:3年前

內容均為作者獨立觀點,不代表八零IT人立場,如涉及侵權,請及時告知。

相關文章