.Net Core 3.1下完成Consul服務註冊

1、ConsulConfig .cs

public class ConsulConfig
{
    /// <summary>
    /// Consul服務註冊地址
    /// </summary>
    public string ConsulAddress { get; set; }
    /// <summary>
    /// 當前服務名稱,可以多個實例共享
    /// </summary>
    public string ServiceName { get; set; }
    /// <summary>
    /// 當前服務地址[可選,如果Consul幫助類中能獲取到,這裏可以不配置]
    /// </summary>
    public string ServiceUriHost { get; set; }
    /// <summary>
    /// 當前服務端口號[可選,如果Consul幫助類中能獲取到,這裏可以不配置]
    /// </summary>
    public int ServiceUriPort { get; set; }
    /// <summary>
    /// 健康檢查的地址,當前服務公佈出來的一個api接口
    /// </summary>
    public string HealthCheck { get; set; }
}

2、ConsulConsumer.cs

public class ConsulConsumer:IServiceConsumer //定義成接口,以後換其它的註冊中心方便替換
{
    private readonly string consulAddress;
    public ConsulConsumer(IOptions<ConsulConfig> serviceOptions)
    {
        consulAddress = serviceOptions.Value.ConsulAddress;
    }

    public async Task<List<string>> GetServices(string serviceName)
    {
        var consulClient = new ConsulClient(configuration =>
        {
            //服務註冊地址:集羣中任意一個地址
            configuration.Address = new Uri(consulAddress);
        });

        var result = await consulClient.Catalog.Service(serviceName);
        if (result == null || result.Response == null || result.Response.Length < 1)
            return null;

        var list = new List<string>();
        foreach (var item in result.Response)
        {
            list.Add($"{item.ServiceAddress}:{item.ServicePort}");
        }
        return list;
    }
}

3、ConsulRegister .cs

/// <summary>
/// 服務註冊
/// </summary>
public static class ConsulRegister
    {
        public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
        {
            //獲取服務配置項
            var serviceOptions = app.ApplicationServices.GetRequiredService<IOptions<ConsulConfig>>().Value;
            CheckConfig(serviceOptions);

            Tuple<string, string, int> hostinfo = GetHostInfo(serviceOptions, app);
            // 服務ID,唯一的
            string serviceId = serviceOptions.ServiceName + Guid.NewGuid().ToString();
            //節點服務註冊對象
            var registration = new AgentServiceRegistration()
            {
                ID = serviceId,
                Name = serviceOptions.ServiceName,  //對服務分組
                Address = hostinfo.Item2, //服務地址
                Port = hostinfo.Item3,
                Tags = new string[] { }, //標籤信息,服務發現的時候可以獲取到的,負載均衡策略擴展的
                Check = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(7),  //在7秒未連接上服務之後註銷關鍵服務
                    Interval = TimeSpan.FromSeconds(2), //每個2秒發送一次心跳檢測
                    Timeout = TimeSpan.FromSeconds(3),  //連接超時時間
                    HTTP = hostinfo.Item1 //   //心跳檢測訪問的接口地址,需要自己在項目中寫好這個接口
                }
            };
            var consulClient = new ConsulClient(configuration =>
            {
                    //服務註冊地址:集羣中任意一個地址
                    configuration.Address = new Uri(serviceOptions.ConsulAddress);
            });
            //註冊到consul
            consulClient.Agent.ServiceRegister(registration).Wait();

            //獲取主機生命週期管理接口
            var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
            //程序停止的時候取消註冊
            lifetime.ApplicationStopping.Register(() =>
            {
                consulClient.Agent.ServiceDeregister(serviceId).Wait();
            });

            return app;
        }


        /// <summary>
        /// 獲取當前主機的域名和端口
        /// 用IIS啓動獲取不到。如果獲取不到必須保證配置文件配了
        /// 啓動的時候指定--Urls纔可以獲取到;例如 :dotnet Service_One.dll --Urls "https://localhost:5002"
        /// </summary>
        /// <param name="serviceOptions"></param>
        /// <param name="app"></param>
        /// <returns>
        /// 第一個返回值:健康檢查地址(例如:http://127.0.0.1:5001/healthcheck)
        /// 第二個返回值:主機地址(例如:127.0.0.1)
        /// 第三個返回值:端口號(例如:80)
        /// </returns>
        private static Tuple<string, string, int> GetHostInfo(ConsulConfig serviceOptions, IApplicationBuilder app)
        {
            #region 
            var features = app.Properties["server.Features"] as FeatureCollection;
            var address = features.Get<IServerAddressesFeature>().Addresses.FirstOrDefault();
            string http = null, host = null;
            int port = 0;
            if (!string.IsNullOrEmpty(address))
            {
                var uri = new Uri(address);// 協議頭:uri.Sechema  主機:uri.Host  端口:uri.Port 
                http = address + serviceOptions.HealthCheck;
                host = uri.Scheme + "://" + uri.Host;
                port = uri.Port;
            }
            else
            {
                http = $"{serviceOptions.ServiceUriHost}:{serviceOptions.ServiceUriPort}{serviceOptions.HealthCheck}";
                host = serviceOptions.ServiceUriHost;
                port = serviceOptions.ServiceUriPort;
            }
            if (string.IsNullOrEmpty(host) || port < 1)
                throw new Exception("Consul配置未能獲取到主機信息,請在consulconfig.json文件中配置");
            Console.WriteLine("健康檢查地址:" + http);
            return Tuple.Create(http, host, port);
            #endregion
        }

        /// <summary>
        /// 檢查配置文件
        /// </summary>
        /// <param name="serviceOptions"></param>
        private static void CheckConfig(ConsulConfig serviceOptions)
        {
            if (serviceOptions == null)
                throw new Exception("請正確配置consulconfig.json,其中包含ConsulAddress、ServiceName、HealthCheck");

            if (string.IsNullOrEmpty(serviceOptions.ConsulAddress))
                throw new Exception("請正確配置ConsulAddress");

            if (string.IsNullOrEmpty(serviceOptions.ServiceName))
                throw new Exception("請正確配置ServiceName");

            if (string.IsNullOrEmpty(serviceOptions.HealthCheck))
                throw new Exception("請正確配置HealthCheck");

        }
    }

4、ConsulServiceCollectionExtensions.cs

public static class ConsulServiceCollectionExtensions
{
    public static void AddConsulRegister(this IServiceCollection service)
    {
        //讀取服務配置文件
        try
        {
            var config = new ConfigurationBuilder().AddJsonFile("consulconfig.json").Build(); //nuget: Microsoft.Extensions.Configuration.Json
            service.Configure<ConsulConfig>(config); // nuget: Microsoft.Extensions.Options.ConfigurationExtensions
        }
        catch
        {
            throw new Exception("請正確配置consulconfig.json");
        }
    }

    public static void AddConsulConsumer(this IServiceCollection service)
    {
        //讀取服務配置文件
        service.AddConsulRegister(); // nuget: Microsoft.Extensions.Options.ConfigurationExtensions
        service.AddSingleton<IServiceConsumer, ConsulConsumer>();
    }
}

5、webapi項目的Startup.cs

//服務註冊者     
public void ConfigureServices(IServiceCollection services)
{
     services.AddHealthChecks(); //添加健康檢查,.net core自帶的
     services.AddConsulRegister();
}
         
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IOptions<ConsulRegister> serviceOptions)
{   
            //啓用健康檢查,.net core自帶的
     app.UseHealthChecks(serviceOptions.Value.HealthCheck);
     app.UseConsul();
}



//服務消費者
//通過構造函數依賴注入“ConsulConsumer”使用就可以
public void ConfigureServices(IServiceCollection services)
{
    services.AddConsulConsumer();
}

6、consulconfig.json

{
  "ConsulAddress": "http://127.0.0.1:8500", //Consul服務註冊地址,如果是消費者,則只需要配置這個字段,其它的無需配置
  "ServiceName": "Serivce_One", //當前服務名稱,可以多個實例共享
  "ServiceUriHost": "https://localhost", //當前服務地址
  "ServiceUriPort": "44323", //當前服務地址
  "HealthCheck": "/api/health/check" //健康檢查的地址,當前服務公佈出來的一個api接口
}

5、開啓兩個Service_One服務

dotnet Service_One.dll --Urls "https://localhost:5001"

dotnet Service_One.dll --Urls "https://localhost:5002"

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章