.Net微服務實踐(三)[網關]:Ocelot配置路由和請求聚合

在上篇.Net微服務實踐(二):Ocelot介紹和快速開始中咱們介紹了Ocelot,建立了一個Ocelot Hello World程序,接下來,咱們會介紹Oclot的主要特性路由和另一個特性請求聚合。這些特性都是經過配置來實現的。html

配置

{
    "ReRoutes": [],
    "GlobalConfiguration": {}
}

Ocelot的配置文件包含兩個節點: ReRoutes和GlobalConfiguration前端

  • ReRoutes - 告訴Ocelot如何處理上游的請求
  • GlobalConfiguration - 全局配置,此節點的配置容許覆蓋ReRoutes裏面的配置,你能夠在這裏進行通用的一些配置信息

Ocelot的完整配置項以下nginx

{
          "DownstreamPathTemplate": "/",
          "UpstreamPathTemplate": "/",
          "UpstreamHttpMethod": [
              "Get"
          ],
          "DownstreamHttpMethod": "",
          "DownstreamHttpVersion": "",
          "AddHeadersToRequest": {},
          "AddClaimsToRequest": {},
          "RouteClaimsRequirement": {},
          "AddQueriesToRequest": {},
          "RequestIdKey": "",
          "FileCacheOptions": {
              "TtlSeconds": 0,
              "Region": ""
          },
          "ReRouteIsCaseSensitive": false,
          "ServiceName": "",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
              {
                  "Host": "localhost",
                  "Port": 51876,
              }
          ],
          "QoSOptions": {
              "ExceptionsAllowedBeforeBreaking": 0,
              "DurationOfBreak": 0,
              "TimeoutValue": 0
          },
          "LoadBalancer": "",
          "RateLimitOptions": {
              "ClientWhitelist": [],
              "EnableRateLimiting": false,
              "Period": "",
              "PeriodTimespan": 0,
              "Limit": 0
          },
          "AuthenticationOptions": {
              "AuthenticationProviderKey": "",
              "AllowedScopes": []
          },
          "HttpHandlerOptions": {
              "AllowAutoRedirect": true,
              "UseCookieContainer": true,
              "UseTracing": true,
              "MaxConnectionsPerServer": 100
          },
          "DangerousAcceptAnyServerCertificateValidator": false
      }

完整配置項中的每一項具體含義和做用接下來會一一介紹,大的配置項的主要含義以下:git

  • Downstream - 下游服務配置
  • UpStream - 上游服務配置
  • Aggregates - 服務聚合配置
  • ServiceName, LoadBalancer, UseServiceDiscovery - 配置服務發現
  • AuthenticationOptions - 配置服務認證
  • RouteClaimsRequirement - 配置Claims鑑權
  • RateLimitOptions - 限流配置
  • FileCacheOptions - 緩存配置
  • QosOptions - 服務質量與熔斷
  • DownstreamHeaderTransform - 頭信息轉發

路由

基本配置

在上一篇的hello world程序中使用的就是基本配置github

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/orders",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/api/orders",
      "UpstreamHttpMethod": [ "Get" ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5000"
  }
}
  • BaseUrl - Ocelot的服務運行地址,要特別注意一下BaseUrl是咱們外部暴露的Url,好比咱們的Ocelot運行在http://localhost:5000,可是前面有一個 nginx綁定了域名http://api.demo.com,那這裏咱們的BaseUrl就是 http://api.demo.com
  • UpstreamPathTemplate、UpstreamHttpMethod - 配置上游服務器請求URL
  • DownstreamPathTemplate、DownstreamScheme、DownstreamHostAndPorts - 配置下游服務器請求URL

在基本配置的示例中:要實現的功能就是將 http://localhost:5000/api/orders GET 請求路由到 http://localhost:5001/api/orders GETjson

佔位符

在Ocelot中,能夠以{something}的形式將變量的佔位符添加到模板中。佔位符變量須要同時出如今DownstreamPathTemplate和UpstreamPathTemplate屬性中。請求時Ocelot將嘗試請求時進行替換api

{
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5002
        }
      ],
      "UpstreamPathTemplate": "/api/{everything}",
      "UpstreamHttpMethod": [ "Get" ]
}

示例說明:全部http://localhost:5000/api/XXXXXX的請求都會路由到http://localhost:5002/api/XXXXXX

例如http://localhost:5000/api/products 路由到 http://localhost:5002/api/products

例如http://localhost:5000/api/products/1 路由到 http://localhost:5002/api/products/1緩存

驗證服務器

修改配置,運行示例程序, 訪問http://localhost:5000/api/products,返回了產品數據數據結構

注意:在添加Ocelot.json文件時 .AddJsonFile("Ocelot.json",false,true), 第三個參數是指定文件發生變化時,是否從新加載,示例程序中是true. 因此咱們只要修改運行目錄下的配置文件,不用從新運行示例程序。

萬能模板

既然佔位符能夠作通用匹配,天然而然就有一種配置能夠匹配全部請求

{
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5002
        }
      ],
      "UpstreamPathTemplate": "/{url}",
      "UpstreamHttpMethod": [ "Get" ]
}

示例說明: 轉發全部的請求到http://localhost:5002

驗證

修改配置,運行示例程序, 訪問http://localhost:5000/api/products,返回了產品數據

優先級

若是一個上游請求有多個路由配置都能匹配,到底該使用哪一個路由呢? 路由能夠配置優先級(Priority), 0最小,路由會使用優先級高的(說明:若是多個匹配路由優先級同樣,則按順序使用第一個)

  • 在product-api中添加一個category api
[ApiController]
public class CategoryController : ControllerBase
{
    // GET: api/Product
    [Route("api/categories")]
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "電子產品", "醫護用品" };
    }
}
  • 修改Ocelot.json配置文件以下
{
  "DownstreamPathTemplate": "/api/products",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5002
    }
  ],
  "UpstreamPathTemplate": "/api/products",
  "UpstreamHttpMethod": [ "Get" ],
  "Priority": 0
},
{
  "DownstreamPathTemplate": "/api/categories",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5002
    }
  ],
  "UpstreamPathTemplate": "/api/{everything}",
  "UpstreamHttpMethod": [ "Get" ],
  "Priority": 1
}

若是這時訪問http://localhost:5000/api/products, 你們猜一下,是返回產品數據仍是類別數據?

驗證

修改配置,運行示例程序, 訪問http://localhost:5000/api/products,返回了類別數據, 由於類別路由的優先級是1, 優先級更高

查詢參數

  • 在order-api中添加一個訂單明細的api\
[Route("api/orders/{id}")]
[HttpGet]
public string Get(int id)
{
    string order = string.Empty;
    switch(id)
    {
        case 1:
            order = "劉明的訂單";
            break;
        case 2:
            order = "王天的訂單";
            break;
        default:
            order = "沒有找到訂單";
            break;
    }
    return order;

}
  • 修改Ocelot.json配置以下
{
  "DownstreamPathTemplate": "/api/orders/{id}",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ],
  "UpstreamPathTemplate": "/api/orders?id={id}",
  "UpstreamHttpMethod": [ "Get" ]
}

咱們指望的結果是,當訪問http://localhost:5000/api/orders?id=1 (下游服務實際沒這個接口)時 路由到http://localhost:5001/api/orders/1返回訂單明細

驗證

修改配置,運行示例程序, 訪問http://localhost:5000/api/orders?id=1,返回了訂單明細數據

請求聚合

有一種場景,前端一個頁面,調用了多個API,要同時開多個鏈接幾回調用才能所有所須要的數據,爲了減小沒必要要的請求和開銷,Ocelot也支持請求聚合

默認聚合

  • 修改配置文件,在ReRoutes 添加以下配置
{
  "DownstreamPathTemplate": "/api/orders",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ],
  "UpstreamPathTemplate": "/api/orders",
  "UpstreamHttpMethod": [ "Get" ],
  "Key": "Orders"
},
{
  "DownstreamPathTemplate": "/api/products",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5002
    }
  ],
  "UpstreamPathTemplate": "/api/products",
  "UpstreamHttpMethod": [ "Get" ],
  "Priority": 0,
  "Key": "Products"
}

你們注意一下,這和以前的配置有什麼區別? 區別就是再每個路由配置下多了一個 Key, Key的值能夠任意定義(但建議仍是按業務含義定義)

  • 在Ocelot.json中添加以下配置
"Aggregates": [
    {
      "ReRouteKeys": [
        "Orders",
        "Products"
      ],
      "UpstreamPathTemplate": "/api/aggregates"
    }
  ]

注意Aggregates配置是和在ReRoutes配置平級的

{
    "ReRoutes": [],
    "Aggregates": [],
    "GlobalConfiguration": {}
}

示例說明: 當訪問http://localhost:5000/api/aggregates, 會同時返回訂單數據和產品數據

運行示例進行驗證

既然是多個請求聚合,那麼問題來了:

  • 若是其中一個服務宕機,會怎麼樣?

    咱們中止訂單服務,再次當訪問http://localhost:5000/api/aggregates, 結果返回500
  • 若是其中一個服務不是宕機,而是返回500,會怎麼樣?

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

再次運行示例,訪問http://localhost:5000/api/aggregates,Response是200, 可是body中Products節點是正常的產品數據,Orders節點裏面的數據是異常信息

自定義聚合

若是默認的聚合返回的結果數據結構不是咱們想要的,想要修改怎麼辦?答案是使用自定義聚合

  • 在ocelot-gateway中, 添加一個自動以聚合器FakeDefinedAggregator, 必須實現IDefinedAggregator接口。這個聚合器的功能很簡單,就是將兩個聚合請求的結果,用逗號拼接起來返回
public class FakeDefinedAggregator : IDefinedAggregator
{
    public FakeDefinedAggregator(FakeDepdendency dep)
    {
    }

    public async Task<DownstreamResponse> Aggregate(List<DownstreamContext> responses)
    {
        var one = await responses[0].DownstreamResponse.Content.ReadAsStringAsync();
        var two = await responses[1].DownstreamResponse.Content.ReadAsStringAsync();

        var merge = $"{one}, {two}";
        var headers = responses.SelectMany(x => x.DownstreamResponse.Headers).ToList();
        return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers, "some reason");
    }
}
  • 注入自定義聚合器
services.AddOcelot()
        .AddSingletonDefinedAggregator<FakeDefinedAggregator>();
  • 在Ocelot.json中修改配置,指定自定義聚合器
"Aggregates": [
    {
      "ReRouteKeys": [
        "Orders",
        "Products"
      ],
      "UpstreamPathTemplate": "/api/aggregates",
      "Aggregator": "FakeDefinedAggregator"
    }
  ],

與以前的配置相比,多了以下的配置,就是指定自定義聚合器的

"Aggregator": "FakeDefinedAggregator"

驗證

修改配置,運行示例程序, 訪問http://localhost:5000/api/aggregate, 驗證返回結果

最後

本篇咱們介紹了Ocelot配置,只要特性路由,以及請求聚合。接下里咱們會介紹Ocelot的其餘特性:限流熔斷、負載均衡 .Net微服務實踐(四)[網關]:Ocelot限流熔斷、緩存以及負載均衡

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

相關文章
相關標籤/搜索