Consul 服務的註冊和發現

  • 2021 年 5 月 18 日
  • 筆記

 Consul 是Hashicorp公司推出的開源工具,用於實現分散式系統的服務發現與配置。Consul是分散式的,高可用的,可橫向擴展的。

Consul 的主要特點有:
    Service Discovery : 服務註冊與發現,Consul 的客戶端可以做為一個服務註冊到 Consul,也可以通過 Consul 來查找特定的服務提供者,並且根據提供的資訊進行調用。

    Health Checking: Consul 客戶端會定期發送一些健康檢查數據和服務端進行通訊,判斷客戶端的狀態、記憶體使用情況是否正常,用來監控整個集群的狀態,防止服務轉發到故障的服務上面。

   KV Store: Consul 還提供了一個容易使用的鍵值存儲。這可以用來保持動態配置,協助服務協調、建立 Leader 選舉,以及開發者想構造的其它一些事務。

  Secure Service Communication: Consul 可以為服務生成分散式的 TLS 證書,以建立相互的 TLS 連接。 可以使用 intentions 定義允許哪些服務進行通訊。 可以使用 intentions 輕鬆管理服務隔離,而不是使用複雜的網路拓  撲和靜態防火牆規則。

  Multi Datacenter: Consul 支援開箱即用的多數據中心,這意味著用戶不需要擔心需要建立額外的抽象層讓業務擴展到多個區域。

  Consul 角色
 Server: 服務端, 保存配置資訊, 高可用集群, 在區域網內與本地客戶端通訊, 通過廣域網與其它數據中心通訊。 每個數據中心的 Server 數量推薦為 3 個或是 5 個。

 Client: 客戶端, 無狀態, 將 HTTP 和 DNS 介面請求轉發給區域網內的服務端集群。

 Consul 旨在對 DevOps 社區和應用程式開發人員友好,使其成為現代、彈性基礎架構的理想選擇。
                                          

                                                                                                     

 

 

 

首先下載Consul服務組件:官網下載://www.consul.io/downloads

 

 

 啟動Consul:

根據啟動後的http顯示埠8500埠:

 

通過瀏覽器查看 Consul服務中心是否啟動成功,地址://localhost:8500,如果成功,效果如下:

 

 

 

 

 

 然後創建一個服務端實例工程:

 

 

 添加必要的測試類:

using System;
using System.Collections.Generic;
using System.Text;

namespace Demo
{
    /// <summary>
    /// 用戶模型。
    /// </summary>
    public class User
    {
        /// <summary>
        /// 獲取或者設置用戶主鍵。
        /// </summary>
        public int ID { get; set; }

        /// <summary>
        /// 獲取或者設置用戶姓名。
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 獲取或者設置用戶帳號名稱。
        /// </summary>
        public string Account { get; set; }

        /// <summary>
        /// 獲取或者設置用戶密碼。
        /// </summary>
        public string Password { get; set; }

        /// <summary>
        /// 獲取或者設置用戶的電子郵箱地址。
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        /// 獲取或者設置用戶角色。
        /// </summary>
        public string Role { get; set; }

        /// <summary>
        /// 獲取或者設置用戶的登錄時間。
        /// </summary>
        public DateTime LoginTime { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Text;

namespace Demo
{
    /// <summary>
    /// 用戶服務的介面定義。
    /// </summary>
    public interface IUserService
    {
        /// <summary>
        /// 查找指定主鍵的用戶實例對象。
        /// </summary>
        /// <param name="id">用戶的主鍵。</param>
        /// <returns>返回查找到的用戶實例對象。</returns>
        User FindUser(int id);

        /// <summary>
        /// 獲取所有用戶的實例集合。
        /// </summary>
        /// <returns>返回所有的用戶實例。</returns>
        IEnumerable<User> UserAll();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Demo
{
    /// <summary>
    /// 實現用戶服務介面的實現類型。
    /// </summary>
    public class UserService : IUserService
    {
        private IList<User> dataList;

        /// <summary>
        /// 初始化類型的實例
        /// </summary>
        public UserService()
        {
            dataList = new List<User>()
             {
             new User {ID=1,Name="張三",Account="5435435345",Password="4535435435",Email="4535345345", Role="Admin", LoginTime=DateTime.Now},
             new User {ID=2,Name="李四",Account="5435435345",Password="5435345345",Email="543534535", Role="Admin", LoginTime=DateTime.Now.AddDays(-5) },
             new User { ID = 3, Name = "王二", Account = "45354", Password = "3245345", Email = "54353455", Role = "Admin", LoginTime = DateTime.Now.AddDays(-30) },
             new User { ID = 4, Name = "麻子", Account = "45354", Password = "4534535", Email = "453534534", Role = "Admin", LoginTime = DateTime.Now.AddDays(-90) },
             new User { ID = 5, Name = "陳五", Account = "54353", Password = "5435345", Email = "5435345345", Role = "Admin", LoginTime = DateTime.Now.AddMinutes(-50) }
             };
        }

        /// <summary>
        /// 查找指定主鍵的用戶實例對象。
        /// </summary>
        /// <param name="id">用戶的主鍵。</param>
        /// <returns>返回查找到的用戶實例對象。</returns>
        public User FindUser(int id)
        {
            return dataList.FirstOrDefault(user => user.ID == id);
        }

        /// <summary>
        /// 獲取所有用戶的實例集合。
        /// </summary>
        /// <returns>返回所有的用戶實例。</returns>
        public IEnumerable<User> UserAll()
        {
            return dataList;
        }
    }
}
using Consul;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Text;

namespace Demo
{
   /// <summary>
     /// Consul 靜態擴展類。
     /// </summary>
     public static class ConsulExtension
     {
            /// <summary>
                   ///類型初始化器,初始化 Consul 網址和數據中心。
        /// </summary>
         static ConsulExtension()
         {
            Uri = new Uri("//localhost:8500");
             DataCenter = "dc1";
          }
            
         /// <summary>
         /// 獲取或者設置 Consul 的網址。
         /// </summary>
         public static Uri Uri { get; set; }
 
         /// <summary>
        /// 獲取或者設置數據中心。
         /// </summary>
         public static string DataCenter { get; set; }

         /// <summary>
         /// 向 Consul 服務中心註冊服務實例。
         /// </summary>
         /// <param name="configuration">配置對象。</param>
         /// <param name="consulServiceName">在 Consul 服務中心註冊的服務類別名稱,多個實例的 ID 可以屬於一個服務類別名稱。</param>
         /// <param name="serviceID">服務實例的主鍵值,必須唯一。</param>
       public static void ConsulRegist(this IConfiguration configuration, string consulServiceName, string serviceID)
         {
             if (string.IsNullOrEmpty(consulServiceName) || string.IsNullOrWhiteSpace(consulServiceName))
             {
                throw new ArgumentNullException("consulServiceName is null");
            }
            if (string.IsNullOrEmpty(serviceID) || string.IsNullOrWhiteSpace(serviceID))
             {
                 throw new ArgumentNullException("serviceID is null.");
             }

            using (ConsulClient client = new ConsulClient(config =>
             {
                 config.Address = Uri;
                 config.Datacenter = DataCenter;
             }))
             {
                     string ip = configuration["ip"];
                     int port = int.Parse(configuration["port"]);
                     int weight = string.IsNullOrWhiteSpace(configuration["weight"]) ? 1 : int.Parse(configuration["weight"]);
    
                client.Agent.ServiceRegister(new AgentServiceRegistration()
                {
                            ID = serviceID,
                     Name = consulServiceName,
                    Address = ip,
                     Port = port,
                     Tags = new string[] { weight.ToString() },
                     Check = new AgentServiceCheck()
                     {
                                    Interval = TimeSpan.FromSeconds(12),
                         HTTP = $"//{ip}:{port}/API/Health/Index",
                         Timeout = TimeSpan.FromSeconds(5),
                         DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20)
                     }
                        }).Wait();
                    Console.WriteLine($"註冊服務:{ip}:{port}--Weight:{weight}");
                 };
         }

         /// <summary>
         /// 向 Consul 服務中心註銷服務實例。
         /// </summary>
         /// <param name="applicationLifetime">配置對象。</param>
         /// <param name="serviceID">服務實例的主鍵值,必須唯一。</param>
         public static void ConsulDown(this IApplicationLifetime applicationLifetime, string serviceID)
         {
                if (string.IsNullOrEmpty(serviceID) || string.IsNullOrWhiteSpace(serviceID))
                     {
                        throw new ArgumentNullException("serviceID is null");
                     }
                 applicationLifetime.ApplicationStopped.Register(() =>
                 {
                         using (var consulClient = new ConsulClient(config => { config.Address = Uri; config.Datacenter = DataCenter; }))
                             {
                                 Console.WriteLine("服務已經退出");
                                consulClient.Agent.ServiceDeregister(serviceID);
                             }
                     });
            }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace ConsulAPIDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HealthController : ControllerBase
    {
        private IConfiguration _configuration;
 
        /// <summary>
         /// 初始化該類型的新實例。
         /// </summary>
         /// <param name="configuration">配置介面。</param>
         public HealthController(IConfiguration configuration)
        {
             _configuration = configuration;
         }
 
        /// <summary>
         /// 要調用的介面。
         /// </summary>
         [HttpGet]
         [Route("Index")]
         public IActionResult Index()
         {
             Console.WriteLine($"This is HealhController {_configuration["port"]} Invoke");
             return Ok();
         }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Demo;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace ConsulAPIDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        /// <summary>
        /// 用戶的 API 類型。
        /// </summary>
        [Route("api/[controller]")]
        [ApiController]
        public class UsersController : ControllerBase
        {
            #region 私有欄位

            private readonly ILogger<UsersController> _logger;
            private readonly IUserService _userService;
            private IConfiguration _configuration;

            #endregion

            #region 構造函數

            /// <summary>
            /// 初始化該類型的新實例。
            /// </summary>
            /// <param name="logger">日誌記錄器。</param>
            /// <param name="userService">用戶服務介面。</param>
            /// <param name="configuration">配置服務。</param>
            public UsersController(ILogger<UsersController> logger, IUserService userService, IConfiguration configuration)
            {
                _logger = logger;
                _userService = userService;
                _configuration = configuration;
            }

            #endregion

            #region 實例方法

            /// <summary>
            /// 獲取一條記錄
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            [HttpGet]
            [Route("Get")]
            public User Get(int id)
            {
                return _userService.FindUser(id);
            }

            /// <summary>
            /// 獲取所有記錄。
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            [Route("All")]
            //[Authorize]
            public IEnumerable<User> Get()
            {
                Console.WriteLine($"This is UsersController {this._configuration["port"]} Invoke");

                return this._userService.UserAll().Select((user => new User
                {
                    ID = user.ID,
                    Name = user.Name,
                    Account = user.Account,
                    Password = user.Password,
                    Email = user.Email,
                    Role = $"{this._configuration["ip"]}:{this._configuration["port"]}",
                    LoginTime = user.LoginTime
                })); ;
            }

            /// <summary>
            /// 超時處理
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            [Route("Timeout")]
            public IEnumerable<User> Timeout()
            {
                Console.WriteLine($"This is Timeout Start");
                //超時設置。
                Thread.Sleep(3000);

                Console.WriteLine($"This is Timeout End");

                return this._userService.UserAll().Select((user => new User
                {
                    ID = user.ID,
                    Name = user.Name,
                    Account = user.Account,
                    Password = user.Password,
                    Email = user.Email,
                    Role = $"{this._configuration["ip"]}:{this._configuration["port"]}",
                    LoginTime = user.LoginTime
                })); ;
            }


            #endregion
        }
    }
}


 

 

startup裡面的配置:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Demo;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace ConsulAPIDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //需要增加的
                       services.AddSingleton<IUserService, UserService>();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env, Microsoft.Extensions.Hosting.IApplicationLifetime applicationLifetime)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see //aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            #region Consul 註冊,需要增加的
           
             string serviceID = $"Service:{Configuration["ip"]}:{Configuration["port"]}---{Guid.NewGuid()}";
             string consuleServiceName = "PatrickLiuService";
            
             //註冊服務。
             Configuration.ConsulRegist(consuleServiceName, serviceID);
            
             //註銷服務
            applicationLifetime.ConsulDown(serviceID);
            

            #endregion
            app.UseHttpsRedirection();
            app.UseMvc();
           
           
        }
    }
}

編譯一下,根據不同埠啟動api介面服務:

API埠設置為2000:

 

 

API埠設置為3000:

 

 

註冊四個實例:

 

 上面整個過程已經完成了服務的註冊,至於發現自己可以測一下,自己關掉一個服務,Consul會自動檢查各個服務的健康度,如果哪個服務出現異常,consul會自動剔除異常服務。

 

 

參考文檔://www.cnblogs.com/PatrickLiu/p/13939135.html