主頁 > .NET開發 > .NetCore·集成Ocelot組件之完全解決方案

.NetCore·集成Ocelot組件之完全解決方案

2020-09-09 22:50:03 .NET開發

閱文時長 | 44.22分鐘 字數統計 | 21003.55字符
『.NetCore·集成Ocelot組件之完全解決方案』
撰寫人 | SCscHero 撰寫時間 | Wednesday, September 8, 2020
文章型別 | 系列 完成度 | 待完善
座右銘 每一個偉大的事業,都有一個微不足道的開始,Hello World!

一、前言、環境說明、預備知識 完成度:100%

a) 開發環境

作業系統:Windows10專業版 版本1903(OS內部版本18362.1016)
開發工具:Visual Studio Enterprise 2019 版本16.4.1
開發框架:ASP.NET Core 3.1
本文相關Nuget包:
Ocelot
Ocelot.Provider.Consul
Ocelot.Provider.Polly
Ocelot.Cache.CacheManager
Microsoft.VisualStudio.Web.CodeGeneration.Design
Microsoft.Extensions.Caching.Memory(可選:Ocelot快取)
Microsoft.Extensions.Caching.Abstractions(可選:Ocelot快取)
IdentityServer4.AccessTokenValidation(可選:集成IDS4專用)
MMLib.SwaggerForOcelot(可選:集成下游Swagger Restful API檔案專用)

b) 程式包管理器控制臺CLI

Install-Package Ocelot -Version 16.0.1
Install-Package Ocelot.Provider.Consul -Version 16.0.1
Install-Package Ocelot.Provider.Polly -Version 16.0.1
Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design -Version 16.0.1
Install-Package Microsoft.Extensions.Caching.Memory -Version 3.1.6
Install-Package Microsoft.Extensions.Caching.Abstractions -Version 3.1.6
Install-Package IdentityServer4.AccessTokenValidation -Version 3.0.1
Install-Package MMLib.SwaggerForOcelot -Version 2.5.1

c) 預備知識

首先,要有.Net Core API專案的一些基礎,并充分理解API網關,關于API網關,可以看下博主的這篇文章,相信可以得到不錯的領悟,傳送門
然后,光有了API網關的儲備知識還是不夠的,需要了解一下注冊中心,.Neter一般都選用Consul,關于Consul的簡單安裝、使用,可以參考傳送門,博主之后會繼續寫下Consul集群、配置、命令等,現在如果要研究的話,可以Google、Baidu一些資料,Ocelot一般要結合注冊中心一起使用,可以實現動態拓展、服務發現等功能,

二、Ocelot基本使用 完成度:100%

a) 快速上手Ocelot

在理解了API網關之后,正式開干,快速集成主要分為幾個步驟:
步驟1:網關是需要獨立部署的,所以準備一個新的.Net Core3.1的WebAPI專案吧,然后上文的需要的Nuget包安裝上,
步驟2:創建一個Json格式的組態檔,了解相關配置,使用相應功能,詳細見下文"Ocelot基本配置",
步驟3:配置Program.cs檔案下的CreateHostBuilder方法,引入組態檔,詳細見下文"Ocelot基本配置",
步驟4:配置Startup.cs中的ConfigureServices()方法和Configure()方法,配置服務注冊和中間件,詳細見下文"Ocelot基本配置",
步驟5:完成前面四步,一個最基本的Ocelot Gateway就搭建好了,

b) Ocelot基本配置

先配置Startup.cs檔案,在ConfigureServices()方法中,添加代碼:

services.AddOcelot().AddConsul().AddPolly();//此處是添加了Consul服務、Polly服務,如果不需要可以去掉,只需要AddOcelot()即可,

Configure()方法中,UseRouting()中間件之前,添加Ocelot中間件:

app.UseOcelot().Wait();

Startup.cs檔案配置完成,現在在專案中添加一個json檔案,暫且命名為"Ocelot_Config_Origin.json",添加內容(注釋是本博主加的,以便更好的學習之用):

//無Consul配置,簡單配置,包含兩大配置塊,轉發路由和全域配置
{
  // 轉發路由,陣列中的每個元素都是某個服務的一組路由轉發規則
  "ReRoutes": [
    {
      // 下游(服務提供方)服務路由模板
      "DownstreamPathTemplate": "/api/{path}",
      // Uri方案,http、https
      "DownstreamScheme": "https",
      // 服務地址和埠,如果是集群就設定多個
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 6001
        },
        {
          "Host": "localhost",
          "Port": 6002
        }
      ],
      // 允許的HTTP請求方法,可以寫多個
      "UpstreamHttpMethod": [ "Get", "Post" ],
      // 上游(客戶端,服務消費方)請求路由模板
      "UpstreamPathTemplate": "/OcelotNotes/{path}",
      // 負載均衡,只有上面匹配了集群,才有效
      /*
       負載均衡演算法,總共三種型別,
        LeastConnection:最小鏈接,將請求發往最空閑的那個服務器
        RoundRobin:輪詢,輪流發送
        NoLoadBalance:無負載均衡,總是發往第一個請求或者是服務發現
        */
      "LoadBalancerOptions": {
        "Type": "RoundRobin" // 輪詢
      }
    },
    {
      // 下游(服務提供方)服務路由模板
      "DownstreamPathTemplate": "/api/{path}",
      // Uri方案,http、https
      "DownstreamScheme": "http",
      // 服務地址和埠,如果是集群就設定多個
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 7001
        },
        {
          "Host": "localhost",
          "Port": 7002
        }
      ],
      // 允許的HTTP請求方法,可以寫多個
      "UpstreamHttpMethod": [ "Get", "Post" ],
      // 上游(客戶端,服務消費方)請求路由模板
      "UpstreamPathTemplate": "/MyServiceB/{path}",
      // 負載均衡,只有上面匹配了集群,才有效
      "LoadBalancerOptions": {
        "Type": "RoundRobin" // 輪詢
      }
    }
  ],
  // 全域配置,此節點的配置會覆寫Routes,可以在這里設定一些通用的配置
  "GlobalConfiguration": {
    "ReRouteIsCaseSensitive": false// 路由是否區分大小寫
  }
}

添加完畢后,我們先來簡單看下這段Json,可見這些內容中可分為"GlobalConfiguration"和"Routes"兩大部分(Ocelot的16.0.1版本是此名,舊版本Key為ReRoutes),Routes中可包含多個物件,以供匹配不同的路由,轉發到下游,在不結合注冊中心的情況下,GlobalConfigurationRoutes中的配置并不一樣,GlobalConfiguration中的配置較為簡單,為公共的一些屬性,Routes中的內容,比如負載均衡、訪問方法限制,注釋已經寫得很詳盡了,只說下路由模板這個地方:

  1. 路由模板分為UpstreamPathTemplate上游模板和DownstreamHttpMethod下游模板,類似于Nginx、DNS,路由訪問網關模板路徑,將被轉發到相應微服務的下游模板中,
  2. 模板中的變數可以用"{}"符號包起來,如上述代碼示例中,博主使用了{path}變數,即OcelotNotes后面的內容都當作變數轉發到下游模板api下,當然可以多指定幾個變數,改變順序也是可以的,
  3. DownstreamHostAndPorts是配置微服務集群的,在不結合注冊中心Consul的前提下,微服務集群每個行程的地址和埠都需要在這里寫死,

Startup.cs檔案和Json檔案配置完成后,我們在Program.cs檔案中,將配置好的Json檔案加入到配置中,修改一下CreateHostBuilder()方法:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration(c =>
                {
                    c.AddJsonFile("Ocelot_Config_Origin.json", optional: false, reloadOnChange: true);
                })

             .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

OK,配置完畢,啟動測驗吧,測驗方式:網關地址:埠+上游模板,看是否跳轉微服務地址,

c) Ocelot結合Consul服務

再新建一個Json檔案,命名為Ocelot_Config_Consul.json,寫入以下內容,與Ocelot_Config_Origin.json檔案中的內容相比,GlobalConfiguration中的內容添加配置注冊中心的部分,以及Routes部分取消掉寫死的DownstreamHostAndPorts,使用ServiceName對應注冊中心的服務集群名稱,Ocelot會到注冊中心服務發現,

{
  //路由配置(16.1版本將ReRoutes換成Routes)
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/{url}", // 下游(服務提供方)服務路由模板
      "DownstreamScheme": "https", //下游協議
      //"DownstreamHttpMethod": [ "Get","Post", "Put", "Delete" ], //下游Http請求型別
      "DownstreamHttpMethod": "Get", //下游Http請求型別?????
      "UpstreamHttpMethod": [ "Options", "Get", "Post", "Put", "Delete" ], //上游允許的Http請求方式
      "UseServiceDiscovery": true, //啟用服務發現,若Ocelot集合Consul必須配置此項
      "UpstreamPathTemplate": "/OcelotNotes/{url}", //Hero:上游路徑模版
      "ServiceName": "MicroService.SCscHero.Notes", //服務名稱
      /*
       負載均衡演算法,總共三種型別,
        LeastConnection:最小鏈接,將請求發往最空閑的那個服務器
        RoundRobin:輪詢,輪流發送
        NoLoadBalance:無負載均衡,總是發往第一個請求或者是服務發現
        */
      "LoadBalancerOptions": {
        "Type": "LeastConnection"
      }
      //,
      //"QoSOptions": {
      //  "ExceptionsAllowedBeforeBreaking": 3, //允許多少個例外請求
      //  "DurationOfBreak": 10000, // 熔斷的時間,單位為ms
      //  "TimeoutValue": 10000 //如果下游請求的處理時間超過多少則自如將請求設定為超時 默認90秒
      //}
      //"DownstreamHttpVersion": "",
      //"AddHeadersToRequest": {},
      //"AddClaimsToRequest": {},
      //"RouteClaimsRequirement": {},
      //"AddQueriesToRequest": {},
      //"RequestIdKey": "",
      //"FileCacheOptions": {
      //  "TtlSeconds": 0,
      //  "Region": ""
      //},
      //"AuthenticationOptions": {
      //  "AuthenticationProviderKey": "",
      //  "AllowedScopes": []
      //},
      //"HttpHandlerOptions": {
      //  "AllowAutoRedirect": true,
      //  "UseCookieContainer": true,
      //  "UseTracing": true,
      //  "MaxConnectionsPerServer": 100 //這控制內部HttpClient將打開多少連接,這可以在路線或全球一級設定,
      //},
      //"DangerousAcceptAnyServerCertificateValidator": false,
      ////路由限流配置
      //"RateLimitOptions": {
      //  "ClientWhitelist": [], //白名單
      //  "EnableRateLimiting": false, //是否啟用限流
      //  "Period": "5s", //統計時間段:1s,5m,1h,1d
      //  "PeriodTimespan": 10, //客戶端可以重試時間,單位秒
      //  "Limit": 3 //在統計時間段內允許的最大請求數量
      //},
      ////Polly配置
      //"QoSOptions": {
      //  "ExceptionsAllowedBeforeBreaking": 3, //允許多少個例外請求
      //  "DurationOfBreak": 5, //熔斷的時間,單位為秒
      //  "TimeoutValue": 6000 //如果下游請求的處理時間超過多少則自動將請求設定為超時
      //},

      //"Priority": 0 //優先權順序
    }
  ],

  //全域配置
  "GlobalConfiguration": {
    //"BaseUrl": "http://127.0.0.1:6299", //網關對外地址
    "RequestIdKey": "OcRequestId",
    "ReRouteIsCaseSensitive": true, //是否區分路由字母大小寫
    "ServiceDiscoveryProvider": { //服務發現提供者,配置Consul地址
      "Host": "localhost", //Consul主機名稱
      "Port": 8501, //Consul埠號
      "Type": "Consul" //必須指定Consul服務發現型別
    }
    //,
    ////限流相關配置
    //"RateLimitOptions": {
    //  "ClientIdHeader": "ClientId",
    //  "QuotaExceededMessage": "RateLimit SCscHero", //限流回應提示
    //  "RateLimitCounterPrefix": "ocelot",
    //  "DisableRateLimitHeaders": false,
    //  "HttpStatusCode": 429
    //}
  }
}

d) 路由優先級

如果訪問路由被多個上游模板匹配,則按照優先級來決定轉發到哪個下游模板,優先級屬性是Routes物件的Priority,資料型別是Int,數值越大,優先級越高,

e) 組態檔合并

問題1:檔案合并是什么?
檔案合并將多個檔案合并成一個檔案,在Ocelot中的檔案合并,是將拆分的多個Json檔案合并成一個Json檔案,
問題2:為什么要進行檔案拆分和合并?
隨著微服務的增多,Ocelot的Routes配置變得越來越繁雜,維護起來易出錯,所以將一個完整的Ocelot組態檔拆分成多個包含Routes的檔案和一個包含GlobalConfiguration的檔案,此舉可以提高組態檔的拓展性和伸縮性;

關于檔案合并,在Ocelot中,已經為我們封裝好了檔案合并的方法,原始碼如下:

        public static IConfigurationBuilder AddOcelot(
          this IConfigurationBuilder builder,
          IWebHostEnvironment env) => builder.AddOcelot(".", env);

        public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder, IWebHostEnvironment env)
        {
            string excludeConfigName = env == null || env.EnvironmentName == null ? string.Empty : "ocelot." + env.EnvironmentName + ".json";
            Regex reg = new Regex("^ocelot\\.(.*?)\\.json$", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            List<FileInfo> list = new DirectoryInfo(folder).EnumerateFiles().Where<FileInfo>((Func<FileInfo, bool>)(fi => reg.IsMatch(fi.Name) && fi.Name != excludeConfigName)).ToList<FileInfo>();
            FileConfiguration fileConfiguration1 = new FileConfiguration();
            foreach (FileInfo fileInfo in list)
            {
                if (list.Count <= 1 || !fileInfo.Name.Equals("ocelot.json", StringComparison.OrdinalIgnoreCase))
                {
                    FileConfiguration fileConfiguration2 = JsonConvert.DeserializeObject<FileConfiguration>(System.IO.File.ReadAllText(fileInfo.FullName));
                    if (fileInfo.Name.Equals("ocelot.global.json", StringComparison.OrdinalIgnoreCase))
                        fileConfiguration1.GlobalConfiguration = fileConfiguration2.GlobalConfiguration;
                    fileConfiguration1.Aggregates.AddRange((IEnumerable<FileAggregateRoute>)fileConfiguration2.Aggregates);
                    fileConfiguration1.Routes.AddRange((IEnumerable<FileRoute>)fileConfiguration2.Routes);
                }
            }
            System.IO.File.WriteAllText("ocelot.json", JsonConvert.SerializeObject((object)fileConfiguration1));
            builder.AddJsonFile("ocelot.json", false, false);
            return builder;
        }

看了原始碼之后,我們看到了此段代碼主要是用正則運算式遍歷符合命名條件的檔案,進行內容組裝,綜上,為了避坑,歸納幾點注意事項:

  • 分拆檔案命名必須為:ocelot.[XXXX].json
  • 分拆檔案不能命名為:ocelot.[環境變數值].json本條中容易被忽略,也容易踩坑很久出不來
  • 分拆檔案的全域組態檔須要命名為ocelot.global.json

了解了檔案合并的原理后,我們把原來的檔案分拆成多個檔案,并遵循上述命名方式,在拓展Json檔案的時候,注意看下檔案屬性->復制到輸出目錄的選項,是否是"如果較新則復制",

接著,我們需要修改Program.cs檔案中檔案引入方式,之前是引入方式是這樣的:

                .ConfigureAppConfiguration(c =>
                {
                    c.AddJsonFile("Ocelot_Config_Consul.json", optional: false, reloadOnChange: true);
                })

現在,將原來的CreateHostBuilder()方法替換為:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        config
                            .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
                            .AddJsonFile("appsettings.json", true, true)
                            .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true,true)//引入與環境有關配置
                            .AddOcelot(hostingContext.HostingEnvironment)//呼叫Ocelot封裝的檔案合并
                            .AddEnvironmentVariables();

                    });
                });

配置完成后,重新運行,會生成一個ocelot.json檔案,我們之前修改命名后的檔案折疊到了此檔案的下面(拓展檔案的名字的前綴與已存在的檔案名字一致時,這兩個json檔案會折疊在一起),

OK,檔案合并已完成,快去檢查下新生成的ocelot.json檔案中的配置是否達到預期功能了呢?

f) 自定義負載均衡

在上文的配置注釋中,我們發現Ocelot支持三種負載均衡策略(實際是兩個引數,還有一種是無負載均衡),這顯然功能有些"單薄",
ocelot支持的負載均衡型別:
LeastConnection:最小鏈接,將請求發往最空閑的那個服務器
RoundRobin:輪詢,輪流發送
NoLoadBalance:無負載均衡,總是發往第一個請求或者是服務發現

我們可以拓展自定義的負載均衡演算法Ocelot官方檔案寫的很清楚了,不過貌似是舊版本的?看著介面引數變了,如下是博主寫的一個"隨機負載均衡"以此為例,說下如何應用,
創建一個類檔案,命名為"CustomRandomLoadBalancer.cs",

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Responses;
using Ocelot.Values;

namespace MicroService.SCscHero.Gateway.OcelotExtension
{
    /// <summary>
    /// 自定義負載均衡策略
    /// </summary>
    public class CustomRandomLoadBalancer : ILoadBalancer
    {
        private readonly Func<Task<List<Service>>> _services;
        private readonly object _lock = new object();

        private int _index;
        public CustomRandomLoadBalancer()
        {
            
        }
        public CustomRandomLoadBalancer(Func<Task<List<Service>>> services)
        {
            _services = services;
        }

        public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
        {
            var serviceList = await _services();

            if (serviceList == null)
                return new ErrorResponse<ServiceHostAndPort>(new ErrorInvokingLoadBalancerCreator(new Exception("負載均衡演算法錯誤")));

            lock (_lock)
            {
                if (serviceList.Count == 1)
                    return new OkResponse<ServiceHostAndPort>(serviceList[0].HostAndPort);
                _index = new Random().Next(serviceList.Count);
                return new OkResponse<ServiceHostAndPort>(serviceList[_index].HostAndPort);
            }
        }

        public void Release(ServiceHostAndPort hostAndPort)
        {

        }
    }
}

Startup.cs檔案中的ConfigureService()方法中的Ocelot服務后的AddCustomLoadBalancer()方法中注冊自定義負載均衡策略,

.AddCustomLoadBalancer<CustomRandomLoadBalancer>();//自定義負載均衡策略

修改Json組態檔中的LoadBalancerOptions物件中的Type屬性為"CustomRandomLoadBalancer",

    "LoadBalancerOptions": {
        "Type": "CustomRandomLoadBalancer"
    },

g) 更多的Ocelot配置

建議訪問官方檔案:傳送門

三、Ocelot功能挖掘 完成度:100%

a) 快取

安裝Nuget包后,在Startup.cs檔案的ConfigureService()方法中注冊服務,

            services.AddOcelot().AddConsul().AddPolly()
                //Ocelot快取
                .AddCacheManager(x =>
                {
                    x.WithDictionaryHandle(); //默認字典存盤
                });

接著在Json組態檔的Routes物件中添加配置

      //快取配置
      "FileCacheOptions": {
        "TtlSeconds": 15,//快取更新秒數
        "Region": "UserCache" //可以呼叫Api清理
      }

配置完成后,就可以測驗快取是否生效,可以在介面中回傳當前時間,在15秒內更新看是否有變化,

b) 自定義快取

在某些業務場景中,可能需要我們自定義快取邏輯,可以在Startup.cs檔案的ConfigureService中將內置IOC容器中Ocelot快取介面的實作替換掉,替換實作命名為OcelotCustomCache

            //將IOcelotCache<CachedResponse>替換成OcelotCustomCache
            services.AddSingleton<IOcelotCache<CachedResponse>, OcelotCustomCache>();

在替換前可以下載Ocelot原始碼、或反編譯看下快取的原始碼,

然后我們在新創建的OcelotCustomCache檔案中,重寫一下IOcelotCache介面的方法,以下是博主的重寫示例,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ocelot.Cache;

namespace MicroService.SCscHero.Gateway.OcelotExtension
{

    public class OcelotCustomCache : IOcelotCache<CachedResponse>
    {
        private class CacheDataModel
        {
            public CachedResponse CachedResponse { get; set; }
            public DateTime Timeout { get; set; }
            public string Region { get; set; }
        }

        private static Dictionary<string, CacheDataModel> CustomCacheDictionary = new
             Dictionary<string, CacheDataModel>();

        public void Add(string key, CachedResponse value, TimeSpan ttl, string region)
        {
            Console.WriteLine($"This is {nameof(OcelotCustomCache)}.{nameof(Add)}");
            CustomCacheDictionary[key] = new CacheDataModel()
            {
                CachedResponse = value,
                Region = region,
                Timeout = DateTime.Now.Add(ttl)
            };
        }

        public void AddAndDelete(string key, CachedResponse value, TimeSpan ttl, string region)
        {
            Console.WriteLine($"This is {nameof(OcelotCustomCache)}.{nameof(AddAndDelete)}");
            CustomCacheDictionary[key] = new CacheDataModel()
            {
                CachedResponse = value,
                Region = region,
                Timeout = DateTime.Now.Add(ttl)
            };
        }

        public void ClearRegion(string region)
        {
            Console.WriteLine($"This is {nameof(OcelotCustomCache)}.{nameof(ClearRegion)}");
            var keyList = CustomCacheDictionary.Where(kv => kv.Value.Region.Equals(region)).Select(kv => kv.Key);
            foreach (var key in keyList)
            {
                CustomCacheDictionary.Remove(key);
            }
        }

        public CachedResponse Get(string key, string region)
        {
            Console.WriteLine($"This is {nameof(OcelotCustomCache)}.{nameof(Get)}");
            if (CustomCacheDictionary.ContainsKey(key) && CustomCacheDictionary[key] != null
                && CustomCacheDictionary[key].Timeout > DateTime.Now
                && CustomCacheDictionary[key].Region.Equals(region))
            {
                return CustomCacheDictionary[key].CachedResponse;
            }
            else
                return null;
        }
    }
}

重寫完后,依照上文的配置,測驗一下控制臺有沒有輸出即可看出是否生效,

c) 熔斷、超時

在安裝了Nuget包之后,在Startup.cs檔案中的ConfigureService()方法配置Polly服務:

               .AddPolly()

在Json組態檔的Routes物件中添加配置

      "QoSOptions": { //超時、熔斷
        "ExceptionsAllowedBeforeBreaking": 3, //允許多少個例外請求
        "DurationOfBreak": 10000, // 熔斷的時間,單位為ms
        "TimeoutValue": 10 //超時時間,單位為ms, 默認90秒
      }

配置完成后,可修改熔斷配置中的TimeoutValue屬性設為10和10000,作對比,測驗是否實作熔斷功能,

d) 限流

令牌桶(Token Bucket)、漏桶(Leaky Bucket)、計數器演算法是常用的三種限流演算法,Ocelot的限流演算法就是計數器演算法,限流的配置,只需要在Json組態檔的Routes物件中加入如下配置,

      "RateLimitOptions": {
        "ClientWhitelist": [ "SCscHeroTokenAPI ],
        //白名單功能,在Request的Header增加名為ClientId的Key,輸入白名單中的元素的Value即可訪問(區分大小寫),
        "EnableRateLimiting": true,//限流是否開啟
        "Period": "5m", //1s, 5m, 1h, 1d
        "PeriodTimespan": 30, //多少秒之后客戶端可以重試
        "Limit": 5 //統計時間段內允許的最大請求數量
      } 

GlobalConfiguration物件中加入如下代碼,

    //限流相關配置
    "RateLimitOptions": {
      "ClientIdHeader": "ClientId",//Request請求頭的Key
      "QuotaExceededMessage": "RateLimit SCscHero", //限流回應的自定義提示
      "RateLimitCounterPrefix": "ocelot",//待研究,看翻譯應該是限流計數器前綴
      "DisableRateLimitHeaders": false,
      "HttpStatusCode": 666
    }

配置完成后,我們看下這段配置能實作什么樣的限流效果,以代碼示例,效果是5min之內,最多訪問5次,超過限制次數后,彈出自定義提示訊息"RateLimit SCscHero",30秒后重新計算次數,如果請求頭中存在鍵為"ClientId",值為"SCscHeroTokenAPI",則白名單通過,不進行限流,(注意理解Period和Limit,并不是"5min內訪問5次滿后,等5min之后才能訪問"

四、Ocelot集成其他組件 完成度:10%

a) Nginx

結合Nginx或其他負載均衡服務器實作網關層的負載均衡,此部分暫時未寫好,請等待更新,

b) Swagger

與下游Swagger結合生成下游服務的API檔案(可以囊括分組的Swagger檔案),效果展示圖如下,此部分暫時未寫好,請等待更新,

c) IdentityServer4

與IdentityServer4可以實作授權認證、權限檢查、JWT、授權碼認證OAuth2.0等相關功能,此部分暫時未寫好,請等待更新,

五、避坑指南 完成度:100%

坑1:本地開發中可能會遇到,Consul和微服務部署在相同IP的服務器上,可以從原始碼中看出,其被翻譯成了主機名:埠號,但在實際的部署中,Consul和任何一個微服務節點,都不可能在同一個服務器/容器中,他們IP不可能相同,
在本機上,有兩個處理方法,第一個處理方式:Consul的node節點改成局域網IP,第二個處理方式:微服務實體注冊的時候,Url將localhost更改成局域網IP,


坑2:微服務注冊Consul時,如果帶http/https協議頭,則在使用Ocelot時候,控制器可能會列印如下日志,所以微服務在注冊的時候,將Scheme可以去掉,協議頭在Json組態檔的DownstreamScheme屬性配置即可,

dbug: Ocelot.LoadBalancer.Middleware.LoadBalancingMiddleware[0]
	  requestId: 8000001a-0004-ff00-b63f-84710c7967bb, previousRequestId: no previous request id, message: there was an error leasing the loadbalancer, setting pipeline error

warndbug: Ocelot.LoadBalancer.Middleware.LoadBalancingMiddleware[0]
	  requestId: 8000001a-0004-ff00-b63f-84710c7967bb, previousRequestId: no previous request id, message: There were no services in NoLoadBalancer

warn: Ocelot.Responder.Middleware.ResponderMiddleware[0]
	  requestId: 8000001a-0004-ff00-b63f-84710c7967bb, previousRequestId: no previous request id, message: Error Code: ServicesAreEmptyError Message: There were no services in NoLoadBalancer errors found in ResponderMiddleware. Setting error response for request path:/api/values, request method: GET

dbugdbug: Ocelot.Errors.Middleware.ExceptionHandlerMiddleware[0]
	  requestId: 8000001a-0004-ff00-b63f-84710c7967bb, previousRequestId: no previous request id, message: ocelot pipeline finished`

坑3:在Ocelot組態檔中的Routes物件中的DownstreamHttpMethod屬性,它的值是只接收一個方法,另外,博主沒搞明白這個屬性的用處在哪里?煩請知道的小伙伴告知一下,博主之前配置了幾個方法,一直報錯,類似坑2的錯誤,后來刪掉了這個屬性就OK了,所以,再三注意UpstreamHttpMethod屬性介面的是個陣列,DownstreamHttpMethod接收的是一個值,

坑4:閱讀其列印后的資訊,發現錯誤資訊包含兩句有指向性資訊,初步確定是聚合請求配置某個鍵例外,結合Ocelot16.0.1的Key名更改,ReRoutes更改為Routes,聚合請求配置ReRouteKeys引數也要更改成RouteKeys,再次啟動,重啟成功,

NullReferenceException: Object reference not set to an instance of an object.(物件為空例外)
FileAggregateRoute(聚合請求)
System.AggregateException
HResult=0x80131500
Message=One or more errors occurred. (Object reference not set to an instance of an object.)
Source=System.Private.CoreLib
StackTrace:
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at MicroService.SCscHero.Gateway.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in [博客代碼]\MicroService.SCscHero.Gateway\Startup.cs:line 84
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.b__0(IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass13_0.b__2(IApplicationBuilder app)
at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisStartupFilter.<>c__DisplayClass0_0.b__0(IApplicationBuilder builder)
at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.b__0(IApplicationBuilder app)
at Microsoft.AspNetCore.Hosting.GenericWebHostService.d__31.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Extensions.Hosting.Internal.Host.d__9.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.d__4.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.d__4.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at MicroService.SCscHero.Gateway.Program.Main(String[] args) in [博客代碼]\MicroService.SCscHero.Gateway\Program.cs:line 13

此例外最初是在此呼叫堆疊中引發的:
Ocelot.Configuration.Validator.FileConfigurationFluentValidator.AllRoutesForAggregateExist.AnonymousMethod__0(Ocelot.Configuration.File.FileRoute) (位于 FileConfigurationFluentValidator.cs 中)
System.Linq.Enumerable.WhereListIterator.GetCount(bool)
System.Linq.Enumerable.Count(System.Collections.Generic.IEnumerable)
Ocelot.Configuration.Validator.FileConfigurationFluentValidator.AllRoutesForAggregateExist(Ocelot.Configuration.File.FileAggregateRoute, System.Collections.Generic.List<Ocelot.Configuration.File.FileRoute>) (位于 FileConfigurationFluentValidator.cs 中)
Ocelot.Configuration.Validator.FileConfigurationFluentValidator..ctor.AnonymousMethod__1_20(Ocelot.Configuration.File.FileConfiguration, Ocelot.Configuration.File.FileAggregateRoute) (位于 FileConfigurationFluentValidator.cs 中)
FluentValidation.DefaultValidatorExtensions.Must.AnonymousMethod__0(T, TProperty, FluentValidation.Validators.PropertyValidatorContext) (位于 DefaultValidatorExtensions.cs 中)
FluentValidation.DefaultValidatorExtensions.Must.AnonymousMethod__0(object, object, FluentValidation.Validators.PropertyValidatorContext) (位于 DefaultValidatorExtensions.cs 中)
FluentValidation.Validators.PredicateValidator.IsValid(FluentValidation.Validators.PropertyValidatorContext) (位于 PredicateValidator.cs 中)
FluentValidation.Validators.PropertyValidator.Validate(FluentValidation.Validators.PropertyValidatorContext) (位于 PropertyValidator.cs 中)
FluentValidation.Internal.CollectionPropertyRule.InvokePropertyValidator(FluentValidation.ValidationContext, FluentValidation.Validators.IPropertyValidator, string) (位于 CollectionPropertyRule.cs 中)
...
[呼叫堆疊已截斷]
內部例外 1:

六、更多的學習資料 完成度:100%

【官方檔案】https://ocelot.readthedocs.io/en/latest/index.html
【Github】待上傳Github,
原創博文,未經許可請勿轉載,
如有幫助,歡迎點贊、收藏、關注,如有問題,請評論留言!如需與博主聯系的,直接博客私信SCscHero即可,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/55.html

標籤:.NET Core

上一篇:NET CORE在Linux下部署并且用Nginx 做負載均衡(主要說明CentOS)

下一篇:.NET Core 下的爬蟲利器

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more