.net core consul watch 监听 服务的变化 获取刷新的节点 做到实时获取最新节点

在 使用consul 时 发现每次 获取节点 需要连接 花费时间 较长 所以优化 放到 内存中 放到内存中比 redis 缓存中要快很多 

那么问题来了 多个服务 怎么做到 统一 更新最新节点  用到Cap 事件总线的 发布订阅的方式 让 各个节点 收到刷新消息

原理 就是 利用 cap 发布订阅的功能 做到实时更新 consul 最新的节点信息

参考 ASP.NET CORE 使用Consul实现服务治理与健康检查(1)——概念篇 | 码农家园 (codenong.com)

通知机制——Consul Watch

Consul 可以通过配置 Agent 对以下类型的数据进行监控,并且同样受反熵机制的影响,如果想监控集群下所有服务,那么需要将监控配置放在服务端:

  • key – 监视指定K/V键值对
  • keyprefix – Watch a prefix in the KV store
  • services – 监视服务列表
  • nodes – 监控节点列表
  • service – 监视服务实例
  • checks- 监视健康检查的值
  • event – 监视用户事件

Consul 主要提供2种通知方式:

  1. script:当发生变化时执行一段脚本(可以是放在服务器中的任何可执行脚本,例如 py sh 等)
  2. HTTP endpoint:当发生变化时请求配置的http地址

例如在 Consul 配置文件创建 watch.json ,重启 Consul 后生效

 

 

 

consul 新建文件

 

 内容为

{
    "watches": [{
        "type": "services",
        "handler_type": "http",
        "http_handler_config": {
            "path": "https://localhost:5011/ConsulWatch/Refresh",
            "method": "GET",
            "header": {
                "x-foo": ["bar", "baz"]
            },
            "timeout": "10s",
            "tls_skip_verify": true
        }
    }]
}

启动 命令为 注意我这个是windows下的consul

consul agent - -config-dir=config -dev

新建一个consulwatchserveice服务项目 专门监控 consul节点的变化

 主要代码如下

using AOPTest;
using DotNetCore.CAP;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace AOPConsul.Controllers
{
    [Route("ConsulWatch")]
    [ApiController]
    public class ConsulWatchController : ControllerBase
    {
        private readonly ILogger<ConsulWatchController> _logger;
        private readonly ICapPublisher _capPublisher;    
        public ConsulWatchController(
            ILogger<ConsulWatchController> logger,
            ICapPublisher capPublisher
            )
        {
            _logger = logger;
            _capPublisher = capPublisher;
        }
        [HttpGet("Refresh")]
        public async Task<IActionResult> GetAsync()
        {
            await _capPublisher.PublishAsync(CapPublishName.CAPCONSULWATCH, "");
            _logger.LogInformation("watch节点监控 发送刷新命令");
            return Ok("刷新consul节点");
        }
    }
}

其余的项目 接收cap消息

 主要代码如下

using AOPTest;
using AOPTest.Consul;
using DotNetCore.CAP;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace AOPWebApp.Controllers
{
    [Route("Watch")]
    [ApiController]
    public class WatchController : ControllerBase
    {
        private readonly ILogger<WatchController> _logger;
        private readonly ICapPublisher _capPublisher;
        private readonly ICustomConsul _customConsul;
        public WatchController(ILogger<WatchController> logger,
            ICapPublisher capPublisher,
            ICustomConsul customConsul
            )
        {
            _logger = logger;
            _capPublisher = capPublisher;
            _customConsul = customConsul;
        }
        /// <summary>
        /// consul 刷新节点
        /// </summary>
        /// <returns></returns>
        [NonAction]
        [CapSubscribe(CapPublishName.CAPCONSULWATCH)]
        public IActionResult Refresh()
        {
            _customConsul.Refresh();
            _logger.LogInformation("consul 节点刷新成功");
            return Ok();
        }
    }
}

 

consul 封装代码

namespace AOPTest.Consul
{
    public class ConsulServeiceNode
    {
        public string Url { get; set; }
    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AOPTest.Consul
{
    public interface ICustomConsul
    {
        /// <summary>
        /// 发现节点
        /// </summary>
        /// <param name="serverName">服务名称</param>
        /// <returns></returns>
        List<ConsulServeiceNode> Discovery(string serverName);

        /// <summary>
        /// 发现所有节点
        /// </summary>
        /// <returns></returns>
        List<ConsulServeiceNode> Discovery();

        /// <summary>
        /// 注销
        /// </summary>
        void Deregister();

        /// <summary>
        /// 注册
        /// </summary>
        Task RegistryAsync();
        /// <summary>
        /// 刷新
        /// </summary>
        void Refresh();
    }
}

 

using AOPTest.Consul.Options;
using Consul;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AOPTest.Consul
{
    public class CustomConsul : ICustomConsul
    {

        public static Dictionary<string, List<ConsulServeiceNode>> dicServiceNodes = new Dictionary<string, List<ConsulServeiceNode>>();
        private readonly ConsulOptions _options;
        public CustomConsul(IOptionsMonitor<ConsulOptions> options)
        {
            _options = options.CurrentValue;
        }
        /// <summary>
        /// 注销
        /// </summary>
        public async void Deregister()
        {
            using ConsulClient client = GetConsulClient();
            await client.Agent.ServiceDeregister(_options.ServiceId.ToString());
        }
        /// <summary>
        /// 发现 节点
        /// </summary>
        /// <param name="serverName">服务名称</param>
        /// <returns></returns>
        public List<ConsulServeiceNode> Discovery(string serverName)
        {
            using ConsulClient client = GetConsulClient();
            List<ConsulServeiceNode> consulServeiceNodes = new List<ConsulServeiceNode>();
            var dicServices = client.Catalog.Services().Result.Response;
            foreach (var dicService in dicServices)
            {
                string key = dicService.Key;
                //根据服务key获取相关的 生成相关的服务的节点
                List<CatalogService> catalogServices = client.Catalog.Service(serverName).Result.Response.ToList();
                consulServeiceNodes = catalogServices.Select(item => new ConsulServeiceNode { Url = $"{item.ServiceAddress}:{item.ServicePort}" }).ToList();
            }
            return consulServeiceNodes;
        }
        /// <summary>
        /// 发现所有节点
        /// </summary>
        /// <returns></returns>
        public List<ConsulServeiceNode> Discovery()
        {
            using ConsulClient client = GetConsulClient();
            List<ConsulServeiceNode> consulServeiceNodes = new List<ConsulServeiceNode>();
            var dicServices = client.Catalog.Services().Result.Response;
            foreach (var dicService in dicServices)
            {
                string key = dicService.Key;
                //根据服务key获取相关的 生成相关的服务的节点
                List<CatalogService> catalogServices = client.Catalog.Service(key).Result.Response.ToList();
                consulServeiceNodes = catalogServices.Select(item => new ConsulServeiceNode { Url = $"{item.ServiceAddress}:{item.ServicePort}" }).ToList();
            }
            return consulServeiceNodes;
        }
        /// <summary>
        /// 刷新
        /// </summary>
        public void Refresh()
        {
            //清空 字典
            dicServiceNodes.Clear();
            using ConsulClient client = GetConsulClient();
            var dicServices = client.Catalog.Services().Result.Response;
            foreach (var dicService in dicServices)
            {
                string key = dicService.Key;
                //根据服务key获取相关的 生成相关的服务的节点
                List<CatalogService> catalogServices = client.Catalog.Service(key).Result.Response.ToList();
                List<ConsulServeiceNode> consulServeiceNodes = catalogServices.Select(item => new ConsulServeiceNode { Url = $"{item.ServiceAddress}:{item.ServicePort}" }).ToList();
                //将节点存到字典中
                dicServiceNodes.Add(key, consulServeiceNodes);
            }
        }

        /// <summary>
        /// 注册
        /// </summary>
        public async Task RegistryAsync()
        {
            using ConsulClient client = GetConsulClient();
            await client.Agent.ServiceRegister(new AgentServiceRegistration
            {
                ID = _options.ServiceId.ToString() ?? Guid.NewGuid().ToString(),
                Name = _options.ConsulRegister.Name,
                Address = _options.ConsulRegister.Address,
                Port = _options.ConsulRegister.Port,
                Tags = new string[] { _options.ConsulRegister.Tag },
                Check = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(_options.ConsulRegister.DeregisterCriticalServiceAfter),//服务启动多久后注册
                    Interval = TimeSpan.FromSeconds(_options.ConsulRegister.Interval),//健康检查时间间隔
                    HTTP = _options.ConsulRegister.HealthCheckUrl,///健康检查地址
                    Timeout = TimeSpan.FromSeconds(_options.ConsulRegister.Timeout)
                }
            });
        }

        /// <summary>
        /// 获取 ConsulClient
        /// </summary>
        /// <returns></returns>
        private ConsulClient GetConsulClient()
        {
            return new ConsulClient(config =>
             {
                 config.Address = new Uri(_options.ConsulMain.Address);
                 config.Datacenter = _options.ConsulMain.Datacenter;
             });
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AOPTest.Consul.Options;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace AOPTest.Consul
{
    public static class ConsulExtend
    {
        public static IServiceCollection AddCustomConsul(this IServiceCollection services, Action<ConsulOptions> action)
        {
            ConsulOptions consulOptions = new ConsulOptions();
            services.Configure<ConsulOptions>(action);
            return services.AddTransient<ICustomConsul, CustomConsul>();
        }

        public static IServiceCollection AddDefaultCustomConsul(this IServiceCollection services,IConfiguration configuration)
        {
            services.Configure<ConsulOptions>(configuration.GetSection("ConsulOptions"));
            return services.AddTransient<ICustomConsul, CustomConsul>();
        }
    }
}

 

using Consul;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using AOPTest.Consul.Options;
namespace AOPTest.Consul
{
    public class ConsulHostServices : IHostedService
    {
        private readonly ConsulOptions _options;
        private readonly ICustomConsul _customConsul;
        public ConsulHostServices(IOptionsMonitor<ConsulOptions> options, ICustomConsul customConsul)
        {
            _options = options.CurrentValue;
            _customConsul = customConsul;
        }
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            //注册
            await _customConsul.RegistryAsync();
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            //注销
            _customConsul.Deregister();
            return Task.CompletedTask;
        }
    }
}

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AOPTest.Consul.Options
{
    public class ConsulOptions
    {
        public ConsulMain ConsulMain { get; set; }
        public ConsulRegister ConsulRegister { get; set; }

        public Guid ServiceId { get; set; }

        public ConsulOptions()
        {
            ServiceId = Guid.NewGuid();
        }
    }
    /// <summary>
    /// consul ui
    /// </summary>
    public class ConsulMain
    {
        public string Address { get; set; }
        public string Datacenter { get; set; }
    }

    /// <summary>
    /// consul 下节点 注册
    /// </summary>
    public class ConsulRegister
    {
        public string Name { get; set; }
        public string Address { get; set; }
        public int Port { get; set; }
        public string Tag { get; set; }

        public string HealthCheckUrl { get; set; }
        public int Interval { get; set; }
        public int Timeout { get; set; }
        public int DeregisterCriticalServiceAfter { get; set; }
    }
}

 

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": { "con": "server=192.168.0.192;port=3306;database=Cap1;uid=root;pwd=123456;" },
  "ConsulOptions": {
    "ConsulMain": {
      "Address": "http://localhost:8500",
      "Datacenter": "dc1"
    },
    "ConsulRegister": {
      "Name": "OrderAPI",
      "Address": "localhost",
      "Port": 5006,
      "Tag": "1",
      "HealthCheckUrl": "https://localhost:5006/HealthCheck",
      "Interval": 10,
      "Timeout": 5,
      "DeregisterCriticalServiceAfter": 20
    }
  },

  "CapOptions": {
    "FailedRetryCount": 3,
    "FailedRetryInterval": 60
  },
  "CapRabbitMqOptions": {
    "HostName": "192.168.0.192",
    "Port": 5672,
    "VirtualHost": "TestCap",
    "UserName": "admin",
    "Password": "admin"
  }
}

 

consul 注册 在程序启动的时候 就开始注册 用的ihostservice

 //添加 consul
            services.AddDefaultCustomConsul(Configuration);
            //程序开始就注册consul
            services.AddHostedService<ConsulHostServices>();
            //添加cap
            services.AddCustomCap(Configuration);

 

  //consul健康检查
            app.Map("/HealthCheck", config =>
            {
                config.Run(context =>
                {
                    context.Response.StatusCode = 200;
                    logger.LogError($"{context.Request.Host}consul健康检查成功");
                    return Task.CompletedTask;
                });
            });

 

.net core  使用 consul 包 如果 节点有变化 如何 让应用程序 感知
在 .NET Core 中使用 Consul 进行服务发现时,你可以通过注册一个 Consul 客户端,并使用该客户端来监控节点变化。Consul 提供了一个 健康检查 和 事件 系统,可以帮助你的应用程序感知节点的变化。

使用 Consul 监控节点变化的步骤:
安装 Consul NuGet 包:
确保你在项目中安装了 Consul NuGet 包。可以使用以下命令安装:

dotnet add package Consul
配置 Consul 客户端:
在你的应用程序中创建一个 Consul 客户端实例。通常在 Startup.cs 中进行配置。

using Consul;

public class Startup
{
    private readonly ConsulClient _consulClient;

    public Startup()
    {
        _consulClient = new ConsulClient(config =>
        {
            config.Address = new Uri("http://localhost:8500"); // Consul 服务器地址
        });
    }

    public void ConfigureServices(IServiceCollection services)
    {
        // 配置你的服务
        services.AddSingleton(_consulClient);
        // 其他服务配置
    }
}
监控服务变更:
你可以使用 Catalog API 来监控服务的变化。你可以定期检查服务的健康状态,或者使用 Blocking 查询来保持与 Consul 的连接并监听变化。

public async Task MonitorServices()
{
    var services = await _consulClient.Catalog.Services();

    // 记录初始服务
    var initialServices = services.Response;

    while (true)
    {
        var queryOptions = new QueryOptions { WaitTime = TimeSpan.FromSeconds(10) }; // 设置等待时间
        var response = await _consulClient.Catalog.Services(queryOptions);
        var currentServices = response.Response;

        // 检查服务变化
        if (!initialServices.SequenceEqual(currentServices))
        {
            // 处理服务变化逻辑
            Console.WriteLine("服务列表发生变化!");
            initialServices = currentServices; // 更新初始服务列表
        }
    }
}
处理服务变化:
在服务变化的逻辑中,你可以实现应用程序需要采取的具体操作,比如重新加载配置、更新路由、通知用户等。

健康检查:
如果你希望 Consul 根据服务的健康状况来监控节点,可以在服务注册时添加健康检查配置。例如:

var registration = new AgentServiceRegistration()
{
    ID = "my-service-id",
    Service = "my-service",
    Address = "localhost",
    Port = 5000,
    Check = new AgentServiceCheck
    {
        HTTP = "http://localhost:5000/health",
        Interval = TimeSpan.FromSeconds(10),
        Timeout = TimeSpan.FromSeconds(5)
    }
};

await _consulClient.Agent.ServiceRegister(registration);
总结
通过以上步骤,你可以在 .NET Core 应用程序中使用 Consul 客户端监控服务节点的变化。利用 Consul 的健康检查和阻塞查询特性,可以实现应用程序对服务变更的实时感知,进而采取相应的业务逻辑处理。确保在生产环境中合理设置阻塞查询的超时时间,以避免过度消耗资源。

  

 

posted on 2023-06-07 17:26  是水饺不是水饺  阅读(310)  评论(0)    收藏  举报

导航