天天看點

Ocelot簡易教程(三)之主要特性及路由詳解

作者:依樂祝

原文位址:

https://www.cnblogs.com/yilezhu/p/9664977.html

上篇《Ocelot簡易教程(二)之快速開始2》教大家如何快速跑起來一個ocelot執行個體項目,也隻是簡單的對Ocelot進行了配置,這篇文章會給大家詳細的介紹一下Ocelot的配置資訊。希望能對大家深入使用Ocelot有所幫助。

上篇中也提到了,最簡單的Ocelot如下面所示,隻有簡單的兩個節點,一個是

ReRoutes

,另一個就是

GlobalConfiguration

關于這兩個節點的作用,上篇也已經講述了,這裡再簡單的講下ReRoutes:告訴Ocelot如何處理上遊的請求。GlobalConfiguration:顧名思義就是全局配置,此節點的配置允許覆寫ReRoutes裡面的配置,你可以在這裡進行通用的一些配置資訊。

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

下面呢給出ReRoute 的所有的配置資訊,當然在實際使用的時候你沒有必要全部進行配置,隻需要根據你項目的實際需要進行相關的配置就可以了。

"ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",//下遊路由模闆
      "UpstreamPathTemplate": "/good/{everything}",//上遊路由模闆
      "UpstreamHttpMethod": [ "Get", "Post" ],//上遊請求方法
      "AddHeadersToRequest": {},
      "UpstreamHeaderTransform": {},
      "DownstreamHeaderTransform": {},
      "AddClaimsToRequest": {},
      "RouteClaimsRequirement": {},
      "AddQueriesToRequest": {},
      "RequestIdKey": null,
      "FileCacheOptions": {
        "TtlSeconds": 0,
        "Region": null
      },
      "ReRouteIsCaseSensitive": false,
      "ServiceName": null,
      "DownstreamScheme": "http",
      "QoSOptions": {//Qos相關配置
        "ExceptionsAllowedBeforeBreaking": 0,
        "DurationOfBreak": 0,
        "TimeoutValue": 0
      },
      "LoadBalancerOptions": {//負載均衡相關選項
        "Type": "RoundRobin",
        "Key": null,
        "Expiry": 0
      },
      "RateLimitOptions": {//限流相關配置
        "ClientWhitelist": [],
        "EnableRateLimiting": false,
        "Period": null,
        "PeriodTimespan": 0.0,
        "Limit": 0
      },
      "AuthenticationOptions": {//認證相關選項
        "AuthenticationProviderKey": null,
        "AllowedScopes": []
      },
      "HttpHandlerOptions": {//HttpHandler相關的配置
        "AllowAutoRedirect": false,//是否對下遊重定向進行響應
        "UseCookieContainer": false,//是否啟動CookieContainer儲存cookies
        "UseTracing": false,
        "UseProxy": true
      },
      "DownstreamHostAndPorts": [//下遊端口及host
        {
          "Host": "localhost",
          "Port": 1001
        },
        {
          "Host": "localhost",
          "Port": 1002
        }
      ],
      "UpstreamHost": null,//上遊Host
      "Key": null,
      "DelegatingHandlers": [],
      "Priority": 1,
      "Timeout": 0,
      "DangerousAcceptAnyServerCertificateValidator": false
    }           

當然上面的配置項我就不一一的進行介紹,因為很多配置相信大家根據意思都能知道個大概了。我隻會對比較常用的配置做下介紹。而且在接下來的文章中對對每個節點進行單獨的詳細的介紹。在介紹之前呢先看Ocelot的幾個特性。

Ocelot特性介紹

合并配置檔案

這個特性允許使用者建立多個配置檔案來友善的對大型項目進行配置。試想一下,如果你的項目有幾十個路由規則需要配置的話,那麼在一個配置檔案進行配置應該很痛苦吧,有了這個特性後,你就可以建立多個配置檔案。Ocelot會自動合并他們。

在加載配置檔案的時候 你可以通過下面的方式來調用AddOcelot()方法來替換直接加載某個配置的寫法 如:AddJsonFile(“ocelot.json”)

.ConfigureAppConfiguration((hostingContext, config) =>
    {
        config
            .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
            .AddJsonFile("appsettings.json", true, true)
            .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
            .AddOcelot()
            .AddEnvironmentVariables();
    })           

在這種情況下,Ocelot會尋找所有比對了 

(?i)ocelot.([a-zA-Z0-9]*).json

的檔案,然後合并他們。如何你要設定GlobalConfiguration 屬性,那麼你需要建立一個ocelot.global.json 的檔案來進行全局的配置。

這裡上一個例子吧!可以友善大家的了解。

建立一個ocelot.good.json檔案,并加入下面的配置:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 1001
        },
        {
          "Host": "localhost",
          "Port": 1002
        }
      ],
      "UpstreamPathTemplate": "/good/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    }
  ]
}
           

然後再建立一個ocelot.order.json檔案,并加入下面的配置:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 1001
        },
        {
          "Host": "localhost",
          "Port": 1002
        }
      ],
      "UpstreamPathTemplate": "/order/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    }
  ]
}
           

最後建立一個ocelot.all.json檔案,并把上篇文章中的路由拷貝到裡面:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 1001
        },
        {
          "Host": "localhost",
          "Port": 1002
        }
      ],
      "UpstreamPathTemplate": "/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    }
  ],
  "GlobalConfiguration": {

  }
}

           

然後修改下,Program.cs檔案中的代碼如下:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    config
                        .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
                        .AddJsonFile("appsettings.json", true, true)
                        .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
                        .AddOcelot()
                        .AddEnvironmentVariables();
                })
                .UseUrls("http://localhost:1000")
                .UseStartup<Startup>();           

這裡最重要的代碼就是

config.AddOcelot()

了。這段代碼就會按照上面的規則查找所有符合條件的檔案并合并路由。合并後的代碼如下:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "UpstreamPathTemplate": "/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "AddHeadersToRequest": {},
      "UpstreamHeaderTransform": {},
      "DownstreamHeaderTransform": {},
      "AddClaimsToRequest": {},
      "RouteClaimsRequirement": {},
      "AddQueriesToRequest": {},
      "RequestIdKey": null,
      "FileCacheOptions": {
        "TtlSeconds": 0,
        "Region": null
      },
      "ReRouteIsCaseSensitive": false,
      "ServiceName": null,
      "DownstreamScheme": "http",
      "QoSOptions": {
        "ExceptionsAllowedBeforeBreaking": 0,
        "DurationOfBreak": 0,
        "TimeoutValue": 0
      },
      "LoadBalancerOptions": {
        "Type": "RoundRobin",
        "Key": null,
        "Expiry": 0
      },
      "RateLimitOptions": {
        "ClientWhitelist": [],
        "EnableRateLimiting": false,
        "Period": null,
        "PeriodTimespan": 0.0,
        "Limit": 0
      },
      "AuthenticationOptions": {
        "AuthenticationProviderKey": null,
        "AllowedScopes": []
      },
      "HttpHandlerOptions": {
        "AllowAutoRedirect": false,
        "UseCookieContainer": false,
        "UseTracing": false,
        "UseProxy": true
      },
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 1001
        },
        {
          "Host": "localhost",
          "Port": 1002
        }
      ],
      "UpstreamHost": null,
      "Key": null,
      "DelegatingHandlers": [],
      "Priority": 1,
      "Timeout": 0,
      "DangerousAcceptAnyServerCertificateValidator": false
    },
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "UpstreamPathTemplate": "/good/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "AddHeadersToRequest": {},
      "UpstreamHeaderTransform": {},
      "DownstreamHeaderTransform": {},
      "AddClaimsToRequest": {},
      "RouteClaimsRequirement": {},
      "AddQueriesToRequest": {},
      "RequestIdKey": null,
      "FileCacheOptions": {
        "TtlSeconds": 0,
        "Region": null
      },
      "ReRouteIsCaseSensitive": false,
      "ServiceName": null,
      "DownstreamScheme": "http",
      "QoSOptions": {
        "ExceptionsAllowedBeforeBreaking": 0,
        "DurationOfBreak": 0,
        "TimeoutValue": 0
      },
      "LoadBalancerOptions": {
        "Type": "RoundRobin",
        "Key": null,
        "Expiry": 0
      },
      "RateLimitOptions": {
        "ClientWhitelist": [],
        "EnableRateLimiting": false,
        "Period": null,
        "PeriodTimespan": 0.0,
        "Limit": 0
      },
      "AuthenticationOptions": {
        "AuthenticationProviderKey": null,
        "AllowedScopes": []
      },
      "HttpHandlerOptions": {
        "AllowAutoRedirect": false,
        "UseCookieContainer": false,
        "UseTracing": false,
        "UseProxy": true
      },
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 1001
        },
        {
          "Host": "localhost",
          "Port": 1002
        }
      ],
      "UpstreamHost": null,
      "Key": null,
      "DelegatingHandlers": [],
      "Priority": 1,
      "Timeout": 0,
      "DangerousAcceptAnyServerCertificateValidator": false
    },
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "UpstreamPathTemplate": "/order/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "AddHeadersToRequest": {},
      "UpstreamHeaderTransform": {},
      "DownstreamHeaderTransform": {},
      "AddClaimsToRequest": {},
      "RouteClaimsRequirement": {},
      "AddQueriesToRequest": {},
      "RequestIdKey": null,
      "FileCacheOptions": {
        "TtlSeconds": 0,
        "Region": null
      },
      "ReRouteIsCaseSensitive": false,
      "ServiceName": null,
      "DownstreamScheme": "http",
      "QoSOptions": {
        "ExceptionsAllowedBeforeBreaking": 0,
        "DurationOfBreak": 0,
        "TimeoutValue": 0
      },
      "LoadBalancerOptions": {
        "Type": "RoundRobin",
        "Key": null,
        "Expiry": 0
      },
      "RateLimitOptions": {
        "ClientWhitelist": [],
        "EnableRateLimiting": false,
        "Period": null,
        "PeriodTimespan": 0.0,
        "Limit": 0
      },
      "AuthenticationOptions": {
        "AuthenticationProviderKey": null,
        "AllowedScopes": []
      },
      "HttpHandlerOptions": {
        "AllowAutoRedirect": false,
        "UseCookieContainer": false,
        "UseTracing": false,
        "UseProxy": true
      },
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 1001
        },
        {
          "Host": "localhost",
          "Port": 1002
        }
      ],
      "UpstreamHost": null,
      "Key": null,
      "DelegatingHandlers": [],
      "Priority": 1,
      "Timeout": 0,
      "DangerousAcceptAnyServerCertificateValidator": false
    }
  ],
  "DynamicReRoutes": [],
  "Aggregates": [],
  "GlobalConfiguration": {
    "RequestIdKey": null,
    "ServiceDiscoveryProvider": {
      "Host": null,
      "Port": 0,
      "Type": null,
      "Token": null,
      "ConfigurationKey": null,
      "PollingInterval": 0
    },
    "RateLimitOptions": {
      "ClientIdHeader": "ClientId",
      "QuotaExceededMessage": null,
      "RateLimitCounterPrefix": "ocelot",
      "DisableRateLimitHeaders": false,
      "HttpStatusCode": 429
    },
    "QoSOptions": {
      "ExceptionsAllowedBeforeBreaking": 0,
      "DurationOfBreak": 0,
      "TimeoutValue": 0
    },
    "BaseUrl": null,
    "LoadBalancerOptions": {
      "Type": null,
      "Key": null,
      "Expiry": 0
    },
    "DownstreamScheme": null,
    "HttpHandlerOptions": {
      "AllowAutoRedirect": false,
      "UseCookieContainer": false,
      "UseTracing": false,
      "UseProxy": true
    }
  }
}           

Ocelot的合并方式是先對滿足格式的檔案周遊查找,然後循環加載他們,并提取所有的ReRoutes以及AggregateReRoutes 的資料。如果發現ocelot.global.json ,則添加到GlobalConfiguration 中。然後Ocelto會将合并後的配置儲存在ocelot.json的檔案中,當Ocelot運作時會加載這個合并後的ocelot.json檔案,進而加載了所有的配置。

注意:這裡需要注意的是Ocelot在合并的過程中不會對内容進行驗證,隻有在最終合并的配置進行校驗,是以如果發現問題的話,那麼你需要檢查最終生成的ocelot.json 是否出錯了!

在consul中存儲配置

這裡你首先要做的就是安裝Ocelot中提供的Consul的NuGet包,Nuget安裝方式:

Install-Package Ocelot.Provider.Consul

然後在注冊服務時添加如下内容:Ocelot将會嘗試在Consul KV存儲并加載配置。

services
   .AddOcelot()
   .AddConsul()
   .AddConfigStoredInConsul();           

當然你還得把下面的配置添加到你的ocelot.json檔案中。這裡定義Ocelot如何查找Consul根并從Consul中加載并存儲配置.

"GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
        "Host": "localhost",
        "Port": 9500
    }
}           

變化時重新加載配置檔案

Ocelot支援在配置檔案發生改變的時候重新加載json配置檔案。在加載ocelot.json檔案的時候按照下面進行配置,那麼當你手動更新ocelot.json檔案時,Ocelot将重新加載ocelot.json配置檔案。

config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);

配置Key

如果你使用Consul進行配置,你可能需要配置Key以便區分多個配置,為了指定Key,你需要在json配置檔案中的ServiceDiscoveryProvider部分設定ConfigurationKey屬性:

"GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
        "Host": "localhost",
        "Port": 9500,
        "ConfigurationKey": "Oceolot_A"
    }
}           

在此執行個體中,Ocelot将會在Consul查找時使用Oceolot_A 作為配置的Key.如果沒有設定ConfigurationKey 則Ocelot将使用字元串InternalConfiguration 作為此配置的Key

跟蹤重定向和使用CookieContainer

在ReRoute配置中可以使用HttpHandlerOptions來設定HttpHandler行為:

  1. AllowAutoRedirect是一個值,訓示請求是否應遵循重定向響應。如果請求應自動遵循來自下遊資源的重定向響應,則将其設定為true; 否則是假的。預設值為false。
  2. UseCookieContainer是一個值,訓示處理程式是否使用CookieContainer屬性存儲伺服器cookie并在發送請求時使用這些cookie。預設值為false。請注意,如果您使CookieContainer,則Ocelot會為每個下遊服務緩存HttpClient。這意味着對該DownstreamService的所有請求将共享相同的cookie。

SSL 錯誤處理

如果你想忽略SSL 警告/錯誤,你可以在你的ReRoute 配置中加上如下配置:

"DangerousAcceptAnyServerCertificateValidator": false           

當然作者是不建議這樣做的,最好的方式是建立你本地以及遠端所信任的證書。

Ocelot路由詳解

路由

Ocelot的最主要的功能是接收傳入的http請求并将其轉發到下遊服務。

Ocelot使用ReRoute節點描述将一個請求路由到另一個請求。為了讓路由在Ocelot中起作用,您需要在配置中設定ReRoute:

{
    "ReRoutes": [
    ]
}           

要配置ReRoute,您需要在ReRoutes json數組中至少添加一個:

{
    "DownstreamPathTemplate": "/api/good/{goodId}",//下遊路由模闆
    "DownstreamScheme": "http",//下遊路由請求的方式
    "DownstreamHostAndPorts": [//下遊路由的Host以及端口
            {
                "Host": "localhost",
                "Port": 1001,
            }
        ],
    "UpstreamPathTemplate": "/good/{goodId}",//上遊路由請求的模闆
    "UpstreamHttpMethod": [ "Put", "Delete" ]//上遊路由請求的方式
}           

DownstreamPathTemplate,DownstreamScheme和DownstreamHostAndPorts定義請求将轉發到的URL。

DownstreamHostAndPorts是一個集合,用于定義您希望将請求轉發到的任何下遊服務的主機和端口。通常這隻包含一個條目,但有時你希望對下遊請求服務進行負載均衡,這個時候你就可以添加多個條目,并配合負載均衡選項進行相關的負載均衡設定。

UpstreamPathTemplate是Ocelot用于辨別要用于給定請求的DownstreamPathTemplate對應的URL。使用UpstreamHttpMethod以便Ocelot可以區分具有不同HTTP謂詞的請求到相同的URL。您可以設定特定的HTTP方法清單,也可以設定一個空清單以允許所有的。

在Ocelot中,您可以以{something}的形式将變量的占位符添加到模闆中。占位符變量需要同時出現在DownstreamPathTemplate和UpstreamPathTemplate屬性中。請求時Ocelot将嘗試請求時進行替換。

你也可以像下面這樣配置,捕獲所有的路由:

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

這個配置将會把路徑+查詢字元串統統轉發到下遊路由.

注意:預設的ReRouting的配置是不區分大小寫的,如果需要修改此配置,可以通過下面進行配置:
"ReRouteIsCaseSensitive": true           
這意味着Ocelot将嘗試将傳入的上遊URL與上遊模闆比對時,區分大小寫。

全部捕獲

Ocelot的路由還支援捕獲所有樣式路由,使用者可以指定他們想要比對所有請求。

如果您設定如下所示的配置,則所有請求都将直接代理。占位符{url}名稱不重要,任何名稱都可以使用。

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

上面配置的全部捕獲的優先級低于任何其他法人ReRoute。如果您的配置中還有下面的ReRoute,那麼Ocelot會在全部捕獲之前比對它。

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

上遊主機

此功能允許您根據上遊主機獲得ReRoutes。這通過檢視用戶端使用的主機頭,然後将其用作我們用于識别ReRoute的資訊的一部分來工作。

要使用此功能,請在配置中添加以下内容。

{
    "DownstreamPathTemplate": "/",
    "DownstreamScheme": "http",
    "DownstreamHostAndPorts": [
            {
                "Host": "localhost",
                "Port": 1001,
            }
        ],
    "UpstreamPathTemplate": "/",
    "UpstreamHttpMethod": [ "Get" ],
    "UpstreamHost": "yilezhu.cn"
}           

僅當主機标頭值為yilezhu.cn時,才會比對上面的ReRoute。

如果您沒有在ReRoute上設定UpstreamHost,那麼任何主機頭都将與之比對。這意味着如果你有兩個相同的ReRoutes,除了UpstreamHost,其中一個為null而另一個不為null 那麼Ocelot将支援已設定的那個。

優先級

你可以通過ocelot.json檔案的ReRoutes節點中的Priorty屬性來設定比對上遊HttpRequest的優先級順序

比如,下面兩個路由:

{
    "UpstreamPathTemplate": "/goods/{catchAll}"
    "Priority": 0
}           

以及

{
    "UpstreamPathTemplate": "/goods/delete"
    "Priority": 1
}           

上面兩個路由中,如果向Ocelot發出的請求時

/goods/delete

格式的話,則Ocelot會優先比對

/goods /delete

的路由。

動态路由

作者的想法是在使用服務發現提供程式時啟用動态路由,這樣您就不必提供ReRoute的配置。我們會在服務發現那一章進行詳細的介紹。

查詢字元串

Ocelot允許您指定一個查詢字元串作為DownstreamPathTemplate的一部分,如下例所示。

{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
            "UpstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
            "UpstreamHttpMethod": [
                "Get"
            ],
            "DownstreamScheme": "http",
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 50110
                }
            ]
        }
    ],
    "GlobalConfiguration": {
    }
}           

在此示例中,Ocelot将使用上遊路徑模闆中{unitId}的值,并将其作為名為unitId的查詢字元串參數添加到下遊請求中!

Ocelot還允許您将查詢字元串參數放在UpstreamPathTemplate中,以便您可以将某些查詢與某些服務比對。

{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates",
            "UpstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}",
            "UpstreamHttpMethod": [
                "Get"
            ],
            "DownstreamScheme": "http",
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 50110
                }
            ]
        }
    ],
    "GlobalConfiguration": {
    }
}           

在此示例中,Ocelot将僅比對具有比對的url路徑的請求,并且查詢字元串以unitId = something開頭。您可以在此之後進行其他查詢,但必須以比對參數開頭。此外,Ocelot将交換查詢字元串中的{unitId}參數,并在下遊請求路徑中使用它。

源碼位址

當然是放上執行個體中的源碼位址了:

https://github.com/yilezhu/OcelotDemo

Ocelot簡易教程目錄

  1. Ocelot簡易教程(一)之Ocelot是什麼
  2. Ocelot簡易教程(二)之快速開始1
  3. Ocelot簡易教程(二)之快速開始2
  4. Ocelot簡易教程(三)之主要特性及路由詳解

總結

本文主要是對Ocelot的新特性以及路由進行詳細的介紹,這些介紹對你使用ocelot會有很大的幫助。下篇文章呢,我會對請求聚合以及服務發現以及動态路由進行記錄,敬請期待!同時需要說明一點是,本文大部分内容是翻譯自官方文檔,當然中間穿插着自己在使用過程中一些了解,希望大家能夠喜歡!