1. 程式人生 > >.NET Core微服務之基於Consul實現服務治理

.NET Core微服務之基於Consul實現服務治理

請求轉發 1.0 asp.net AC port prefix 我們 tle nan

一、Consul基礎介紹

技術分享圖片

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

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

  關於Consul的更多介紹,比如優點,這裏就不再贅述了,上網一搜就可以隨處找到了。但是,必須貼一個和其他類似軟件的對比:

技術分享圖片

  此外,關於Consul的架構以及相關的角色,如下圖所示:

技術分享圖片

  要想利用Consul提供的服務實現服務的註冊與發現,我們需要建立Consul Cluster。在Consul方案中,每個提供服務的節點上都要部署和運行Consul的agent,所有運行Consul Agent節點的集合構成Consul Cluster。Consul Agent有兩種運行模式:ServerClient。這裏的Server和Client只是Consul集群層面的區分,與搭建在Cluster之上 的應用服務無關。以Server模式運行的Consul Agent節點用於維護Consul集群的狀態,官方建議每個Consul Cluster至少有3個或以上

的運行在Server Mode的Agent,Client節點不限。

  Consul支持多數據中心,每個數據中心的Consul Cluster都會在運行於Server模式下的Agent節點中選出一個Leader節點,這個選舉過程通過Consul實現的raft協議保證,多個 Server節點上的Consul數據信息是強一致的。處於Client Mode的Consul Agent節點比較簡單,無狀態,僅僅負責將請求轉發給Server Agent節點。

二、Consul集群搭建

2.1 環境準備

  這裏我準備了三臺Linux(CentOS)虛擬機和一臺Windows Server 2008 R2虛擬機,借助VMware Workstation搭建,如下圖所示。

技術分享圖片

  其中,192.168.80.100會作為leader角色,其余兩臺192.168.80.101和192.168.80.102會作為follower角色。當然,實際環境中leader角色不會是一個固定的,會隨著環境的變化(比如Leader宕機或失聯)由算法選出新的leader。在進行下面的操作會前,請確保三臺節點能夠相互ping通,並能夠和宿主機也ping通。另外,192.168.80.71會作為client角色,並且和其余三臺虛擬機互相ping通。

2.2 下載Consul

  Consul的下載很簡單,直接去:https://www.consul.io/downloads.html 選擇對應的平臺即可。

  這裏我們的linux虛擬機選擇的是Linux版本:

  技術分享圖片

  下載之後是一個zip文件,我們通過XFtp等工具將其傳送到我們的linux節點中即可。

  而Windows Server虛擬機選擇的是Windows版本,不再贅述。

2.3 安裝與配置Consul

  1.解壓Consul.zip:

  分別在三臺節點中解壓,解壓命令:

> unzip consul_1.1.0_linux_386.zip  

  解壓之後將consul復制到我們的自定義文件目錄中,比如:/usr/local/consul

> cp consul /usr/local/consul

  2.設置環境變量

  分別在三臺節點中設置環境變量:

> vim /etc/profile  

  在profile中增加一行CONSUL_HOME並更改PATH:

# Consul

export CONSUL_HOME=/usr/local/consul

export PATH=$PATH:$JAVA_HOME/bin:$CONSUL_HOME;  

  使得配置生效

> source /etc/profile

  測試是否生效,在三個節點測試輸入consul

> consul

  看到下圖所示的命令提示,就代表OK了。

  技術分享圖片

  3.啟動Server(s)

  分別在三臺節點上執行以下命令即可啟動Consul

192.168.80.100>consul agent -server -ui -bootstrap-expect=3 -data-dir=/tmp/consul -node=consul-1 -client=0.0.0.0 -bind=192.168.80.100 -datacenter=dc1

192.168.80.101>consul agent -server -ui -bootstrap-expect=3 -data-dir=/tmp/consul -node=consul-2 -client=0.0.0.0 -bind=192.168.80.101 -datacenter=dc1 -join 192.168.80.100

192.168.80.102>consul agent -server -ui -bootstrap-expect=3 -data-dir=/tmp/consul -node=consul-3 -client=0.0.0.0 -bind=192.168.80.102 -datacenter=dc1 -join 192.168.80.100

  註意101和102的啟動命令中,有一句 -join 192.168.80.100 => 有了這一句,就把101和102加入到了100所在的集群中。

  啟動之後,集群就開始了Vote(投票選Leader)的過程,通過下面的命令可以看到集群的情況:

  技術分享圖片

  在Windows Server虛擬機上啟動:

> consul agent -bind 0.0.0.0 -client 192.168.80.71 -data-dir=C:\Counsul\tempdata -node EDC.DEV.WebServer -join 192.168.80.100

  啟動後會有如下提示:

  技術分享圖片

  4.通過UI查看集群

  Consul不僅提供了豐富的命令查看集群情況,還提供了一個WebUI,默認端口8500,我們可以通過訪問這個URL(eg. http://192.168.80.100:8500)得到如下圖所示的WebUI:

技術分享圖片

  可以看到三個節點都正常啟動,下面我們就來試試向Consul註冊一下我們基於ASP.NET Core的WebAPI服務。

  5.模擬Leader掛掉,查看Consul集群的新選舉Leader

  這裏我暴力一點直接將Leader節點關機:shutdown -h now,可以看到我們的80.100已經掛了。

  技術分享圖片

  查看其余兩個節點的日誌可以發現,consul-3 (80.102)被選為了新的leader:

  技術分享圖片

  當然,也可以通過80.101或102的WebUI查看:

技術分享圖片

  也可以通過以下命令查看目前的各個Server的角色狀態:

> consul operator raft list-peers

  技術分享圖片

  雖然這裏80.100這個原leader節點掛掉了,但是只要超過一半的Server(這裏是2/3還活著)還活著,集群是可以正常工作的,這也是為什麽像Consul、ZooKeeper這樣的分布式管理組件推薦我們使用3個或5個節點來部署的原因。

三、ASP.NET Core WebAPI服務註冊

3.1 準備一個ASP.NET Core WebAPI程序

  Step1.創建一個ASP.NET Core WebAPI程序

  技術分享圖片

  Step2.創建一個HealthController用於Consul的健康檢查

    [Produces("application/json")]
    [Route("api/Health")]
    public class HealthController : Controller
    {
        [HttpGet]
        public IActionResult Get() => Ok("ok");
    }

  *.Consul會通過call這個API來確認Service的健康狀態

  Step3.改寫啟動代碼,調用Consul API註冊服務

  (1)通過Nuget安裝Consul的.NET客戶端

PM> install-package Consul

  (2)基於IApplicationBuilder寫一個擴展方法,用於調用Consul API

    public static class AppBuilderExtensions
    {
        public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IApplicationLifetime lifetime, ServiceEntity serviceEntity)
        {
            var consulClient = new ConsulClient(x => x.Address = new Uri($"http://{serviceEntity.ConsulIP}:{serviceEntity.ConsulPort}"));//請求註冊的 Consul 地址
            var httpCheck = new AgentServiceCheck()
            {
                DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服務啟動多久後註冊
                Interval = TimeSpan.FromSeconds(10),//健康檢查時間間隔,或者稱為心跳間隔
                HTTP = $"http://{serviceEntity.IP}:{serviceEntity.Port}/api/health",//健康檢查地址
                Timeout = TimeSpan.FromSeconds(5)
            };

            // Register service with consul
            var registration = new AgentServiceRegistration()
            {
                Checks = new[] { httpCheck },
                ID = Guid.NewGuid().ToString(),
                Name = serviceEntity.ServiceName,
                Address = serviceEntity.IP,
                Port = serviceEntity.Port,
                Tags = new[] { $"urlprefix-/{serviceEntity.ServiceName}" }//添加 urlprefix-/servicename 格式的 tag 標簽,以便 Fabio 識別
            };

            consulClient.Agent.ServiceRegister(registration).Wait();//服務啟動時註冊,內部實現其實就是使用 Consul API 進行註冊(HttpClient發起)
            lifetime.ApplicationStopping.Register(() =>
            {
                consulClient.Agent.ServiceDeregister(registration.ID).Wait();//服務停止時取消註冊
            });

            return app;
        }

  *.這裏主要是參考的田園的蟋蟀的Code,鏈接見參考資料部分

  (3)在Starup類的Configure方法中,調用此擴展方法

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
            // register this service
            ServiceEntity serviceEntity = new ServiceEntity
            {
                IP = NetworkHelper.LocalIPAddress,
                Port = Convert.ToInt32(Configuration["Service:Port"]),
                ServiceName = Configuration["Service:Name"],
                ConsulIP = Configuration["Consul:IP"],
                ConsulPort = Convert.ToInt32(Configuration["Consul:Port"])
            };
            app.RegisterConsul(lifetime, serviceEntity);
        }

  其中ServiceEntity類定義如下:

技術分享圖片
    public class ServiceEntity
    {
        public string IP { get; set; }
        public int Port { get; set; }
        public string ServiceName { get; set; }
        public string ConsulIP { get; set; }
        public int ConsulPort { get; set; }
    }
View Code

  其中用到了appSettings.json配置文件,其定義如下:

技術分享圖片
{
  "Service": {
    "Name": "CAS.NB.ClientService",
    "Port": "8810"
  },
  "Consul": {
    "IP": "192.168.80.101",
    "Port": "8500"
  },
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  }
}
View Code

  *.這段代碼不再解釋,一眼就能看懂。另外,除了調用Consul API之外,還可以通過配置文件的方式,例如以下配置文件格式,這裏不再演示。

技術分享圖片
{
  "service": {
    "name": "hwapp_web",
    "tags": ["master"],
    "address": "10.9.10.173",
    "port": 5000,
    "checks": [
      {
        "http": "http://10.9.10.173:5000/health",
        "interval": "10s"
      }
    ]
  }
}
View Code

  Step4.保留默認創建的ValuesController,其余不再創建任何API,不是本次實驗的重點。當然,你可以集成一下Swagger,這樣有個界面文檔可以看。

  這裏我默認跳轉到healthcontroller:

  技術分享圖片

3.2 發布到IIS

  Step1.在.NET Core程序中進行發布很簡單,既可以采用原來在VS裏邊創建配置文件進行發布,也可以使用命令行(例如:dotnet publish),這裏我還是在VS裏面發布,得到Release文件

  Step2.通過Ftp工具copy到Windows Server虛擬機中

  Step3.這裏我的Windows Server虛擬機是2008 R2,需要裝一個Windows Server Hosting,下載地址:點我點我。如果不安裝這個,你的IIS是跑不起來.NET Core程序的。

  Step4.按照你熟悉的方式在IIS中添加一個網站(服務):

  技術分享圖片

  Step5.更改默認應用程序池的.net framework版本為“無托管代碼”。

  技術分享圖片

  Step6.照理說,到這裏就OK了,點擊瀏覽訪問,TMD,給我報了個502.5的錯誤

  技術分享圖片

  於是乎網上一陣搜索,發現需要打個補丁(Windows6.1-KB2533623-x64.msu),下載地址:點我點我。

  Step7.安裝補丁之後,重啟IIS,可以成功訪問了=>確保Consul能夠call到我們的服務的health API。

3.3 查看Consul集群狀態

  Step1.訪問Consul的WebUI查看服務是否註冊成功:可以看到我們的ClientService已成功註冊

技術分享圖片

  Step2.Consul不僅僅提供了服務註冊,還提供了服務發現,我們可以通過調用其提供的API來發現服務的IP和Port。

Url>http://192.168.80.100:8500/v1/catalog/service/CAS.NB.ClientService  

技術分享圖片

  *.我們可以看到返回了ClientService的ServiceAddress和ServicePort,就可以通過其組成URL進行服務調用了。當然,我們可能會對一個服務部署多個實例,以組成集群來實現負載均衡。我們可以設置一些負載均衡的策略,假設通過取模運算隨機選擇一個服務地址返回給服務消費者

  Step3.這裏我們將發布的Release文件在Windows Server虛擬機上copy一份,並改一下配置文件,讓其ServiceName變為CAS.NB.ProductService,其Port變為8820,在IIS上再添加一個網站,啟動起來,再通過WebUI查看Consul集群狀態:

技術分享圖片

  Step4.這時我們再通過以下API進行服務發現:

Url>http://192.168.80.100:8500/v1/catalog/service/CAS.NB.ProductService

  技術分享圖片

四、小結與後續工作

  本篇主要基於一個最小化的集群搭建了一個Consul服務治理組件,並將ASP.NET Core API程序註冊到了Consul,並嘗試通過Consul進行服務發現(雖然沒有模擬具體的服務消費)。本篇沒有仔細講述Consul的介紹、優點、缺點,因為本人也沒有啥實際的經驗,因此只能是站在其他園友的肩膀上做個小實驗。ASP.NET Core是一個天生適合微服務的技術,也希望能在我們的學習和推動下,讓公司把.NET Core應用起來,將來能夠跑在Linux和Docker上,這是我目前的目標,與大家共勉。

技術分享圖片

  後續我會繼續嘗試基於Ocelot構建API網關,到時會結合Consul進行進一步的集成。另外,還會嘗試Polly進行熔斷降級、Identity Server進行驗證,Exceptionless作分布式日誌,基本上會遵循.NET Core大隊長張善友的POC-NanoFabric用到的(或者說是給我們安利的技術框架)那些開源技術來搭建一個初步的微服務架構,以便於以後在公司內部推廣和應用。此外,考慮到公司目前的微服務架構是基於Java的(Spring Cloud),那麽也會考慮通過 Steeltoe 來和Spring Cloud進行兼容,讓Java和.Net Core微服務能夠共存(可以參考資料裏的第7個,田園裏的蟋蟀出品)。這裏,也安利一下正在學習微服務的.NET Coder們,可以看看張善友老師的NanoFabric,或者是楊中科老師的.NET Core微服務課程。

  技術分享圖片

  最近很多朋友問我為什麽不再寫技術文了,老是寫一些觀後感、讀後感之類的,一來因為讀那些書其實也是我今年的個人OKR,二來這半年來的確不想看技術書和資料,最後因為身體原因沒法過多地熬夜。現在學習.NET Core以來被其深深吸引,就像是找到了一個那些年我們追過的女孩兒,覺得它樣樣都美,很想跟它繼續走下去,發現它更多的美,因此也會趁著微服務學習的勁跟大家分享更多的文章。

  技術分享圖片

附件下載

  示例代碼:點我下載

參考資料

(1)田園裏的蟋蟀,《Docker & Consul & Fabio & ASP.NET Core 2.0 微服務跨平臺實踐》

(2)不小下,《服務發現 - consul 的介紹、部署和使用》

(3)二胡槽子,《我是服務的執政官-服務發現和註冊工具consul簡介》

(4)大副,《consul分布式集群搭建&簡單功能測試&故障恢復》

(5)94cool,《win2008server R2 x64 部署.net core到IIS》

(6)楊中科《.NET Core微服務課件》

(7)田園裏的蟋蟀,《.NET Core 微服務架構 Steeltoe 使用(基於 Spring Cloud)》

作者:周旭龍

出處:http://edisonchou.cnblogs.com

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。

.NET Core微服務之基於Consul實現服務治理