Ocelot簡易教程(四)之請求聚合以及服務發現

上篇文章給大家講解了Ocelot的一些特性並對路由進行了詳細的介紹,今天呢就大家一起來學習下Ocelot的請求聚合以及服務發現功能。希望能對大家有所幫助。

Ocelot簡易教程目錄

  1. Ocelot簡易教程(一)之Ocelot是什麼
  2. Ocelot簡易教程(二)之快速開始1
  3. Ocelot簡易教程(二)之快速開始2
  4. Ocelot簡易教程(三)之主要特性及路由詳解
  5. Ocelot簡易教程(四)之請求聚合以及服務發現
  6. Ocelot簡易教程(五)之集成IdentityServer認證以及授權
  7. Ocelot簡易教程(六)之重寫配置文件存儲方式並優化響應數據
  8. Ocelot簡易教程(七)之配置文件數據庫存儲插件源碼解析

請求聚合

Ocelot允許你聲明聚合路由,這樣你可以把多個正常的ReRoutes打包並映射到一個對象來對客戶端的請求進行響應。比如,你請求訂單信息,訂單中又包含商品信息,這裏就設計到兩個微服務,一個是商品服務,一個是訂單服務。如果不運用聚合路由的話,對於一個訂單信息,客戶端可能需要請求兩次服務端。實際上這會造成服務端額外的開銷。這時候有了聚合路由後,你只需要請求一次聚合路由,然後聚合路由會合並訂單跟商品的結果都一個對象中,並把這個對象響應給客戶端。使用Ocelot的此特性可以讓你很容易的實現前後端分離的架構。
爲了實現Ocelot的請求功能,你需要在ocelot.json中進行如下的配置。這裏我們指定了了兩個正常的ReRoutes,然後給每個ReRoute設置一個Key屬性。最後我們再Aggregates節點中的ReRouteKeys屬性中加入我們剛剛指定的兩個Key從而組成了兩個ReRoutes的聚合。當然我們還需要設置UpstreamPathTemplate匹配上游的用戶請求,它的工作方式與正常的ReRoute類似。

注意:不要把Aggregates中UpstreamPathTemplate設置的跟ReRoutes中的UpstreamPathTemplate設置成一樣。

下面我們先上個實例例子先!演示代碼已經同步更新Github上。有興趣的朋友可以查看源碼:https://github.com/yilezhu/OcelotDemo

在開始實例前先把我們的ocelot Nuget包升級到最新的12.0.0版本,當然你也可以不進行升級。這裏需要注意一下,如果你升級到12.0.0的版本的話,那麼你config.AddOcelot()的用法會發生改變,需要傳入參數config.AddOcelot(hostingContext.HostingEnvironment)

1.爲了演示的需要這裏我們新增一個類庫項目,分別新建兩個類,一個是商品Good類,一個是訂單Order類(這裏只是爲了演示的需要,所以代碼很簡陋)如下所示:

<span style="color:#333333"><code> <span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">Goods</span>
    {
        <span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> Id { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; }
        <span style="color:#0000ff">public</span> <span style="color:#0000ff">string</span> Content { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; }
    }

<span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">Orders</span>
    {
        <span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> Id { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; }
        <span style="color:#0000ff">public</span> <span style="color:#0000ff">string</span> Content { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; }
    }</code></span>
  1. 接下來我們給OrderApi以及GoodApi分別新建一個控制器,並返回相應的實體。如下所示:

    //GoodApi項目中
        [Route("api/[controller]")]
        [ApiController]
        public class GoodController : ControllerBase
        {
            // GET api/Good/5
            [HttpGet("{id}")]
            public ActionResult<string> Get(int id)
            {
                var item = new Goods
                {
                    Id = id,
                    Content = $"{id}的關聯的商品明細",
                };
                return JsonConvert.SerializeObject(item);
            }
        }
      //OrderApi項目中  
    [Route("api/[controller]")]
        [ApiController]
        public class OrderController : ControllerBase
        {
            // GET api/Order/5
            [HttpGet("{id}")]
            public ActionResult<string> Get(int id)
            {
                var item = new Orders {
                    Id=id,
                    Content=$"{id}的訂單明細",
                };
                return JsonConvert.SerializeObject(item);
            }
        }
  2. 接下來我們分別在ocelot.good.json以及ocelot.order.json中新增一個路由,並給出Keys.如下所示:

    這裏注意,跟上篇文章中的路由不同的是,這裏多了一個Key屬性。

    //ocelot.good.json
    {
          "DownstreamPathTemplate": "/api/Good/{id}",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "localhost",
              "Port": 1001
            }
          ],
          "UpstreamPathTemplate": "/good/{id}",
          "UpstreamHttpMethod": [ "Get", "Post" ],
          "Key": "Good",
          "Priority": 2
        }
    //ocelot.order.json
    {
          "DownstreamPathTemplate": "/api/Order/{id}",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "localhost",
              "Port": 1002
            }
          ],
          "UpstreamPathTemplate": "/order/{id}",
          "UpstreamHttpMethod": [ "Get", "Post" ],
          "Key": "Order",
          "Priority": 2
        }
  3. 在ocelot.all.json中加入聚合配置,如下所示:

      "Aggregates": [
        {
          "ReRouteKeys": [
            "Good",
            "Order"
          ],
          "UpstreamPathTemplate": "/GetOrderDetail/{id}"
        }
      ]

    注意:這裏AggregatesReRoutes同級,ReRouteKeys中填寫的數組就是上面步驟3中設置的Key屬性對應的值。

  4. 我們分別運行起來三個項目,然後訪問接口地址:http://localhost:1000/GetOrderDetail/1 會得到如下的聚合響應內容:

    1537715634581
    格式化後代碼如下:

    {
        "Good":{
            "Id":1,
            "Content":"1的關聯的商品明細"
        },
        "Order":{
            "Id":1,
            "Content":"1的訂單明細"
        }
    }
  5. 眼尖的朋友可能已經猜到了。聚合路由返回的內容就是json串。json串由ReRouteKeys組成,每個Key的內容就是具體下游響應的內容了!實例代碼已經同步更新到Github上,地址:https://github.com/yilezhu/OcelotDemo

    Ocelot將始終使用聚合請求返回內容類型application/json。還有需要注意的是聚合請求不會返回404請求。如果兩個下游都返回404狀態碼的話,這裏聚合後的響應也不會返回404,只會返回空的json串,拿上面的實例,如果兩個下游都返回404的話,那麼他的響應代碼類似下面這樣:

    {
       "Good": ,
       "Order":
    }

    如果下游服務返回404,則聚合將僅爲該下游服務返回任何內容。即使所有下游都返回404,它也不會將聚合響應更改爲404。

服務發現

Ocelot允許您指定服務發現提供程序,並將使用它來查找Ocelot將請求轉發到的下游服務的主機和端口。目前,這僅在GlobalConfiguration部分中受支持,這意味着相同的服務發現提供程序將用於爲ReRoute級別指定ServiceName的所有ReRoutes。

Consul

在使用Consul前你首先要做的就是安裝在Ocelot中提供Consul支持的NuGet包
Install-Package Ocelot.Provider.Consul
然後將下面的內容添加在ConfigureServices方法中

<span style="color:#333333"><code><span style="color:#0000ff">services</span><span style="color:#000000">.AddOcelot</span>()<span style="color:green">//注入Ocelot服務</span>
                    <span style="color:#000000">.AddConsul</span>(); </code></span>

GlobalConfiguration中需要加入以下內容。如果您未指定主機和端口,則將使用Consul默認值。

<span style="color:#333333"><code><span style="color:#a31515">"ServiceDiscoveryProvider"</span>: {
    <span style="color:#a31515">"Host"</span>: <span style="color:#a31515">"localhost"</span>,
    <span style="color:#a31515">"Port"</span>: <span style="color:#000000">8500</span>,
    <span style="color:#a31515">"Type"</span>: <span style="color:#a31515">"Consul"</span>
}</code></span>

注意:如果你採用AddOcelot()這種方式來自動加載ocelot配置文件的方式,那麼你需要新建一個ocelot.global.json文件,然後加入上面的配置:如下所示:

{
"GlobalConfiguration": {
  "ServiceDiscoveryProvider": {
    "Host": "localhost",
    "Port": 8500,
    "Type": "Consul"
  }
}
}
然後重新運行dotnet run命令會自動合併配置信息到Ocelot.json中,生成的對應內容如下:
​```C#
"ServiceDiscoveryProvider": {
      "Host": "localhost",
      "Port": 8500,
      "Type": "Consul",
      "Token": null,
      "ConfigurationKey": null,
      "PollingInterval": 0
    }

這個上篇文章中已經進行了相關的介紹。

爲了告訴Ocelot ReRoute是爲其主機和端口使用服務發現提供程序,您必須在下游請求時添加要使用的ServiceName和負載均衡器。目前,Ocelot可以使用RoundRobin和LeastConnection算法。如果未指定負載均衡器,則Ocelot將不會對請求進行負載均衡。

<span style="color:#333333"><code>{
    <span style="color:red">"DownstreamPathTemplate"</span>: <span style="color:#a31515">"/api/posts/{postId}"</span>,
    <span style="color:red">"DownstreamScheme"</span>: <span style="color:#a31515">"https"</span>,
    <span style="color:red">"UpstreamPathTemplate"</span>: <span style="color:#a31515">"/posts/{postId}"</span>,
    <span style="color:red">"UpstreamHttpMethod"</span>: [ <span style="color:#a31515">"Put"</span> ],
    <span style="color:red">"ServiceName"</span>: <span style="color:#a31515">"product"</span>,
    <span style="color:red">"LoadBalancerOptions"</span>: {
        <span style="color:red">"Type"</span>: <span style="color:#a31515">"LeastConnection"</span>
    },
}</code></span>

設置此項後,Ocelot將從服務發現提供程序中查找下游主機和端口,並跨任何可用服務進行負載平衡請求。

動態路由

作者的想法是在使用服務發現提供程序時啓用動態路由。在此模式下,Ocelot將使用上游路徑的第一個段來與服務發現提供程序一起查找下游服務。

例如,使用https://api.yilezhu.cn/product/products 等網址調用ocelot 。Ocelot將採用產品路徑的第一部分product,並將其用作在Consul中查找服務的Key。如果consul返回一個服務,Ocelot將使用從consul返回的主機和端口以及剩餘路徑段組合後的Url來進行請求的響應。,如:http:// hostfromconsul:portfromconsul/products。Ocelot將正常向下游URL轉發查詢字符串。即query

要啓用動態路由,您需要在配置中保留0個ReRoutes。目前您無法混合動態和配置ReRoutes。除此之外,您還需要指定上面概述的Service Discovery提供程序詳細信息和下游http / https方案作爲DownstreamScheme。

除此之外,您還可以設置RateLimitOptions,QoSOptions,LoadBalancerOptions和HttpHandlerOptions,DownstreamScheme(您可能希望在https上調用Ocelot,但可以通過http與私有服務進行通信),這些將應用於所有動態ReRoutes。

配置可能看起來像:

<span style="color:#333333"><code>{
    <span style="color:red">"ReRoutes"</span>: [],
    <span style="color:red">"Aggregates"</span>: [],
    <span style="color:red">"GlobalConfiguration"</span>: {
        <span style="color:red">"RequestIdKey"</span>: <span style="color:#a31515">null</span>,
        <span style="color:red">"ServiceDiscoveryProvider"</span>: {
            <span style="color:red">"Host"</span>: <span style="color:#a31515">"localhost"</span>,
            <span style="color:red">"Port"</span>: <span style="color:#000000">8500</span>,
            <span style="color:red">"Type"</span>: <span style="color:#a31515">"Consul"</span>,
            <span style="color:red">"Token"</span>: <span style="color:#a31515">null</span>,
            <span style="color:red">"ConfigurationKey"</span>: <span style="color:#a31515">null</span>
        },
        <span style="color:red">"RateLimitOptions"</span>: {
            <span style="color:red">"ClientIdHeader"</span>: <span style="color:#a31515">"ClientId"</span>,
            <span style="color:red">"QuotaExceededMessage"</span>: <span style="color:#a31515">null</span>,
            <span style="color:red">"RateLimitCounterPrefix"</span>: <span style="color:#a31515">"ocelot"</span>,
            <span style="color:red">"DisableRateLimitHeaders"</span>: <span style="color:#a31515">false</span>,
            <span style="color:red">"HttpStatusCode"</span>: <span style="color:#000000">429</span>
        },
        <span style="color:red">"QoSOptions"</span>: {
            <span style="color:red">"ExceptionsAllowedBeforeBreaking"</span>: <span style="color:#000000">0</span>,
            <span style="color:red">"DurationOfBreak"</span>: <span style="color:#000000">0</span>,
            <span style="color:red">"TimeoutValue"</span>: <span style="color:#000000">0</span>
        },
        <span style="color:red">"BaseUrl"</span>: <span style="color:#a31515">null</span>,
            <span style="color:red">"LoadBalancerOptions"</span>: {
            <span style="color:red">"Type"</span>: <span style="color:#a31515">"LeastConnection"</span>,
            <span style="color:red">"Key"</span>: <span style="color:#a31515">null</span>,
            <span style="color:red">"Expiry"</span>: <span style="color:#000000">0</span>
        },
        <span style="color:red">"DownstreamScheme"</span>: <span style="color:#a31515">"http"</span>,
        <span style="color:red">"HttpHandlerOptions"</span>: {
            <span style="color:red">"AllowAutoRedirect"</span>: <span style="color:#a31515">false</span>,
            <span style="color:red">"UseCookieContainer"</span>: <span style="color:#a31515">false</span>,
            <span style="color:red">"UseTracing"</span>: <span style="color:#a31515">false</span>
        }
    }
}</code></span>

Ocelot還允許您設置DynamicReRoutes,允許您爲每個下游服務設置速率限制規則。如果您有一個產品和搜索服務,並且您希望對另一個進行速率限制,則此功能非常有用。這方面的一個例子如下。

<span style="color:#333333"><code>{
    <span style="color:red">"DynamicReRoutes"</span>: [
        {
        <span style="color:red">"ServiceName"</span>: <span style="color:#a31515">"product"</span>,
        <span style="color:red">"RateLimitRule"</span>: {
                <span style="color:red">"ClientWhitelist"</span>: [],
                <span style="color:red">"EnableRateLimiting"</span>: <span style="color:#a31515">true</span>,
                <span style="color:red">"Period"</span>: <span style="color:#a31515">"1s"</span>,
                <span style="color:red">"PeriodTimespan"</span>: <span style="color:#000000">1000.0</span>,
                <span style="color:red">"Limit"</span>: <span style="color:#000000">3</span>
            }
        }
    ],
    <span style="color:red">"GlobalConfiguration"</span>: {
        <span style="color:red">"RequestIdKey"</span>: <span style="color:#a31515">null</span>,
        <span style="color:red">"ServiceDiscoveryProvider"</span>: {
            <span style="color:red">"Host"</span>: <span style="color:#a31515">"localhost"</span>,
            <span style="color:red">"Port"</span>: <span style="color:#000000">8523</span>,
            <span style="color:red">"Type"</span>: <span style="color:#a31515">"Consul"</span>
        },
        <span style="color:red">"RateLimitOptions"</span>: {
            <span style="color:red">"ClientIdHeader"</span>: <span style="color:#a31515">"ClientId"</span>,
            <span style="color:red">"QuotaExceededMessage"</span>: <span style="color:#a31515">""</span>,
            <span style="color:red">"RateLimitCounterPrefix"</span>: <span style="color:#a31515">""</span>,
            <span style="color:red">"DisableRateLimitHeaders"</span>: <span style="color:#a31515">false</span>,
            <span style="color:red">"HttpStatusCode"</span>: <span style="color:#000000">428</span>
        }
        <span style="color:#a31515">"DownstreamScheme"</span>: <span style="color:#a31515">"http"</span>,
    }
}</code></span>

此配置意味着如果您在/product/上進入Ocelot請求,則動態路由將啓動,並且ocelot將使用針對DynamicReRoutes部分中的產品服務的速率限制設置。

GitHub地址

https://github.com/yilezhu/OcelotDemo

總結

本文接着上篇文章進行了Ocelot請求聚合功能以及服務發現功能的介紹,並且對Ocelot動態路由功能也進行了簡單的闡述。對於請求聚合這塊進行了相關實例代碼的演示,並已經更新到Github上面了!希望能對大家有所幫助!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章