.Net微服務實踐(四)[網關]:Ocelot限流熔斷、緩存以及負載均衡

在上篇.Net微服務實踐(三)[網關]:Ocelot配置路由和請求聚合中咱們介紹了Ocelot的配置,主要特性路由以及服務聚合。接下來,咱們會介紹Ocelot的限流、熔斷、緩存以及負載均衡。html

限流

咱們先來看限流的配置git

Reroute節點中的配置以下:github

{
      "DownstreamPathTemplate": "/api/orders",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/api/orders",
      "UpstreamHttpMethod": [ "Get" ],
      "RateLimitOptions": {
        "ClientWhitelist": [],
        "EnableRateLimiting": true,
        "Period": "10m",
        "PeriodTimespan": 3,
        "Limit": 1
      }
    }

GlobalConfiguration中的配置以下:算法

"GlobalConfiguration": {
    "BaseUrl": "http://localhost:5000",
    //限流
    "RateLimitOptions": {
      "QuotaExceededMessage": "您的請求量超過了配額1/10分鐘",
      "HttpStatusCode": 999
    }
}

配置說明json

在Reroute和GlobalConfiguration節點中添加了RateLimitOptions節點api

  • ClientWhitelist - 白名單,也就是不受限流控制的客戶端
  • EnableRateLimiting - 是否開啓限流
  • Period & Limit - 在一段時間內容許的請求次數
  • PeriodTimespan - 客戶端的重試間隔數,也就是客戶端間隔多長時間能夠重試
  • QuotaExceededMessage - 限流之後的提示信息
  • HttpStatusCode - 超出配額時,返回的http狀態碼

示例說明
客戶端在10分鐘以內只容許請求一次http://localhost:5000/api/orders,在請求以後3秒鐘以後能夠重試緩存

驗證服務器

修改配置,運行示例程序,app

訪問http://localhost:5000/api/orders,第一次能夠正常獲取返回結果,再次訪時,顯示"您的請求量超過了配額1/10分鐘, 而且response狀態碼是999負載均衡

PeriodTimespan的驗證

修改Period爲1s, 修改PeriodTimespan爲10,這樣當前的配置是1秒中容許一個請求,10秒後才能重試。 再次運行示例程序。

訪問http://localhost:5000/api/orders,第一次能夠正常獲取返回結果, 等待兩秒,再次訪問,你們想一下,這個時候,會不會返回正常結果(已通過了兩秒)這時仍是返回999,爲何? 由於儘管配額上是容許的,可是由於配置是客戶端10秒之後才能重試,而這時只等待了2秒,因此仍是返回999.

熔斷

Ocelot的熔斷使用了Polly來實現,在OcelotGateway項目添加Polly包

注入Polly

services
        .AddOcelot()
        .AddPolly();

修改配置

{
     "DownstreamPathTemplate": "/api/orders",
     "DownstreamScheme": "http",
     "DownstreamHostAndPorts": [
       {
         "Host": "localhost",
         "Port": 5001
       }
     ],
     "UpstreamPathTemplate": "/api/orders",
     "UpstreamHttpMethod": [ "Get" ],
     "QoSOptions": {
       "ExceptionsAllowedBeforeBreaking": 2,
       "DurationOfBreak": 5000,
       "TimeoutValue": 2000
     }
   }

配置說明

在Reroute節點中添加了QoSOptions節點

  • ExceptionsAllowedBeforeBreaking - 在熔斷以前容許的異常次數
  • DurationOfBreak - 熔斷時長 ,單位毫秒
  • TimeoutValue - 請求超時設置, 單位毫秒

示例說明

當訪問http://localhost:5000/api/orders出現2次異常後,服務熔斷5秒,若是服務響應超過2秒,也觸發熔斷條件

驗證

  • 場景一:服務宕機

    修改配置,只啓動網關,不啓動oder api,訪問http://localhost:5000/api/orders,第一次有響應耗時,返回500,第二次也有響應耗時,返回500. 第三次則快速返回503 Service Unavalible, 服務熔斷了。

  • 場景二:超時

    修改配置

    修改api/orders代碼,等待3秒

// GET: api/orders
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    Task.Delay(3000).Wait();
    return new string[] { "劉明的訂單", "王天的訂單" };
}

啓動網關,啓動order-api,訪問http://localhost:5000/api/orders,返回503

  • 場景三:服務正常響應,可是服務500 內部錯誤

    修改配置

    修改api/orders代碼,拋出異常
// GET: api/orders
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    throw new Exception("獲取全部訂單出錯");
}

啓動網關,啓動order-api,訪問http://localhost:5000/api/orders, 不觸發熔斷

緩存

緩存使用了CacheManageer來實現,添加CacheManager包

Install-Package Ocelot.Cache.CacheManager

注入緩存組件

services.AddOcelot()
        .AddCacheManager(x =>
        {
            x.WithDictionaryHandle();
        });

Ocelot.json配置文件修改

{
  "DownstreamPathTemplate": "/api/orders",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ],
  "UpstreamPathTemplate": "/api/orders",
  "UpstreamHttpMethod": [ "Get" ],
  "FileCacheOptions": {
    "TtlSeconds": 60,
    "Region": "orders"
  }
}

緩存是根據 downstream service 的URL來緩存的
配置說明

在Reroute節點中添加了FileCacheOptions節點

  • TtlSeconds - 緩存有效期,單位是秒
  • Region - 緩存分區, 能夠經過調用後臺Api 來清空一個region下的緩存

示例說明

當訪問http://localhost:5000/api/orders後,結果會緩存60秒,在緩存有效期內即便原始的order api的返回結果發生變化,經過網關請求時,仍是會返回緩存的結果。

驗證

  • 修改配置,只啓動網關,啓動oder api,訪問http://localhost:5000/api/orders,返回的結果以下
"劉明的訂單", "王天的訂單"
  • 修改api/orders, 從新啓動api/orders
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    return new string[] { "帥的訂單", "個人訂單" };
}
  • 再次訪問http://localhost:5000/api/orders, 這個時候是會返回什麼結果呢?, 驗證顯示仍是返回
"劉明的訂單", "王天的訂單"

由於結果被緩存了

  • 等待2分鐘,再訪問http://localhost:5000/api/orders,這個時候是會返回什麼結果呢?, 驗證顯示返回了新的結果
"帥的訂單", "個人訂單"

由於緩存有效期已通過了

Header轉化

Ocelot容許在上游服務的request和下游服務的response的header中添加、替換信息

配置以下:

{
  "DownstreamPathTemplate": "/api/shopping-carts",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ],
  "DownstreamHeaderTransform": {
    "devops": "rdc"
  },
  "UpstreamPathTemplate": "/api/shopping-carts",
  "UpstreamHttpMethod": [ "Get" ],
  "UpstreamHeaderTransform": {
    "lakin": "rdc",
    "CI": "msbuild, jenkins",
    "Location": "http://localhost:5001, {BaseUrl}"
  }

}

配置說明

在Reroute節點中添加了DownstreamHeaderTransform節點和UpstreamHeaderTransform節點

"DownstreamHeaderTransform": {
    "devops": "rdc"
  }

說明:在下游服務的response中添加一個header, key是devops, value是rdc

"UpstreamHeaderTransform": {
    "lakin": "rdc",
    "CI": "msbuild, jenkins",
    "Location": "http://localhost:5001, {BaseUrl}"
  }
  • 添加header信息

    在上游服務的request中添加一個header, key是lakin, value是rdc
  • 替換header信息

    在上游服務的request中, 將key是CI的header, 其值由msbuild替換爲jenkins
  • 替換時使用placeholder

    在上游服務的request中, 將key是Location的header, 其值由http://localhost:5001替換爲{BaseUrl} placehokder

示例說明

當訪問http://localhost:5000/api/orders後,結果會緩存60秒,在緩存有效期內即便原始的order api的返回結果發生變化,經過網關請求時,仍是會返回緩存的結果。

驗證

  • 修改Order Service, 添加一個ShoppingCart api, 代碼以下
// GET: api/shopping-carts
[Route("api/shopping-carts")]
[HttpGet]
public IEnumerable<string> Get()
{
    Console.WriteLine($"開始打印header信息");
    foreach (var item in this.Request.Headers)
    {
        Console.WriteLine($"{item.Key} - {item.Value}");
    }
    Console.WriteLine($"打印header信息完成");
    return new string[] { "洗髮水", "無人機" };
}
  • 啓動網關、啓動Order Service
  • 使用Postman調用http://localhost:5000/api/shopping-carts, 在request中添加兩個header項
"CI": "msbuild",
    "Location": "http://localhost:5001"
  • 發起請求
  • 在Order Service的控制檯,能夠看到以下輸出, 添加一個header項,替換兩個header項的值
開始打印header信息CI
lakin - rdc
CI - jenkins
Location - http://localhost:5000
打印header信息完成
  • 檢查Postman中response的header信息,會發現添加了以下的header項
devops - rdc

HTTP方法轉換

Ocelot容許在路由時轉化HTTP方法

{
  "DownstreamPathTemplate": "/api/shopping-carts",
  "DownstreamScheme": "http",
  "DownstreamHttpMethod": "POST",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ],
  "UpstreamPathTemplate": "/api/shopping-carts",
  "UpstreamHttpMethod": [ "Get" ]
}

示例說明
上述示例中,將GET /api/shopping-carts 路由到 POST /api/shopping-carts, 將GET轉換成了POST

適用場景:例若有些已經存在的的API,由於某些歷史緣由都是用POST,在經過網關對外提供服務時,就能夠按照標準API進行轉換

驗證

  • 當前GET /api/shopping-carts返回以下結果
"洗髮水", "無人機"
  • 咱們在ShoppingCartController中添加一個新的POST API
[Route("api/shopping-carts")]
[HttpPost]
public string Post()
{
    return "添加商品到購物車成功";
}
添加商品到購物車成功

負載均衡

Ocelot內置了負載均衡,咱們先來看配置

{
     "DownstreamPathTemplate": "/api/orders",
     "DownstreamScheme": "http",
     "DownstreamHostAndPorts": [
       {
         "Host": "localhost",
         "Port": 5001
       },
       {
         "Host": "localhost",
         "Port": 6001
       }
     ],
     "UpstreamPathTemplate": "/api/orders",
     "UpstreamHttpMethod": [ "Get" ],
     "LoadBalancerOptions": {
       "Type": "RoundRobin"
     }
}

配置說明

在DownstreamHostAndPorts指指定多個服務地址

在Reroute節點中添加LoadBalancerOptions,這是負載均衡的配置節點,其中Type屬性指定了負載均衡的算法, 它有以下幾個值:

  • LeastConnection – 將請求發往最空閒的那個服務器
  • RoundRobin – 輪流發送
  • NoLoadBalance – 老是發往第一個請求(若是配置了服務發現,則老是發往發現的第一個服務地址)

驗證

  • 啓動網關
  • 修改api/orders的代碼,並啓動Order Service
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    return new string[] { "劉明的訂單", "王天的訂單" };
}
  • 拷貝一份Order Service的副本,修改api/orders的代碼,修改副本的啓動端口爲6001, 並啓動Order Service
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    return new string[] { "帥的訂單", "個人訂單" };
}

第一次的結果是

"劉明的訂單", "王天的訂單"

第二次的結果是

"帥的訂單", "個人訂單"

第三次的結果是

"劉明的訂單", "王天的訂單"

注入/重寫中間件

Ocelot自己是一組中間件,它也提供了方式來注入和重寫其中的某些中間件:

  • PreErrorResponderMiddleware - 在全部Ocelot中間件以前運行, 容許用戶在Ocelot管道運行以前和運行以後提供任何行爲。
  • PreAuthenticationMiddleware - 提供預身份認證邏輯,在身份認證中間件以前運行
  • AuthenticationMiddleware - 覆寫Ocelot中間件提供的身份認證邏輯
  • PreAuthorisationMiddleware - 提供預受權邏輯,在受權中間件以前運行
  • AuthorisationMiddleware - 覆寫Ocelot中間件提供的受權邏輯
  • PreQueryStringBuilderMiddleware - 在QueryString轉換以前,提供預處理邏輯

下面是注入PreErrorResponderMiddleware中間件的代碼示例:

//注入中間件
var configuration = new OcelotPipelineConfiguration
{
    PreErrorResponderMiddleware = async (ctx, next) =>
    {
        ctx.HttpContext.Request.Headers.Add("myreq", "ocelot-request");
        await next.Invoke();
    }
};
app.UseOcelot(configuration).Wait();

注意: Ocelot也是一組中間件,因此能夠在Ocelot中間件以前,按常規方式添加任何中間件, 可是不能在Ocelot中間件以後添加,由於Ocelot沒有調用 next

後臺管理

Ocelot提供了一組後臺管理的API, 從前三篇文章能夠看出,Ocelot主要也就是配置文件的管理,因此API主要也就是管理配置

  • 獲取管理後臺的token

    POST {adminPath}/connect/token
  • 獲取配置

    GET {adminPath}/configuration
  • 建立/修改配置

    POST {adminPath}/configuration
  • 刪除緩存

    DELETE {adminPath}/outputcache/{region}

最後

本篇咱們介紹了Ocelot的限流、熔斷、緩存、負載均衡以及其餘一些特性。到目前爲止,Ocelot的基本配置和功能都已經介紹完了。接下里咱們會結合consul來介紹服務發現,以及Ocelot和Consul的集成

示例代碼下載地址: https://github.com/lcyhjx/ocelot-demo/tree/master

相關文章
相關標籤/搜索