• Asp .Net Core 系列:集成 Consul 实现 服务注册与健康检查


    什么是 Consul?

    官网:https://www.consul.io/

    Consul 是一款开源的服务发现和配置管理工具,它能够监控应用程序和服务之间的通信,并提供了一组 API 和 Web UI,用于管理服务和配置。

    Consul 是分布式的、高可用的、可横向扩展的,具备以下特性:

    1. 服务发现:Consul 通过 DNS 或者 HTTP 接口使服务注册和服务发现变得很容易,一些外部服务,例如 saas 提供的也可以一样注册。
    2. 健康检查:健康检测使 Consul 可以快速的告警在集群中的操作。和服务发现的集成,可以防止服务转发到故障的服务上面。
    3. 键/值存储:一个用来存储动态配置的系统。提供简单的 HTTP 接口,可以在任何地方操作。
    4. 多数据中心:无需复杂的配置,即可支持任意数量的区域。

    Consul 的优势:

    1. 使用 Raft 算法来保证一致性,比复杂的 Paxos 算法更直接。
    2. 支持多数据中心,内外网的服务采用不同的端口进行监听。多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟、分片等情况等。
    3. 支持健康检查。
    4. 支持 http 和 dns 协议接口。
    5. 官方提供 web 管理界面。
    6. 安装和部署简单,使用起来也较为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持 Linux、windows 和 Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合 。

    安装和运行 Consul

    下载地址:https://developer.hashicorp.com/consul/install?product_intent=consul#Windows

    运行 consul,指定为开发环境

    consul agent -dev
    

    web 界面:http://localhost:8500/ui

    Asp .Net Core 如何集成 Consul 实现服务注册和健康检查

    要在 ASP.NET Core 应用程序中集成 Consul 实现服务注册和服务发现,可以按照以下步骤进行操作:

    1. 安装 Consul 客户端 SDK 和 Asp .Net Core 扩展包

    首先,需要在 ASP.NET Core 应用程序中安装 Consul 客户端 SDK。可以通过 NuGet 包管理器来安装,在包管理器控制台中输入以下命令:

    Install-Package Consul
    Install-Package Consul.AspNetCore
    
    1. 配置服务注册
      在应用程序启动时,需要将服务注册到 Consul 中。这通常在 Startup 类的 ConfigureServices 方法中完成。首先,示例代码如下:
            /// 
            /// 向容器中添加Consul必要的依赖注入
            /// 
            /// 
            /// 
            public static IServiceCollection AddMCodeConsul(this IServiceCollection services)
            {
                var configuration = services.BuildServiceProvider().GetRequiredService();
                // 配置consul服务注册信息
                var consulOptions = configuration.GetSection("Consul").Get();
                // 通过consul提供的注入方式注册consulClient
                services.AddConsul(options => options.Address = new Uri($"http://{consulOptions.ConsulIP}:{consulOptions.ConsulPort}"));
    
                // 通过consul提供的注入方式进行服务注册
                var httpCheck = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                    Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔,或者称为心跳间隔
                    HTTP = $"http://{consulOptions.IP}:{consulOptions.Port}/health",//健康检查地址
                    Timeout = TimeSpan.FromSeconds(10)
                };
    
                // Register service with consul
                services.AddConsulServiceRegistration(options =>
                {
                    options.Checks = new[] { httpCheck };
                    options.ID = Guid.NewGuid().ToString();
                    options.Name = consulOptions.ServiceName;
                    options.Address = consulOptions.IP;
                    options.Port = consulOptions.Port;
                    options.Meta = new Dictionary<string, string>() { { "Weight", consulOptions.Weight.HasValue ? consulOptions.Weight.Value.ToString() : "1" } };
                    options.Tags = new[] { $"urlprefix-/{consulOptions.ServiceName}" }; //添加
                });
    
                return services;
            }
    

    ConsulOptions 配置类

        public class ConsulOptions
        {
            /// 
            /// 当前应用IP
            /// 
            public string IP { get; set; }
    
            /// 
            /// 当前应用端口
            /// 
            public int Port { get; set; }
    
            /// 
            /// 当前服务名称
            /// 
            public string ServiceName { get; set; }
    
            /// 
            /// Consul集群IP
            /// 
            public string ConsulIP { get; set; }
    
            /// 
            /// Consul集群端口
            /// 
            public int ConsulPort { get; set; }
    
            /// 
            /// 权重
            /// 
            public int? Weight { get; set; }
        }
    

    appsettings.json

    {
        "Consul": {
            "ConsulIP": "127.0.0.1",
            "ConsulPort": "8500",
            "ServiceName": "ConsulDemoService",
            "Ip": "localhost",
            "Port": "5014",
            "Weight": 1
        }
    }
    

    Consul.AspNetCore 中的 AddConsul 和 AddConsulServiceRegistration 方法 究竟做了什么?

    AddConsul 方法

    可以看到通过 ConsulClientFactory 类创建和配置 Consul 的客户端实例,ConsulClientFactory 通过 IOptionsMonitor 读取应用程序的配置更改
    IOptionsMonitor: 是 ASP.NET Core 的一个接口,它提供了一种方式来观察和监视应用程序的配置更改。通过实现 IOptionsMonitor 接口,你可以创建自定义的配置监视器,以便在配置更改时自动更新应用程序的设置

    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddConsul(this IServiceCollection services)
        {
            return services.AddConsul(delegate
            {
            });
        }
    
        public static IServiceCollection AddConsul(this IServiceCollection services, Action configure)
        {
            return services.AddConsul(Options.DefaultName, configure);
        }
    
        public static IServiceCollection AddConsul(this IServiceCollection services, string name, Action configure)
        {
            services.Configure(name, configure);
            services.TryAddSingleton();
            services.TryAddSingleton((IServiceProvider sp) => sp.GetRequiredService().CreateClient(name));
            return services;
        }
        。。。
    }
    
    public class ConsulClientFactory : IConsulClientFactory
    {
        private readonly IOptionsMonitor _optionsMonitor;
    
        public ConsulClientFactory(IOptionsMonitor optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public IConsulClient CreateClient()
        {
            return CreateClient(Options.DefaultName);
        }
    
        public IConsulClient CreateClient(string name)
        {
            return new ConsulClient(_optionsMonitor.Get(name));
        }
    }
    

    AddConsulServiceRegistration 方法

    可以看出使用 AgentServiceRegistrationHostedService 类定义应用程序的后台服务,用于注册和取消注册 Consul 实例

    public static class ServiceCollectionExtensions
    {
       public static IServiceCollection AddConsulServiceRegistration(this IServiceCollection services, Action configure)
       {
           AgentServiceRegistration agentServiceRegistration = new AgentServiceRegistration();
           configure(agentServiceRegistration);
           return services.AddSingleton(agentServiceRegistration).AddHostedService();
       }
    }
    
    public class AgentServiceRegistrationHostedService : IHostedService
    {
       private readonly IConsulClient _consulClient;
    
       private readonly AgentServiceRegistration _serviceRegistration;
    
       public AgentServiceRegistrationHostedService(IConsulClient consulClient, AgentServiceRegistration serviceRegistration)
       {
           _consulClient = consulClient;
           _serviceRegistration = serviceRegistration;
       }
    
       public Task StartAsync(CancellationToken cancellationToken)
       {
           return _consulClient.Agent.ServiceRegister(_serviceRegistration, cancellationToken);
       }
    
       public Task StopAsync(CancellationToken cancellationToken)
       {
           return _consulClient.Agent.ServiceDeregister(_serviceRegistration.ID, cancellationToken);
       }
    }
    
    

    配置 Consul 检查服务

            /// 
            /// 配置Consul检查服务
            /// 
            /// 
            /// 
            public static IApplicationBuilder UseConsulCheckService(this IApplicationBuilder app)
            {
                app.Map("/health", app =>
                {
                    app.Run(async context =>
                    {
                        await Task.Run(() => context.Response.StatusCode = 200);
                    });
                });
    
                return app;
            }
    

    封装成扩展

            /// 
            /// 向容器中添加Consul必要的依赖注入
            /// 
            /// 
            /// 
            /// 
            public static IServiceCollection AddMCodeConsul(this IServiceCollection services)
            {
                var configuration = services.BuildServiceProvider().GetRequiredService();
                // 配置consul服务注册信息
                var consulOptions = configuration.GetSection("Consul").Get();
                // 通过consul提供的注入方式注册consulClient
                services.AddConsul(options => options.Address = new Uri($"http://{consulOptions.ConsulIP}:{consulOptions.ConsulPort}"));
    
                // 通过consul提供的注入方式进行服务注册
                var httpCheck = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                    Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔,或者称为心跳间隔
                    HTTP = $"http://{consulOptions.IP}:{consulOptions.Port}/health",//健康检查地址
                    Timeout = TimeSpan.FromSeconds(10)
                };
    
                // Register service with consul
                services.AddConsulServiceRegistration(options =>
                {
                    options.Checks = new[] { httpCheck };
                    options.ID = Guid.NewGuid().ToString();
                    options.Name = consulOptions.ServiceName;
                    options.Address = consulOptions.IP;
                    options.Port = consulOptions.Port;
                    options.Meta = new Dictionary<string, string>() { { "Weight", consulOptions.Weight.HasValue ? consulOptions.Weight.Value.ToString() : "1" } };
                    options.Tags = new[] { $"urlprefix-/{consulOptions.ServiceName}" }; //添加
                });
    
                return services;
            }
    
            /// 
            /// 配置Consul检查服务
            /// 
            /// 
            /// 
            public static IApplicationBuilder UseConsulCheckService(this IApplicationBuilder app)
            {
                app.Map("/health", app =>
                {
                    app.Run(async context =>
                    {
                        await Task.Run(() => context.Response.StatusCode = 200);
                    });
                });
    
                return app;
            }
    

    效果

    image

    image

  • 相关阅读:
    c语言的数据结构:找环状链表入口处
    Redis实现短信登入功能(一)传统的Session登入
    LeetCode_排序_快速选择_中等_462.最少移动次数使数组元素相等 II
    使用 SAP WebIDE 将 SAP UI5 应用部署到 ABAP 系统时遇到的关于传输请求的错误
    自动化办公03 用xlrd和xlwt库操作excel.xls文件(老版本)
    【iOS】实现评论区展开效果
    Android 外接设备获取驱动和获取申请权限
    Go 内存释放策略:MADV_DONTNEED 和 MADV_FREE
    k8s-服务网格实战-入门Istio
    齐博X1-栏目的终极方法get_sort
  • 原文地址:https://www.cnblogs.com/vic-tory/p/17961740