windows Consul集群搭建及微服務概念


Consul介紹:

Consul 是由 HashiCorp 公司推出的開源軟件,用於實現分布式系統的服務發現與配置。與其他分布式服務注冊與發現的方案,Consul 的方案更“一站式”,內置了服務注冊與發現框 架、分布一致性協議實現、健康檢查、Key/Value 存儲、多數據中心方案,不再需要依賴其他工具(比如 ZooKeeper 等),使用起來也較為簡單。

Consul的如何實現的?

Consul 用 Golang 實現,因此具有天然可移植性(支持 Linux、windows 和 Mac OS X ),它的安裝包僅包含一個可執行文件,方便部署,與 Docker 等輕量級容器可無縫配合。

微服務概念:

微服務是一種架構風格,一個大型復雜軟件應用由一個或多個微服務組成。系統中的各個微服務可被獨立部署,各個微服務之間是松耦合的。每個微服務僅關注於完成一件任務並很好地完成該任務。在所有情況下,每個任務代表着一個小的業務能力。

盡管“微服務”這種架構風格沒有精確的定義,但其具有一些共同的特性,如圍繞業務能力組織服務、自動化部署、智能端點、對語言及數據的“去集中化”控制等等。簡而言之,微服務架構風格是一種將單個應用程序開發為一套小型服務的方法,每個小型服務都在自己的流程中運行,並與輕量級機制(通常是HTTP資源API)進行通信。這些服務圍繞業務功能構建,可通過全自動部署機制獨立部署。這些服務至少集中管理,可以用不同的編程語言編寫,並使用不同的數據存儲技術。

 Consul的安裝與配置

我們需要去下載Consul ,下載很簡單,直接去:https://www.consul.io/downloads.html 選擇對應的平台即可,如果你的是Mac OS x 那么直接雙擊那個可執行文件即可,我的平台是windows10 ,那么相對於MacOsX是比較困難的,即配置環境變量,將文件的位置放到你的path中就可以了,其目的就是為了在Cmd終端中能夠鍵入執行Consul命令,如果你的環境變量已經配置成功,請在Cmd中敲擊consul,如果結果如下,那么恭喜您,你成功進入Consul大門。

 名詞解釋

Client : Consul 的 Client模式,就是客戶端模式。是 Consul 節點的一種模式,這種模式下,所有注冊到當前節點的服務會被轉發到 Server,本身是不持久化這些信息。
Server :Consul 的 Server 模式,表明這個 Consul 是個 Server ,這種模式下,功能和 Client 都一樣,唯一不同的是,它會把所有的信息持久化的本地,這樣遇到故障,信息是可以被保留的。
Server-Leader:  Server 是它們的老大,它和其它 Server 不一樣的一點是,它需要負責同步注冊的信息給其它的 Server ,同時也要負責各個節點的健康監測。
raft: Server 節點之間的數據一致性保證協議使用的是 raft,而 zookeeper 用的 paxos,etcd采用的也是raft
服務發現協議:Consul 采用 http 和 DNS 協議,etcd 只支持 http 。
服務注冊:Consul 支持兩種方式實現服務注冊,一種是通過 Consul 的服務注冊 Http API,由服務自己調用 API 實現注冊,另一種方式是通過 json 格式的配置文件實現注冊,將需要注冊的服務以 json 格式的配置文件給出。Consul 官方建議使用第二種方式。

啟動Consul

如果是在單機情況下使用開發模式啟動,在終端中輸入:

1
~ » consul agent -dev

開啟Consul 集群

首先創建Server-Leader,即老大,那么在終端上輸入以下命令:

 
1
2
3
4
5
//Server
consul agent -server -ui -bootstrap-expect=2 -data-dir=/tmp/consul -node=con
sul-server-1 -client=0.0.0.0 -bind=10.211.55.2 -datacenter=dc1
//Client
  consul agent -client -bind 0.0.0.0  本機ip -data-dir=\tmp\consul -node 節點名 - join  隨意一個服務器的地址
                                                                   consul agent -client=0.0.0.0 -bind=10.2.234.183   -data-dir=\tmp\consul -node consul-client1 -join 10.0.104.17

其中:

consul agent -server 表示以服務器模式啟動代理。
-bootstrap-expect=2 表示節點個數為2個。
-node=consul-server-1 表示節點名稱
-client=0.0.0.0 表示客戶端 IP
-bind=10.211.55.2 表示服務端 IP
-datacenter=dc1 數據中心名稱

需要注意的是,如果你啟動的Server模式的話,你一定要注意node名不要相同,否則將會替換你的項。

那么Server-Leader已經創建了,需要別的服務器去進行連接了,如果是Server模式,輸入以下命令,只不過是拼接了-join id.

1
2
3
consul agent -server -ui -bootstrap-expect=2 -data-dir=/tmp/consul -node=con
sul-server-2 -client=0.0.0.0 -bind=10.211.55.4 -datacenter=dc1 - join  10.211.
55.2
  • -data-dir=/tmp/consul 表示臨時數據存儲路徑
  • -join 10.211.55.2 表示加入 10.211.55.2 所在的集群

這個時候如果出現Consul agent Running!!還有一大堆東西。那么說明你已經集群成功了,你可以去自己的通過Consul提供的UI去看節點狀況,地址為 你自己的id+8500端口,即192.168.10.6:8500或者你的子節點均可,但是如果說你的節點個數是4個,你還有幾個節點沒有放上去,那么UI是出不來了(有可能會報500錯)。

那么除了UI查看節點以外,還可以通過終端輸入指令來查看我們的節點狀態。

1
consul members

如果我們希望得到更為詳細的信息,可以使用指令來查看

1
consul  operator  raft list-peers

如果說你沒有服務器集群,那么輸入以上命令就會出現以下結果:

 創建.NET Core API 並注冊服務

如果你的 Consul Cluster 已經搭建完成,那么可以接下來創建一個.NET Core API 來實現服務的注冊。

我們創建一個空的解決方案,添加解決方案文件夾 “服務注冊”,創建Api項目。

 

修改啟動配置文件

在launchSettings.json中修改啟動ip和端口。

1
2
3
4
5
6
7
8
9
10
11
{
   "profiles" : {
     "BasicPlatformService" : {
       "commandName" "Project" ,
       "applicationUrl" "http://192.168.43.174:8080" ,
       "environmentVariables" : {
         "ASPNETCORE_ENVIRONMENT" "Development"
       }
     }
   }
}

添加健康檢查控制器

在項目中添加一個名為  HealthController 的 API 控制器,用於在將服務注冊到 Consul 后的健康檢查。

1
2
3
4
5
6
7
8
9
10
11
[Route( "api/[controller]" )]
[ApiController]
public  class  HealthController : ControllerBase
{
     /// <summary>
     /// 服務健康檢測 ⽅方法
     /// </summary>
     /// <returns></returns>
     [HttpGet]
     public  IActionResult Get() => Ok( " health check ok" );
}

 在項目中添加Consul,或者鍵入命令,這當然,由你選擇。

1
install-package Consul

 注冊Consul中間件,修改Startup.cs文件,這里使用的是 Consul 的服務注冊 Http API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public  void  Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime life)
        {
            if  (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
 
            app.UseMvc();
 
            using  (ConsulClient client =  new  ConsulClient(x => x.Address =  new  Uri( "http://192.168.43.174:8500" )))
            {
                var  httpCheck =  new  AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),
                    Interval = TimeSpan.FromSeconds(10),
                    HTTP = $ "http://192.168.43.174:8080/api/health" ,
                    Timeout = TimeSpan.FromSeconds(5)
                };
                var  registration =  new  AgentServiceRegistration()
                {
                    Checks =  new [] { httpCheck },
                    ID =  "MicroService.Service" ,
                    Name =  "MicroService.Service" ,
                    Address =  "192.168.43.174" ,
                    Port = 8080,
                    Tags =  new [] {  "urlprefix-/MicroService.Chanpter01.Service"  }
                };
                // 服務啟動時注冊,內部實現是使⽤用 Consul API 進⾏行行注冊(HttpClient發起)
                client.Agent.ServiceRegister(registration).Wait();
                // 當服務停⽌止時,取消注冊
                life.ApplicationStopping.Register(() =>
                {
                    // 服務停⽌止時取消注冊
                    client.Agent.ServiceDeregister(registration.ID).Wait();
                });
            }
        }

  完成后,啟動項目,此時已成功注冊。

2018-12-18 更新 使用Json文件配置

為了不讓每個服務器都打以上的代碼,那么我們可以使用json配置(放到你的config中)。創建一個ProjectServer.json

復制代碼
{
  "services": [
    {
      "ID": "t169_000000001_student_service",
      "name": "T169.ZhangZiHao.Service",
      "tags": [
        "urlprefix-/T169.Studeent.Service"
      ],
      "address": "192.168.43.174",
      "port": 8080,
      "checks": [
        {
          "name": "Student Service check",
          "http": "http://192.168.43.174:8080/api/health",
          "interval": "10s",
          "timeout": "5s"
        }
      ]
    },
    {
      "ID": "t169_000000002_mail_service",
      "name": "T169.Mail.Service",
      "tags": [
        "urlprefix-/T169.Mail.Service"
      ],
      "address": "192.168.43.174",
      "port": 8081,
      "checks": [
        {
          "name": "Mail Service check",
          "http": "http://192.168.43.174:8081/api/health",
          "interval": "10s",
          "timeout": "5s"
        }
      ]
    }
  ]
}
復制代碼

其中每個節點我就不一一闡述了,和硬編碼中的都一樣,就需要改改http address。

然后開始部署集群的時候要注意了,和上面不一樣的是 需要你去指定tmp了,就是你的那個config的地方 。

consul agent -server -ui -bootstrap-expect=2 -data-dir=/tmp/consul -config-d
ir=/tmp/consul/config -node=consul-server-2 -client=0.0.0.0 -bind=10.211.55.
4 -datacenter=dc1 

如果要加入集群則是:

consul agent -server -ui -bootstrap-expect=2 -data-dir=/tmp/consul -config-d
ir=/tmp/consul/config -node=consul-server-2 -client=0.0.0.0 -bind=10.211.55.
4 -datacenter=dc1 -join 10.211.55.2

2018-12-19 更新 使用MailKit

為了更好的接收健康狀態,那么我們使用MailKit,使用這個之前,先nuget這個emailkit。

定義EmailHelper.cs:

 

復制代碼
public class EmailSettings
    {
        public string SmtpServer { get; set; }
        public int SmtpPort { get; set; }
        public string AuthAccount { get; set; }
        public string AuthPassword { get; set; }
        public string ToWho { get; set; }
        public string ToAccount { get; set; }
        public string FromWho { get; set; }
        public string FromAccount { get; set; }
        public string Subject { get; set; }
    }
    public class EmailHelper
    {
        public static void SendHealthEmail(EmailSettings settings, string content)
        {
            try
            {
                dynamic list = JsonConvert.DeserializeObject(content);
                if (list != null && list.Count > 0)
                {
                    var emailBody = new StringBuilder("健康檢查故障:\r\n");
                    foreach (var noticy in list)
                    {
                        emailBody.AppendLine($"--------------------------------------");
                        emailBody.AppendLine($"Node:{noticy.Node}");
                        emailBody.AppendLine($"Service ID:{noticy.ServiceID}");
                        emailBody.AppendLine($"Service Name:{noticy.ServiceName}");
                        emailBody.AppendLine($"Check ID:{noticy.CheckID}");
                        emailBody.AppendLine($"Check Name:{noticy.Name}");
                        emailBody.AppendLine($"Check Status:{noticy.Status}");
                        emailBody.AppendLine($"Check Output:{noticy.Output}");
                        emailBody.AppendLine($"--------------------------------------");
                    }

                    var message = new MimeMessage();
                    // 從誰發出
                    message.From.Add(new MailboxAddress(settings.FromWho, settings.FromAccount));
                    // 發送給誰
                    message.To.Add(new MailboxAddress(settings.ToWho, settings.ToAccount));

                    // 設置郵件標題
                    message.Subject = settings.Subject;
                    // 設置郵件內容
                    message.Body = new TextPart("plain") { Text = emailBody.ToString() };

                    using (var client = new SmtpClient())
                    {
                        client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                        client.Connect(settings.SmtpServer, settings.SmtpPort, false);
                        client.AuthenticationMechanisms.Remove("XOAUTH2");// 去除安全令牌
                        // 驗證賬號+密碼
                        client.Authenticate(settings.AuthAccount, settings.AuthPassword);
                        // 發郵件
                        client.Send(message);
                        client.Disconnect(true);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
復制代碼

 

創建一個MailController:

復制代碼
 [Produces("application/json")]
    [Route("api/Mail")]
    public class MailController : Controller
    {
        public IConfiguration Configuration { get; }

        public MailController(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        [HttpPost("/notice")]
        public IActionResult Notice()
        {
            var bytes = new byte[10240];
            var i = Request.Body.ReadAsync(bytes, 0, bytes.Length);
            var content = System.Text.Encoding.UTF8.GetString(bytes).Trim('\0');
            
            EmailSettings settings = new EmailSettings()
            {
                SmtpServer = "smtp.126.com",    // 發送郵件的服務器地址
                SmtpPort = 25,                  // 服務器端口號
                AuthAccount = "",        // 用戶名
                AuthPassword = "",     // 密碼
                ToWho = "接收方名字",             // 接收方的名字
                ToAccount = "",    // 接收方郵箱
                FromWho = "你自己的名字",          //  發郵件的人姓名
                FromAccount ="", //  發郵件的郵件地址
                Subject = "health check notice" //  郵件標題
            };

            EmailHelper.SendHealthEmail(settings, content);

            return Ok();
        }
    }
復制代碼

然后再創建emailserver.json 那么就ok了 ,重新啟動集群。

復制代碼
{
    "watches": [
    {
      "type": "checks",
      "handler_type": "http",
      "state": "critical",
      "http_handler_config": {
        "path": "http://10.211.55.4:8081/notice",
        "method": "POST",
        "timeout": "10s",
        "header": { "Authorization": [ "token" ] }
      }
    }
  ]
}
復制代碼

 

 

 

 

————————————————————————————————————————————————————————————————————————————————————

 

.NET Core 接入 Consul

 
service discovery
  1. 創建 .NET Core WebAPI 服務 ServiceA(2個實例) 和 ServiceB

  2. NuGet 安裝 Consul

  3. 注冊到 Consul 的核心代碼如下(源碼下載):

    public static class ConsulBuilderExtensions { public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IApplicationLifetime lifetime, ConsulOption consulOption) { var consulClient = new ConsulClient(x => { // consul 服務地址 x.Address = new Uri(consulOption.Address); }); var registration = new AgentServiceRegistration() { ID = Guid.NewGuid().ToString(), Name = consulOption.ServiceName,// 服務名 Address = consulOption.ServiceIP, // 服務綁定IP Port = consulOption.ServicePort, // 服務綁定端口 Check = new AgentServiceCheck() { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服務啟動多久后注冊 Interval = TimeSpan.FromSeconds(10),//健康檢查時間間隔 HTTP = consulOption.ServiceHealthCheck,//健康檢查地址 Timeout = TimeSpan.FromSeconds(5) } }; // 服務注冊 consulClient.Agent.ServiceRegister(registration).Wait(); // 應用程序終止時,服務取消注冊 lifetime.ApplicationStopping.Register(() => { consulClient.Agent.ServiceDeregister(registration.ID).Wait(); }); return app; } } 
  4. 添加配置如下:

    "ServiceName": "ServiceA",
    "ServiceIP": "192.168.124.8",
    "ServicePort": 8000,
    "ServiceHealthCheck": "http://192.168.124.8:8000/healthCheck",
    "ConsulAddress": "http://192.168.124.8:8500"
    
  5. 注冊成功結果如下:

     
    service register
  6. ServiceB 調用 ServiceA 接口

    ServiceB 通過 ConsulClient 進行服務發現,獲取到 ServiceA 的地址,然后隨機任意一台進行請求,核心代碼如下:

    var url = _configuration["ConsulAddress"].ToString(); using (var consulClient = new ConsulClient(a => a.Address = new Uri(url))) { var services = consulClient.Catalog.Service("ServiceA").Result.Response; if (services != null && services.Any()) { // 模擬隨機一台進行請求,這里只是測試,可以選擇合適的負載均衡工具或框架 Random r = new Random(); int index = r.Next(services.Count()); var service = services.ElementAt(index); using (HttpClient client = new HttpClient()) { var response = await client.GetAsync($"http://{service.ServiceAddress}:{service.ServicePort}/values/test"); var result = await response.Content.ReadAsStringAsync(); return result; } } } 
  7. 多次調用 ServiceB 接口結果如下:

 
result-1
 
result-2


作者:BeckJin
鏈接: https://www.jianshu.com/p/4aaaee6e9ce1
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM