前言
前面利用《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 的方式
归根到底一张图:
Redis Client 先询问 Sentinels,Sentinel 返回 Master (IP,Port)
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 像便秘一样最近才关闭.
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 地址:
总结输入
本文记录两个内容:
Redis-Sentinel 在 Docker 环境因 NAT,Forward_Port 触发的问题, 以及 Redis 官方给出的方案
- C# 两个 Redis 客户端如何感知 Redis-Sentinel 的 Master 查询节点
- https://redis.io/topics/sentinel
- https://redis.io/topics/replication
来源: https://www.cnblogs.com/JulianHuang/p/12687090.html