1. 程式人生 > >C#兩大知名Redis客戶端連線哨兵叢集的姿勢

C#兩大知名Redis客戶端連線哨兵叢集的姿勢

前言

前面利用《Docker-Compose搭建Redis高可用哨兵叢集》,
我們的思路是將Redis、Sentinel、Redis Client App連結到同一個網橋網路,這個網橋內的Redis Client App就可以使用ContainerIP訪問網橋內任意redis節點。

同一網橋網路訪問規避了Docker上發生的NAT,埠對映的複雜性, 但實際上並不是常規實踐

Redis Client App獨立組網遇到的問題

很多時候,Redis-Sentinel與Redis Client App獨立組網,涉及Docker宿主機NAT轉換和 Port對映。

Sentinel,Docker或其他形式的網路地址轉換或埠對映應謹慎混合。
我實際測試發現,如果將前文Sentinel.conf中Master(ContainerIP,Port) 換成(宿主機IP,對映Port),
確實會引起混亂,無法找到正確的Slaves, 無法正常故障轉移。

為了解決Redis-Sentinel在Docker環境下因為NAT,Forward Port導致的無法正確獲知Slaves和正確故障轉移的問題。
Redis3.2之後可以強制讓Slave宣告自己的(IP,Port);強制讓Sentinel宣告自己的(IP,Port)

 # since Redis 3.2.2, to force a replica to announce an arbitrary pair of IP and port to the master. The two configurations directives to use are:
replica-announce-ip <ip>
replica-announce-port <port>

上述配置可以寫在Docker Command引數指定或通過Volume redis.conf 載入進redis容器

# you can use the following two Sentinel configuration directives in order to force Sentinel to announce a specific set of IP and port:

sentinel announce-ip <ip>
sentinel announce-port <port>

sentinel.conf的配置只能通過Config載入進sentinel容器。

通過明牌方式通知 所有互動物件,redis例項就是在這個(IP,Port)上發生了NAT轉化,Port對映,上述搭建Docker搭建Redis-sentinel才是常規實踐。

C#兩大客戶端訪問Redis-Sentinel的方式

歸根到底一張圖:

  1. Redis Client先詢問Sentinels,Sentinel返回Master (IP,Port)
  2. Redis Client再與以上Master (IP,Port)建立連線

Redis-Sentinel

這裡我們採用Docker-compose在單機上部署了Redis-Sentinel叢集,
1Master- 2 Slave- 3Sentinel,
分別佔據宿主機6380、6381、6382、 26379、26380、26381埠.

CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS                  PORTS                                NAMES
484da8d832f1        redis                          "docker-entrypoint.s…"   2 hours ago         Up 2 hours              6379/tcp, 0.0.0.0:26380->26379/tcp   redis-sentinel-2
50599c15adba        redis                          "docker-entrypoint.s…"   2 hours ago         Up 2 hours              6379/tcp, 0.0.0.0:26379->26379/tcp   redis-sentinel-1
51ce90cc52d7        redis                          "docker-entrypoint.s…"   2 hours ago         Up 2 hours              6379/tcp, 0.0.0.0:26381->26379/tcp   redis-sentinel-3
d58d6973de28        redis                          "docker-entrypoint.s…"   2 hours ago         Up 2 hours              0.0.0.0:6381->6379/tcp               redis-slave-1
b88bd85ac109        redis                          "docker-entrypoint.s…"   2 hours ago         Up 8 seconds            0.0.0.0:6382->6379/tcp               redis-slave-2
3dc26c01a90d        redis                          "docker-entrypoint.s…"   2 hours ago         Up About an hour        0.0.0.0:6380->6379/tcp               redis-master


進入任意Sentinel節點,使用sentinel master mymaster確認叢集資訊

存在測試鍵值: testKey:hello Redis-sentinel!

StackExchange.Redis & CSRedisCore連線Redis哨兵

老牌StackExchange.Redis 今年才真正支援Sentinel, Github上有關Sentinel的Issue、PR歷時久遠,PR像便祕一樣最近才關閉。
https://github.com/StackExchange/StackExchange.Redis/pull/692#issuecomment-375298108
https://github.com/StackExchange/StackExchange.Redis/pull/1067

CSRedisCore得到真傳,很早就支援了,而且程式設計寫法更簡單,清晰。

話不多說:

using StackExchange.Redis;
using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var sw = new Stopwatch();
            sw.Start();
            UseStackExchangeRedis();  
            sw.Stop();
            Console.WriteLine("連線+查詢測試key,耗時"+sw.ElapsedMilliseconds);

            sw.Reset();
            sw.Start();
            UseCSRedisCore();
            sw.Stop();
            Console.WriteLine("連線+查詢測試key,耗時" + sw.ElapsedMilliseconds);
            Console.ReadKey();
        }
       // StackExchange.Reids連線Redis-Sentinel
        public  static void  UseStackExchangeRedis()
        {
            ConfigurationOptions sentinelOptions = new ConfigurationOptions();
            sentinelOptions.EndPoints.Add("180.76.*.*", 26379);
            sentinelOptions.EndPoints.Add("180.76.*.*", 26380);
            sentinelOptions.EndPoints.Add("180.76.*.*", 26381);
            sentinelOptions.TieBreaker = "";
            sentinelOptions.CommandMap = CommandMap.Sentinel;
            sentinelOptions.AbortOnConnectFail = false;
            // Connect!
            ConnectionMultiplexer sentinelConnection = ConnectionMultiplexer.Connect(sentinelOptions);

            // Get a connection to the master
            ConfigurationOptions redisServiceOptions = new ConfigurationOptions();
            redisServiceOptions.ServiceName = "mymaster1";   //master名稱
            redisServiceOptions.Password = "redis_pwd";     //master訪問密碼
            redisServiceOptions.AbortOnConnectFail = true;
            ConnectionMultiplexer masterConnection = sentinelConnection.GetSentinelMasterConnection(redisServiceOptions);

            var db  = masterConnection.GetDatabase();
            var value= db.StringGet("testKey");
            Console.WriteLine($"[Use StackExchange-Redis] The remote redis-sentinel test key value:{value}");
        }

    // CSRedisCore連線Redis-Sentinel
        public static  void UseCSRedisCore()
        {
            var csredis = new CSRedis.CSRedisClient("mymaster1,password=redis_pwd",
                    new[] { "180.76.*.*:26379", "180.76.*.*:26380", "180.76.*.*:26381" });
            var value = csredis.Get("testKey");
            Console.WriteLine($"[Use CSRedisCore] The remote redis-sentinel test key value:{value}");
        }
    }
}

執行輸出:

本文長話短說,快速介紹兩塊C#常見的Redis客戶端連線Redis哨兵叢集的方式,各有千秋。
StackExchange.Redis更能體現連線的實質過程: 先查詢,再連線。
CSRedisCore 小白寫法,無感知。

Gthub地址:https://github.com/zaozaoniao/Redis-sentinel-with-docker-compose

總結輸入

本文記錄兩個內容:

  1. Redis-Sentinel在Docker環境因NAT,Forward_Port觸發的問題, 以及Redis官方給出的方案
  2. C#兩個Redis客戶端如何感知Redis-Sentinel的Master查詢節點
  • https://redis.io/topics/sentinel
  • https://redis.io/topics/replication