微服務之:從零搭建ocelot網關和consul集群

来源:https://www.cnblogs.com/xiaoliangge/archive/2019/01/04/10221950.html
-Advertisement-
Play Games

介紹 微服務中有關鍵的幾項技術,其中網關和服務服務發現,服務註冊相輔相成。 首先解釋幾個本次教程中需要的術語 網關 Gateway(API GW / API 網關),顧名思義,是企業 IT 在系統邊界上提供給外部訪問內部介面服務的統一入口,簡化了外部由於多服務協同完成任務時的繁瑣配置。網關組件有Ko ...


介紹

 

微服務中有關鍵的幾項技術,其中網關和服務服務發現,服務註冊相輔相成。

首先解釋幾個本次教程中需要的術語

網關 Gateway(API GW / API 網關),顧名思義,是企業 IT 在系統邊界上提供給外部訪問內部介面服務的統一入口,簡化了外部由於多服務協同完成任務時的繁瑣配置。網關組件有Kong,ocelot,

服務發現:通過網關訪問內部各個微服務,網關要找到所需服務的過程稱為服務發現

服務註冊:既然有服務發現,前提是要把所需服務提前“錄入”,這個錄入的過程稱為服務註冊。服務註冊可配置文件(人肉方式不推薦),也可用服務註冊組件如Consul或者Eureka等等(推薦)

搭建Consul集群(Windows)

官網下載Consul程式,https://www.consul.io/downloads.html

下載下來就是一個可執行文件Consul.exe

Consul有兩種代理模式,一種server,一種client,官方建議Server端達到3台以上才可高可用,但不要太多,太多會給集群間數據同步造成壓力,client數量不限。

多個server端之間會選擇出一個leader,當一個server的leader宕機則會從其他server端”投票“選擇新的leader

實踐

這裡server我們用2台實驗

192.168.74.55

192.168.74.54

1台Client

192.168.74.161

consul啟動有兩種方式一種是命令行,一種是配置文件的方式。

命令行方式啟動一個consul的server端

consul agent -server -ui -bootstrap-expect 2 -data-dir opt/consul/data -node ServerMaster -bind 192.168.74.55 -client 192.168.74.55 關鍵參數說明 -server:server模式啟動 -ui :開啟ui界面(consul.exe內部帶了GUI圖形界面操作)  -bootstrap-expect 2:server端到2個時集群生效 -data-dir:consul產生的文件路徑(consul自己會產生一下數據存儲的位置) -node:此節點名稱 -bind:集群內部通信地址,預設0.0.0.0 -client:此節點綁定的通訊地址 以上只是關鍵參數,以下是完整參數說明:     

但是命令啟動很繁瑣,所以推薦下麵的配置文件的方式啟動

在consul同文件夾下建立一個server.json的配置文件

 
{
  "datacenter": "dc1",
  "data_dir": "opt/consul/data",
  "node_name": "consul-server01",
  "server": true,
  "bootstrap_expect": 2,
  "bind_addr": "192.168.74.55",
  "client_addr": "192.168.74.55",
  "ui":true
}

為了快速啟動,再建立一個bat批處理文件runconsul.bat

consul agent -config-dir server.json
pause

雙擊runconsul.bat啟動consul

在192.168.74.54伺服器開啟一個server端繼續以上操作。

命令方式啟動

consul agent -server -ui -data-dir opt/consul/data -node Server01 -bind 192.168.74.54 -client 192.168.74.54 -join=192.168.74.55

-join將192.168.74.54加入到192.168.74.55伺服器

配置文件方式:

 

{
  "datacenter": "dc1",
  "data_dir": "opt/consul/data",
  "node_name": "consul-server2",
  "server": true,
  "bind_addr": "192.168.74.54",
  "client_addr": "192.168.74.54",
  "ui":true,
  "retry_join": ["192.168.74.55"],
  "retry_interval": "30s",
  "rejoin_after_leave": true,
  "start_join":["192.168.74.55"]
  }

在192.168.74.161伺服器開啟一個consul的client端

命令方式:

consul agent -ui -data-dir opt/consul/data -node ServerSlave  -bind 192.168.74.161 -client 192.168.74.161 -join 192.168.74.55

配置文件方式:

{
  "datacenter": "dc1",
  "data_dir": "opt/consul/data",
  "node_name": "consul-client01",
  "server": false,
  "bind_addr": "192.168.74.161",
  "client_addr": "192.168.74.161",
  "ui":true,
  "retry_join": ["192.168.74.55"],
  "retry_interval": "30s",
  "rejoin_after_leave": true,
  "start_join":["192.168.74.55"]
}

效果 

簡單Consul集群到這裡就搭建成功,只要訪問三台伺服器任意一個都可數據同步,演示:

 

 netcore集成Consul服務註冊

首先新建一個ConsulClient的類庫

 

ConsulRegister.csproj所需組件如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Consul" Version="0.7.2.6" />
    <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.1.0" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.0" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.0" />
  </ItemGroup>

</Project>
 服務發現自動註冊,無需手動綁定本機地址,會自動掃描本地ipv4地址和localhost地址,項目中無需再手動創建健康檢查介面
ServiceDiscoveryOptions.cs

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

namespace ConsulRegister
{
    /// <summary>
    /// 服務治理第三方組件Consul相關配置參數
    /// </summary>
    public class ServiceDiscoveryOptions
    {
        public string ServiceName { get; set; }

        public ConsulOptions Consul { get; set; }
    }

    public class ConsulOptions
    {
        public string HttpEndPoint { get; set; }
    }
}
RegisterToConsulExtension.cs

using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace ConsulRegister
{
    public static class RegisterToConsulExtension
    {
        /// <summary>
        /// Add Consul
        /// 添加consul
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static IServiceCollection AddConsul(this IServiceCollection services, IConfiguration configuration)
        {
            // configuration Consul register address
            //配置consul註冊地址
            services.Configure<ServiceDiscoveryOptions>(configuration.GetSection("ServiceDiscovery"));
            
            //configuration Consul client
            //配置consul客戶端
            services.AddSingleton<IConsulClient>(sp => new Consul.ConsulClient(config =>
            {
                var consulOptions = sp.GetRequiredService<IOptions<ServiceDiscoveryOptions>>().Value;
                if (!string.IsNullOrWhiteSpace(consulOptions.Consul.HttpEndPoint))
                {
                    config.Address = new Uri(consulOptions.Consul.HttpEndPoint);
                }
            }));

            return services;
        }

        /// <summary>
        /// use Consul
        /// 使用consul
        /// The default health check interface format is http://host:port/HealthCheck
        /// 預設的健康檢查介面格式是 http://host:port/HealthCheck
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
        {
            IConsulClient consul = app.ApplicationServices.GetRequiredService<IConsulClient>();
            IApplicationLifetime appLife = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
            IOptions<ServiceDiscoveryOptions> serviceOptions = app.ApplicationServices.GetRequiredService<IOptions<ServiceDiscoveryOptions>>();
            var features = app.Properties["server.Features"] as FeatureCollection;

            var port = new Uri(features.Get<IServerAddressesFeature>()
                .Addresses
                .FirstOrDefault()).Port;
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine($"application port is :{port}");
            var addressIpv4Hosts = NetworkInterface.GetAllNetworkInterfaces()
            .OrderByDescending(c => c.Speed)
            .Where(c => c.NetworkInterfaceType != NetworkInterfaceType.Loopback && c.OperationalStatus == OperationalStatus.Up);

            foreach (var item in addressIpv4Hosts)
            {
                var props = item.GetIPProperties();
                //this is ip for ipv4
                //這是ipv4的ip地址
                var firstIpV4Address = props.UnicastAddresses
                    .Where(c => c.Address.AddressFamily == AddressFamily.InterNetwork)
                    .Select(c => c.Address)
                    .FirstOrDefault().ToString();
                var serviceId = $"{serviceOptions.Value.ServiceName}_{firstIpV4Address}:{port}";

                var httpCheck = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),
                    Interval = TimeSpan.FromSeconds(30),
                    //this is default health check interface
                    //這個是預設健康檢查介面
                    HTTP = $"{Uri.UriSchemeHttp}://{firstIpV4Address}:{port}/HealthCheck",
                };

                var registration = new AgentServiceRegistration()
                {
                    Checks = new[] { httpCheck },
                    Address = firstIpV4Address.ToString(),
                    ID = serviceId,
                    Name = serviceOptions.Value.ServiceName,
                    Port = port
                };

                consul.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
                                
                //send consul request after service stop
                //當服務停止後想consul發送的請求
                appLife.ApplicationStopping.Register(() =>
                {
                    consul.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();
                });

                Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine($"health check service:{httpCheck.HTTP}");
            }

            //register localhost address
            //註冊本地地址
            var localhostregistration = new AgentServiceRegistration()
            {
                Checks = new[] { new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),
                    Interval = TimeSpan.FromSeconds(30),
                    HTTP = $"{Uri.UriSchemeHttp}://localhost:{port}/HealthCheck",
                } },
                Address = "localhost",
                ID = $"{serviceOptions.Value.ServiceName}_localhost:{port}",
                Name = serviceOptions.Value.ServiceName,
                Port = port
            };

            consul.Agent.ServiceRegister(localhostregistration).GetAwaiter().GetResult();

            //send consul request after service stop
            //當服務停止後想consul發送的請求
            appLife.ApplicationStopping.Register(() =>
            {
                consul.Agent.ServiceDeregister(localhostregistration.ID).GetAwaiter().GetResult();
            });

            app.Map("/HealthCheck", s =>
            {
                s.Run(async context =>
                {
                    await context.Response.WriteAsync("ok");
                });
            });
            return app;
        }
    }
}

再新建一個.netcore的webapi項目WebA,並且引用ConsulRegister項目

在WebA項目中的Startup.cs文件中加入Consul服務

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddConsul(Configuration);
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

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

            app.UseConsul();
            app.UseMvc();
        }

在WebA項目的appsettings.json配置文件中加入以下Consul服務端配置

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  
  "ServiceDiscovery": {
    "ServiceName": "A",
    "Consul": {
      "HttpEndpoint": "http://192.168.74.161:8500"
    }
  }
}

這裡服務註冊就算完成

Ocelot網關搭建

接下來繼續Ocelot藉助於Consul實現服務發現

新建項目Ocelot.Gateway

將以下依賴加入Ocelot.Gateway.csproj中:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Ocelot" Version="12.0.1" />
    <PackageReference Include="Ocelot.Provider.Consul" Version="0.1.2" />
  </ItemGroup>

  <ItemGroup>
    <Content Update="ocelot.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

</Project>

新建ocelot.json文件

{
  "ReRoutes": [
    {
      "UseServiceDiscovery": true, 
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "ServiceName": "A",
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      },
      "UpstreamPathTemplate": "/a/{url}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "ReRoutesCaseSensitive": false 
    }
  ],
  "GlobalConfiguration": {
    // 使用Consul服務治理
    "ServiceDiscoveryProvider": {
      "Host": "192.168.74.161",
      "Port": 8500,
      "ConfigurationKey": "Oceolot_A" //存儲在Consul上的Key
    }
  }
}

修改Startup.cs文件如下:

   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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);


            services.AddOcelot(
                 new ConfigurationBuilder()
                 .AddJsonFile("ocelot.json", optional: false, reloadOnChange: true).Build())
                 .AddConsul()
                 .AddConfigStoredInConsul();
        }

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

            app.UseHttpsRedirection();
            app.UseOcelot().Wait();
        }
    }

發佈WebA後複製兩份分別啟動

dotnet WebA.dll --urls="http://0.0.0.0:2001"

dotnet WebA.dll --urls="http://0.0.0.0:2002"

到這裡相當於2001和2002程式簡單集群了一下

可以發現日誌中有 http://192.168.74.161:2002/HealthCheck調用信息:

這其實是consul進行健康檢查進行的調用。

啟動多個程式後,打開瀏覽器打開Consuld界面會發現註冊了兩個服務

 

 

這裡ocelot網關和consul的服務註冊和發現就算初步集成。

如果生成環境是windows的情況,將consul做成windwos服務即可

sc create "ConsulServer" binPath="F:\XXX\consul.exe agent -config-dir XXX.json"

生產環境是linux則藉助systemd做成守護進程即可 

目前集群搭建成功,但是連接的話如果指定某個端點的ip進行連接,端點宕機,就會導致網關一樣無法連接consul進行服務發現。所以還需進行配置暴露一個端點讓客戶端連接,配置詳情:https://www.consul.io/docs/connect/configuration.html

不過也可以做成虛擬ip進行多台consul的負載。客戶端連接虛擬ip即可

項目地址:

github地址

 

 

 

 

 

 

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天在寫一個StringIO.write(int)示例時思維那麼一發散就拐到了字元集的問題上,順手搜索一發,除了極少數以外,絕大多數中文博客都解釋的慘不忍睹,再鑒於被此問題在oracle的字元集體系中蹂躪過,因此在過往筆記的基礎上增刪了幾個示例貼出來。 Python2: 首先清楚兩個Python 2 ...
  • 什麼是main方法? 是類中的一段代碼,可以讓程式獨立運行。 HelloWorld 類中有main()方法,說明這是個java應用程式,通過JVM直接啟動運行的程式。這個main()方法的聲明為:public static void main(String args[])。必須這麼定義,這是Java ...
  • 只對當前文件有效的操作:菜單欄->View -> Active Editor -> Use Soft Wraps; 如果想對所有文件都有效,就要在setting裡面進行操作:File-> Setting-> Editor-> General -> Use soft wraps in editor。 ...
  • 寫在前面 很高興我這系列的文章寫道第18篇了,今天寫一個爬蟲愛好者特別喜歡的網站煎蛋網 ,這個網站其實還是有點意思的,網站很多人寫了N多的教程了,各種方式的都有,當然網站本身在爬蟲愛好者的不斷進攻下,也在不斷的完善,反爬措施也很多,今天我用 在揍他一波。 整體看上去,煎蛋網的妹子圖質量還是可以的,不 ...
  • [TOC] 1. URL的組成 漢字通過URL encode(UTF 8)編碼出來的編碼,裡面的字元全是打位元組 如果你複製粘貼下來這個網址,出來的不是漢字,而是編碼後的位元組 https://www.baidu.com/s?wd=%E7%BC%96%E7%A8%8B%E5%90%A7 我們也可以在py ...
  • 刪除 # 刪除 # 只能刪除文件,若為目錄則報錯# 若文件正在使用,Windows下會直接報錯,Linux下會在目錄表中刪除記錄,但文件還在# 與os.unlink()一樣的作用os.remove(r"C:\Users\Microsoft\Desktop\111.exe")# 刪除目錄,目錄必須為空 ...
  • 對象序列化的目標是將對象保存在磁碟中,或者允許在網路中直接傳輸對象。 對象序列化允許把記憶體中的Java對象轉換成平臺無關的二進位流,從而允許把這種二進位流持久保存在磁碟上或者通過網路將這種二進位流傳輸到另外一個網路節點。 其他程式一旦獲得了這種二進位流,都可以將這種二進位流恢覆成原本的Java對... ...
  • 1 private static string IsOpenSendMail = ConfigurationManager.AppSettings["IsOpenSendMail"]; //是否啟用異常消息發送郵箱功能 2 private static string SendUser=Configu... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...