当前位置: 首页 > 工具软件 > ServiceStack > 使用案例 >

C# 使用 ServiceStack.Redis链接哨兵redis服务端

南门欣怡
2023-12-01

Redis 哨兵
Redis Sentinel是运行高可用性 Redis 配置的官方建议,通过运行许多额外的 redis sentinel 进程来主动监控现有的 Redis 主从实例,以确保它们都按预期工作。如果通过共识确定主服务器不再可用,它将自动故障转移并将其中一个复制的从服务器提升为新的主服务器。哨兵还维护可用 redis 实例的权威列表,为客户端提供中心存储库以发现他们可以连接的可用实例。

对 Redis Sentinel 的支持可用于RedisSentinel侦听可用 Sentinel 的类,以获取其可用的主、从和其他哨兵 redis 实例的列表,用于配置和维护 Redis 客户端管理器,并在报告它们时启动任何故障转移。

用法#
要使用新的 Sentinel 支持,而不是使用主实例和从属实例的连接字符串填充 Redis 客户端管理器,您将创建一个RedisSentinel配置有正在运行的 Redis Sentinel 的连接字符串的单个实例:

var sentinelHosts = new[]{ “sentinel1”, “sentinel2:6390”, “sentinel3” };
var sentinel = new RedisSentinel(sentinelHosts, masterName: “mymaster”);
这显示了配置RedisSentinel引用 3 个哨兵主机的典型示例(即,可以在任何节点故障后幸存的高可用性设置的最小数量)。它还配置为查看mymaster配置集(默认主组)。

信息

Redis Sentinels 可以监控多个主/从组,每个组都有不同的主组名称。

哨兵的默认端口是26379(未指定时),由于 RedisSentinel 可以自动发现其他哨兵,所需的最低配置只是:

var sentinel = new RedisSentinel(“sentinel1”);
信息

可以禁用扫描和自动发现其他哨兵ScanForOtherSentinels=false

开始监控哨兵#
配置完成后,您可以开始监控 Redis Sentinel 服务器并使用以下命令访问预配置的客户端管理器:

IRedisClientsManager redisManager = sentinel.Start();
和以前一样,可以在您首选的 IOC 中注册为单例实例:

container.Register(c => sentinel.Start());
高级哨兵配置#
RedisSentinel 默认管理一个配置的PooledRedisClientManager实例,该实例解析主 Redis 客户端的读/写GetClient()和从属的只读GetReadOnlyClient()API。

这可以更改为使用较新RedisManagerPool的:

sentinel.RedisManagerFactory = (master,slaves) => new RedisManagerPool(master);
自定义 Redis 连接字符串#
RedisSentinel 配置的主机仅适用于该 Sentinel 主机,您仍然可以使用Redis 连接字符串的灵活性通过指定自定义配置单个 Redis 客户端HostFilter:

sentinel.HostFilter = host => “{0}?db=1&RetryTimeout=5000”.Fmt(host);
这将返回配置为使用数据库 1 和 5 秒重试超时的客户端(用于新的自动重试功能)。

其他 RedisSentinel 配置#
虽然上面涵盖了通常使用的流行 Sentinel 配置,但几乎每个RedisSentinel行为方面都可以使用以下配置进行自定义:

OnSentinelMessageReceived 当 Sentinel 工作人员收到来自 Sentinel 订阅的消息时触发
故障转移 当 Sentinel 将 Redis 客户端管理器故障转移到新的主服务器时触发
OnWorkerError Redis Sentinel Worker 连接失败时触发
IP地址映射 将 Sentinels 返回的内部 redis 主机 IP 映射到其外部 IP
扫描其他哨兵 是否定期扫描其他哨兵主机(默认为true)
RefreshSentinelHostsAfter 扫描其他哨兵主机的时间间隔(默认 10 分钟)
WaitBetweenFailedHosts 连接下一个redis实例失败后等待多长时间(默认250ms)
MaxWaitBetweenFailedHosts 投掷前重试连接主机多长时间(默认 60 秒)
WaitBeforeForcingMasterFailover 连续尝试强制故障转移失败后多长时间(默认 60 秒)
ResetWhenSubjectivelyDown 当 Sentinel 报告 redis 主观关闭时重置客户端(默认 true)
ResetWhenObjectivelyDown 当 Sentinel 报告 redis 客观关闭时重置客户端(默认为 true)
SentinelWorkerConnectTimeoutMs Sentinel Worker 的最大连接时间(默认 100 毫秒)
SentinelWorkerSendTimeoutMs Sentinel Worker 的最大 TCP 套接字发送时间(默认 100 毫秒)
SentinelWorkerReceiveTimeoutMs Sentinel Worker 的最大 TCP 套接字接收时间(默认 100 毫秒)
配置 Redis Sentinel 服务器#
即时 Redis 设置

redis config 项目简化了设置和运行高可用性的多节点 Redis Sentinel 配置,包括启动/停止脚本,用于在单个(或多个)Windows、OSX 或 Linux 服务器上即时设置最小的高可用性 Redis Sentinel 配置。这种单服务器/多进程配置非常适合在单个开发工作站或远程服务器上设置工作哨兵配置。

redis-config 存储库还包括MS OpenTech Windows redis 二进制文件,不需要任何软件安装。

视窗使用#
要运行包含的 Sentinel 配置,请在要运行它的服务器上克隆 redis-config 存储库:

git clone https://github.com/ServiceStack/redis-config.git
然后使用以下命令启动 1x Master、2x Slaves 和 3x Sentinel redis-servers:

cd redis-config\sentinel3\windows
start-all.cmd
关闭启动实例:

stop-all.cmd
如果您在开发工作站上本地运行 redis 进程,则连接到正在运行的实例的最小配置只是:

var sentinel = new RedisSentinel(“127.0.0.1:26380”);
container.Register(c => sentinel.Start());
本地主机与网络 IP#
sentinel 配置假定所有 redis 实例都在127.0.0.1本地运行。如果您改为在希望网络中的所有开发人员能够访问的远程服务器上运行它,则需要更改*.conf文件中的 IP 地址以使用服务器网络 IP。否则,您可以保留默认值并使用RedisSentinelIP 地址映射功能将 localhost IP 透明地映射到网络上的每台 PC 可以连接到的网络 IP。

例如,如果它在具有10.0.0.9网络 IP 的远程服务器上运行,它可以配置为:

var sentinel = new RedisSentinel(“10.0.0.9:26380”) {
IpAddressMap = {
{“127.0.0.1”, “10.0.0.9”},
}
};
container.Register(c => sentinel.Start());
Google Cloud -点击部署 Redis#
我们发现可以立即设置多节点 Redis Sentinel 配置的最简单的云服务是使用 Google Cloud 的click to deploy Redis 功能,该功能可从 Google Cloud Console 的Deploy & Manage下获得:

单击部署按钮将让您配置要部署 Redis 虚拟机的类型、大小和位置。有关在 Google Cloud 上设置和检查高可用性 redis 配置的演练,请参阅完整的 Click to Deploy Redis 指南。

改为使用 RedisManagerPool#
默认情况下,RedisSentinel 使用 a PooledRedisClientManager,这可以更改为使用较新RedisManagerPool的:

sentinel.RedisManagerFactory = (master,replicas) => new RedisManagerPool(master);
开始监控哨兵#
配置完成后,您可以开始监控 Redis Sentinel 服务器并使用以下命令访问预配置的客户端管理器:

IRedisClientsManager redisManager = sentinel.Start();
和以前一样,可以在您首选的 IOC 中注册为单例实例:

container.Register(c => sentinel.Start());
配置 Redis Sentinel 服务器#
即时 Redis 设置

请参阅redis 配置项目以快速设置最小的高可用性 Redis Sentinel 配置,包括用于在单个(或多个)Windows、OSX 或 Linux 服务器上即时运行多个 redis 实例的启动/停止脚本。

Redis 统计#
您可以使用RedisStats该类对正在运行的实例进行可见性和内省。Redis Stats wiki列出了可用的统计信息。

自动重试#
为了提高客户端连接的弹性,RedisClient将在从10ms到10000msRetryTimeout的指数退避中透明地重试由于 Socket 和 I/O 异常而失败的 Redis 操作。这些默认值可以通过以下方式进行调整:

RedisConfig.DefaultRetryTimeout = 10000;
RedisConfig.BackOffMultiplier = 10;

redis 的 操作hash表通用方法。 能够比较方便从json转化成对象:

using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Autobio.LAS.Infrastructure.OrleansSilo
{
    internal class SlioRedisHelper
    {
        public static List<T> HashGet<T>(string connectionString, string key, int dbNum = 0)
        {
            return Do(database =>
            {
                var r = database.HashGetAll(key);
                RedisValue[] aa = r.Select(rv => rv.Value).ToArray();
                if (aa == null || aa.Length <= 0)
                {
                    return new();
                }
                return ConvertList<T>(aa);
            }, connectionString, dbNum) ?? new List<T>();
        }

        public static List<HashEntry> HashGet(string connectionString, string key, int dbNum = 0)
        {
            return Do(database =>
            {
                return database.HashGetAll(key).ToList();
            }, connectionString, dbNum) ?? new List<HashEntry>();
        }

        public static bool HashSet(string connectionString, string key, List<HashEntry>  redisValues, int dbNum = 0)
        {
            return Do(database =>
            {
                database.HashSet(key, redisValues.ToArray());
                return true; 
            }, connectionString, dbNum) ;
        }

        public static bool SetGet<T>(string connectionString, string key, List<T> entityList, int dbNum = 0)
        {
            return false;
        }

        private static T? Do<T>(Func<IDatabase, T> func, string connectionString, int dbNum = 0)
        {
            try
            {
                using var connectionMultiplexer = ConnectionMultiplexer.Connect(connectionString);
                var database = connectionMultiplexer.GetDatabase(dbNum);
                return func(database);
            }
            catch
            {
                return default(T);
            }
        }

        private static List<T> ConvertList<T>(RedisValue[] values)
        {
            List<T> result = new ();
            if (!(values != null && values.Length > 0))
            {
                return result;
            }
            foreach (var item in values)
            {
                try
                {
                    var model = JsonConvert.DeserializeObject<T>(item);
                    if (model != null)
                    {
                        result.Add(model);
                    }
                }
                catch
                {
                }
            }
            return result;
        }
    }
}

 类似资料: