zoukankan      html  css  js  c++  java
  • .net core consul 服务配置 服务发现 服务健康检测 服务变更加载

    准备环境

     安装consul之后

    1. 创建一个.net core webapi 举例为UsercenterService

    2. nuget引用Consul组件  https://github.com/PlayFab/consuldotnet

    3. 创建配置实体类 (后面涉及功能介绍时候再解释属性含义)

     1     public class AppSettings
     2     {
     3         /// <summary>
     4         /// 数据库连接字符串
     5         /// </summary>
     6         public string DbConnection { get; set; }
     7 
     8         /// <summary>
     9         /// 服务注册参数
    10         /// </summary>
    11         public ServiceRegisterOptions ServiceRegisterOptions { get; set; }
    12     }
    13 
    14     public class ServiceRegisterOptions
    15     {
    16         /// <summary>
    17         /// 是否启用
    18         /// </summary>
    19         public bool IsActive { get; set; }
    20         /// <summary>
    21         /// 服务名称
    22         /// </summary>
    23         public string ServiceName { get; set; }
    24         /// <summary>
    25         /// 服务IP或者域名
    26         /// </summary>
    27         public string ServiceHost { get; set; }
    28         /// <summary>
    29         /// 服务端口号
    30         /// </summary>
    31         public int ServicePort { get; set; }
    32         /// <summary>
    33         /// consul注册地址
    34         /// </summary>
    35         public string ConsulRegisterUrl { get; set; }
    36         /// <summary>
    37         /// 标签 例如laiwutest
    38         /// </summary>
    39         public string[] Tags { get; set; }
    40     }
    View Code

    4. appsettings配置consul服务地址和UserService配置在consul的节点key

      4.1 配置consul地址(举例是在VS调试开发环境。所以在appsettings.Development.json中配置) 

    1 {
    2   "ConsulForConfig": {
    3     "Host": "{IP}:8500",//这里替换成自己consul服务的IP地址
    4     "Prefix": "git-dev/huangqiang/usercenterRegionIIS.json"
    5   }
    6 }
    View Code

           4.2 在consul上创建该节点并且配置

     1 {
     2     "DbConnection": "111111111111111111111111111111111111111",
     3     "ServiceRegisterOptions":
     4     {
     5         "IsActive":true,
     6         "ServiceName":"UserCenterRegion",
     7         "ServiceHost":"{IP}",//修改{IP}为你注入的服务的ip地址
     8         "ServicePort":"{Port}",//修改{Port}为你注入的服务的端口
     9         "ConsulRegisterUrl":"{IP}:8500",//修改{IP}为你的consul服务的IP
    10         "Tags":["浙江杭州"]
    11     },
    12 }
    View Code

       


    获取配置  

     1   public static AppSettings AddAppSettingByConsul(this IServiceCollection sc, IConfiguration configuration)
     2         {
     3             try
     4             {
     5                 //get local consul service address configration consulclient
     6                 var consulAddress = $"http://" + configuration["ConsulForConfig:Host"];
     7                 var key = configuration["ConsulForConfig:Prefix"];
     8                 if (string.IsNullOrWhiteSpace(consulAddress) || string.IsNullOrWhiteSpace(key))
     9                 {
    10                     throw new Exception("无法获取consulAddress地址或者consul key");
    11                 }
    12                 var consulClient = new ConsulClient(cfg => { cfg.Address = new Uri(consulAddress); });
    13                 sc.AddSingleton<IConsulClient>(p => consulClient);
    14                 //get app config
    15                 var res = consulClient.KV.Get(key).GetAwaiter().GetResult();
    16                 var resStr = Encoding.UTF8.GetString(res.Response.Value);
    17                 var appSettings = JsonConvert.DeserializeObject<AppSettings>(resStr);
    18                 if (appSettings == null)
    19                 {
    20                     throw new Exception($"appSettings 为null,consul 配置:{resStr}");
    21                 }
    22                 sc.AddSingleton<AppSettings>(appSettings);
    23                 return appSettings;
    24             }
    25             catch (Exception e)
    26             {
    27                 _log.Main.Error($"获取consul appsettings配置异常:{e.Message}");
    28                 Environment.Exit(-1);
    29             }
    30             return null;
    31         }
    View Code

    这里抽了一个扩展方法。使用的时候在Startup.cs类中的方法ConfigureServices中加入,这里弄了返回值只是偷懒下。

    AddAppSettingByConsul方法逻辑:先是拿到配置的consull服务地址和Key,再通过前面nuget引用的consul组件中的consulclient获取配置,最后注入到容器

    调试下 就拿到配置了。这样方便分布式服务,不用每台都配置,直接consul管理


    配置健康检测和服务注册

     准备健康检测接口:

    1     [Route("api/v1/[controller]")]
    2     [ApiController]
    3     public class HealthController : ControllerBase
    4     {
    5         [HttpGet]
    6         public IActionResult Get() => Ok("ok");
    7     }
    View Code

    .net core 配置注册和健康检测的地址

     1  public static void UseConsul(this IApplicationBuilder app, IApplicationLifetime appLife)
     2         {
     3             try
     4             {
     5                 var appSettings = app.ApplicationServices.GetService<AppSettings>();
     6                 var consulClient = app.ApplicationServices.GetService<IConsulClient>();
     7 
     8                 //config consul health check
     9                 var healthCheck = new AgentServiceCheck
    10                 {
    11                     DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),
    12                     Interval = TimeSpan.FromSeconds(30),
    13                     HTTP = $"{appSettings.ServiceRegisterOptions.ServiceHost}:{appSettings.ServiceRegisterOptions.ServicePort}/api/v1/Health",
    14                 };
    15 
    16                 //service register
    17                 var serviceId = $"{appSettings.ServiceRegisterOptions.ServiceName}_{appSettings.ServiceRegisterOptions.ServiceHost}:{appSettings.ServiceRegisterOptions.ServicePort}";
    18                 var registration = new AgentServiceRegistration
    19                 {
    20                     Checks = new[] { healthCheck },
    21                     Address = appSettings.ServiceRegisterOptions.ServiceHost,
    22                     Port = appSettings.ServiceRegisterOptions.ServicePort,
    23                     ID = serviceId,
    24                     Name = appSettings.ServiceRegisterOptions.ServiceName,
    25                     Tags = appSettings.ServiceRegisterOptions.Tags
    26                 };
    27                 consulClient.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
    28 
    29                 //service Deregister when app stop
    30                 appLife.ApplicationStopped.Register(() =>
    31                 {
    32                     consulClient.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();
    33                 });
    34 
    35 
    36             }
    37             catch (Exception e)
    38             {
    39                 _logger.Main.Error($"UseConsul error:{e.Message}");
    40                 Environment.Exit(-1);
    41             }
    42 
    43         }
    View Code

    这里也是抽了个扩展方法。调用放到Startup

    UseConsul方法解释:先是从容器中拿到前面注入的配置实体AppSettings和ConsulClient。再配置健康检测,再配置服务注册,再配置当服务关闭时候注销服务。

    其中健康检测的DeregisterCriticalServiceAfter表示如果服务启动失败,多少时间内注销consul上的该服务。

    服务注册的参数就不介绍了

    然后跑起来之后,到consul ui瞧一瞧。是不是注册成功,心跳正常

    状态为passing为正常的,刚启动时候状态会为critical。 当你的状态一直为critical时候,过了前面DeregisterCriticalServiceAfter的时间,服务将会注销,也就是注册失败。可能原因:服务地址配置有问题,consul无法访问你的health地址,也可能你的端口没打开。telnet看看

    当都成功的时候,服务已经正常注册到consul。下面再说说服务发现和服务变更发现


     

    服务发现和服务变更发现

    服务发现调用的方法有很多,agent,catalog,health,都可以获取列表。但是agent是查询本地自己的,catalog是整个集群的,heath是查询健康的。这里用health获取举例

    关键就一句话:_consulClient.Health.Service(serviceName, tag, true, queryOptions).Result。

    这样就能获取到注册到consul的服务列表了,但是如果有服务变更了(新的服务注册,旧的服务停止),应该怎么办?

    一般想到启动一个线程不停的去拿,是没有问题,但是有个更好的东西,“Blocking Queries”  https://www.consul.io/api/index.html

    这个东西简单来说就是会记录一个版本,consul服务端通过这个版本来判断是不是已经是最新的服务列表,如果是的话,那么将会阻塞一定时间(这个时间可配置)

    在c# 里面体现就是第三个参数queryOptions的WaitIndex和WaitTime,以及返回LastIndex,下面po出一部分代码。

            public void GetAllService()
            {
                _serviceIndexList.ForEach(p =>
                {
                    Task.Run(() =>
                    {
                        var queryOptions = new QueryOptions { WaitTime = TimeSpan.FromSeconds(_waitTime) };
                        while (true)
                        {
                            GetAgentServices(queryOptions, p.ServiceName, p.Tag);
                        }
                    });
                });
            }
    
            public void GetAgentServices(QueryOptions queryOptions, string serviceName, string tag = null)
            {
                try
                {
                    var res = _consulClient.Health.Service(serviceName, tag, true, queryOptions).Result;
                    _logger.Main.Info($"GetServiceList:{serviceName} {tag} waitIndex:{res.LastIndex}");
                    if (queryOptions.WaitIndex != res.LastIndex)
                    {
                        queryOptions.WaitIndex = res.LastIndex;
                        var currentService = _consulServices.FirstOrDefault(p => p.ServiceName == serviceName);
                        if (currentService == null)
                        {
                            _consulServices.Add(new ConsulService
                            {
                                ServiceName = serviceName,
                                Tag = tag,
                                ServiceEntries = new ConcurrentBag<ServiceEntry>(res.Response)
                            });
                        }
                        else
                        {
                            currentService.ServiceEntries = new ConcurrentBag<ServiceEntry>(res.Response);
                        }
                    }
                }
                catch (AggregateException ae)
                {
                    _logger.Main.Error($"consul获取{serviceName},{tag}服务列表资源错误:{ae.Flatten()}",ae);
                }
                catch (Exception e)
                {
                    _logger.Main.Error($"consul获取{serviceName},{tag}服务列表资源错误",e);
                }
            }
    View Code

    注:代码中的_serviceIndexList是存着需要获取哪些服务的服务tag,_consulServices是程序维护的最新服务列表

  • 相关阅读:
    Thinkphp5.0 模型hasOne、hasMany、belongsTo详解
    ES6中async和await说明和用法
    必会SQL练习题
    Git初识学习
    CI框架简单使用
    JavaScript 的 this 原理
    javascript实现游戏贪吃蛇
    js清除childNodes中的#text(选项卡中会用到获取第一级子元素)
    JavaNIO
    MongoDB入门_shell基本操作
  • 原文地址:https://www.cnblogs.com/TeemoHQ/p/10523637.html
Copyright © 2011-2022 走看看