CSRedis用于Redis哨兵模式,NetCore

十年河东,十年河西,莫欺少年穷

学无止境,精益求精

上一节通过两台windowsServer服务器部署了Redis的哨兵模式,详情参考:两台windowserver服务器配置Redis哨兵集群----一主二从

redis通过主从复制来实现高可用,但是发生故障时需要人工进行主从切换,效率低下。哨兵机制实现了redis主从的自动切换,提高了redis集群的可用性,提高了redis集群的故障转移效率。 我们可以看到哨兵机制是有缺点的:   

1.主从服务器的数据要经常进行主从复制,这样造成性能下降。   

2.当主服务器宕机后,从服务器切换成主服务器的那段时间,服务是不能用的。

3.为了保证redis的真真的高可用官方推荐使用redis-cluster集群。 一般的项目我们采用redis的哨兵模式架构(推荐采用一主二从三哨兵)就可以满足业务要求了

 1、新建控制台应用程序

引用如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
      
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CSRedisCore" Version="3.8.803" />
      <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.9" />
      <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.9" />
      <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="3.1.9" />
      <PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.11" />
      <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.9" />
      <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
      <PackageReference Include="NLog.Extensions.Logging" Version="5.0.4" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Common\Common.csproj" />
  </ItemGroup>
    <ItemGroup>
        <None Update="appsettings.json">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </None>
        <None Update="nlog.config">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </None>
    </ItemGroup>
</Project>
View Code

代码如下:

using Common;
using CSRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace CSRedisCoreTEST
{
    class Program
    {
        static void Main(string[] args)
        {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json", true, true);
            var ConfigRoot = builder.Build();//根节点
            IServiceCollection Services = new ServiceCollection();
         
            Services.AddLogging(log => { log.AddConsole(); log.AddNLog(); log.SetMinimumLevel(LogLevel.Error); });
            string sentinelConnectString = ConfigRoot.GetSection("CsRedisConfig:SentinelConnectString").Value;
 
            string[] sentinelValues = ConfigRoot.GetSection("CsRedisConfig:Sentinel").Get<string[]>();

            //  创建redis哨兵访问类(Redis Sentinel)
            var csredis = new CSRedis.CSRedisClient(connectionString: sentinelConnectString, sentinels: sentinelValues);

            // 初始化 RedisHelper
            RedisHelper.Initialization(csredis);

            // 将csredis实例注册到管道中
            Services.AddSingleton<CSRedis.CSRedisClient>(RedisHelper.Instance);
            Services.AddScoped<RdsTestService>();
            using (ServiceProvider provider = Services.BuildServiceProvider())
            {
                var der = provider.GetService<RdsTestService>();
                der.TestSet(); der.TestGet();
            }  
            CreateHostBuilder(args).Run();
        }

        public static IHost CreateHostBuilder(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args)
                 .ConfigureServices((hostContext, services) =>
                 {
                     services.AddHostedService<BeatService>();

                 }).UseWindowsService();
            var host = builder.Build();

            return host;
        }
    }

    public class RdsTestService
    {
        private readonly CSRedis.CSRedisClient cSRedisClient;

        public RdsTestService(CSRedis.CSRedisClient cSRedisClient)
        {
            this.cSRedisClient = cSRedisClient;
        } 

        public void TestSet()
        {
            this.cSRedisClient.Set("testDF","我是一只小小小奥鸟",TimeSpan.FromMinutes(4));
        }
        public void TestGet()
        {
            var result = this.cSRedisClient.Get("testDF"); 
        }

    }

    public class BeatService : BackgroundService
    {
        private readonly ILogger<BeatService> _logger; 
        public BeatService( 
        ILogger<BeatService> logger)
        { 
            _logger = logger;
        }
        public override Task StartAsync(CancellationToken cancellationToken)
        {
            return base.StartAsync(cancellationToken);
        }

        /// <summary>
        ///  每一秒执行一次
        /// </summary>
        /// <param name="stoppingToken"></param>
        /// <returns></returns>
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            { 
                var result = CSRedisHelper.GetInstance().Get("testDF");
                Console.WriteLine("BackgroundService通过单例模式获取到的值为:" + result);
                await Task.Delay(1000, stoppingToken);
            }
               
        } 
    }
}
View Code

配置文件如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "CsRedisConfig": {
    "SentinelConnectString": "mymaster,defaultDatabase=3,poolsize=1000",
    "Sentinel": [
      "172.27.40.27:26379",
      "172.27.40.29:26379",
      "172.27.40.29:26380"
    ]
  }
}

nlog.config如下

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Info"
      internalLogFile="logs/internal-nlog-AspNetCore.txt">

    <!-- enable asp.net core layout renderers -->
    <extensions>
        <add assembly="NLog.Web.AspNetCore"/>
    </extensions>

    <!-- the targets to write to -->
    <targets>
        <!-- File Target for all log messages with basic details -->
        <target xsi:type="File" name="allfile" fileName="logs/log-${shortdate}.log"
                layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" archiveAboveSize="10000" maxArchiveFiles="3"/>

        <!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
        <target xsi:type="File" name="ownFile-web" fileName="logs/nlog-AspNetCore-own-${shortdate}.log"
                layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}" archiveAboveSize="10000" maxArchiveFiles="3" />

        <!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
        <target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
    </targets>

    <!-- rules to map from logger name to target -->
    <rules>
        <!--All logs, including from Microsoft-->
        <logger name="*" minlevel="Trace" writeTo="allfile" />

        <!--Output hosting lifetime messages to console target for faster startup detection -->
        <logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />

        <!--Skip non-critical Microsoft logs and so log only own logs (BlackHole) -->
        <logger name="Microsoft.*" maxlevel="Info" final="true" />
        <logger name="System.Net.Http.*" maxlevel="Info" final="true" />

        <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
    </rules>
</nlog>
View Code

 

2、新建公共类库,用于Csredis单例模式

引用如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CSRedisCore" Version="3.8.803" />
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.9" />
      <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
      <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.9" />
  </ItemGroup>

</Project>

公共类如下:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using System;
using CSRedis;  

namespace Common
{
    public class ConfigCommon
    {
        public static IConfiguration Configuration { get; set; }
        static ConfigCommon()
        {
            //#if DEBUG
            //            Configuration = new ConfigurationBuilder()
            //           .Add(new JsonConfigurationSource { Path = "appsettings.Development.json", ReloadOnChange = true })
            //           .Build();
            //#else     
            //            Configuration = new ConfigurationBuilder()
            //            .Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true })
            //            .Build();
            //#endif

            Configuration = new ConfigurationBuilder()
            .Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true })
            .Build();
        }

        public static string GetConfig(string key, string defaultValue = "")
        {
            string settingValue = Configuration[key];
            return string.IsNullOrEmpty(settingValue) ? defaultValue : settingValue;
        }

    } 
    /// <summary>
    /// 单例模式的实现
    /// </summary>
    public class CSRedisHelper
    {
        // 定义一个静态变量来保存类的实例
        private static CSRedisClient Instance;

        // 定义一个标识确保线程同步
        private static readonly object locker = new object();

        // 定义私有构造函数,使外界不能创建该类实例
        private CSRedisHelper()
        {
        }

        /// <summary>
        /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
        /// </summary>
        /// <returns></returns>
        public static CSRedisClient GetInstance()
        {
            // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
            // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
            // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
            // 双重锁定只需要一句判断就可以了
            if (Instance == null)
            {
                lock (locker)
                {
                    // 如果类的实例不存在则创建,否则直接返回
                    if (Instance == null)
                    {
                        Console.WriteLine(DateTime.Now.ToString("HHmmss") + "开始获取实例");
                        string sentinelConnectString = ConfigCommon.GetConfig("CsRedisConfig:SentinelConnectString");

                        string[] sentinelValues = ConfigCommon.Configuration.GetSection("CsRedisConfig:Sentinel").Get<string[]>();

                        //  创建redis哨兵访问类(Redis Sentinel)
                        Instance = new CSRedis.CSRedisClient(connectionString: sentinelConnectString, sentinels: sentinelValues);

                        // 初始化 RedisHelper
                        RedisHelper.Initialization(Instance);
                    }
                }
            }
            return Instance;
        }
    }
}
View Code

 

posted @ 2024-06-04 14:10  天才卧龙  阅读(29)  评论(0编辑  收藏  举报