Ocelot-基於.NET Core的開源網關實現

原文: Ocelot-基於.NET Core的開源網關實現

寫在前面

   API網關是系統內部服務暴露在外部的一個訪問入口,相似於代理服務器,就像一個公司的門衛承擔着尋址、限制進入、安全檢查、位置引導等工做,咱們能夠形象的用下圖來表示: 外部設備須要訪問內部系統服務時必需要經過咱們的API Gateway,目的是爲了隔離內部服務和外部訪問來作統一的認證受權,限流熔斷,請求聚合,負載均衡,日誌記錄,監控預警等 通用功能,就像是咱們系統的防火牆同樣,在任何外部請求訪問系統時都必須通過防火牆的驗證。javascript

更多關於網關的信息請參考前面的一篇文章《API網關模式》html

Ocelot是什麼?

     Ocelot是基於.NET Core實現的輕量級API網關,它包括的主要功能有:路由、請求聚合、服務發現、認證、受權、限流熔斷、並內置了LoadBanalce以及集成了Service Fabric、 Consul、Eureka等功能,這些功能只都只須要簡單的配置便可使用。目前騰訊財付通的API Gateway就是基於此作的實現(參考善友兄的這篇文章),下面是詳細信息以及Ocelot如何在微軟官方示例代碼庫 eShopContainers中的使用。java

Ocelot在騰訊的使用:https://customers.microsoft.com/en-us/story/tencent-telecommunications-dotnetcore
微軟官方示例:https://github.com/dotnet-architecture/eShopOnContainersgit

Ocelot的實現機制

    簡單的來講它是一堆的asp.net core middleware組成的pipeline,當它拿到請求以後會用一個request builder來構造一個HttpRequestMessage發到下游的真實服務器,等下游的服務 返回response以後再由一個middleware將它返回的HttpResponseMessage映射到HttpResponse上。github

代碼示例

我在Github上建立了示例代碼庫僅供參考,咱們可使用下面的步驟來建立示例代碼:(示例代碼算法

1.建立BookingApi: dotnet new -n BookingApi
2.建立PassengerApi: dotnet new -n PassengerApi
3.建立ApiGateway: dotnet new -n ApiGateway
4.添加BookingApi和PassengerApi的實現代碼
5.在ApiGateway項目中用Nuget安裝Ocelot依賴包
6.添加configuration.json的配置文件
7.配置路由響應規則
8.啓動服務並經過Api網關訪問服務json

啓動BookingApi PassengerApi這兩個服務,咱們能夠看到他們分別提供了兩個接口api

此時再啓動咱們的Api Gateway項目,經過Gateway來訪問咱們這兩個API緩存

咱們能夠看到本來咱們能夠直接訪問的兩個API如今均可以經過Gateway來訪問了,那這一切是怎麼作到的呢?安全

當咱們經過Nuget安裝Ocelot的依賴以後,咱們須要在項目中添加.json的配置文件,在此項目中咱們配置文件命名爲configuration.json,內容以下:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/booking",
      "UpstreamPathTemplate": "/api/getbooking",
      "UpstreamHttpMethod": [ "Get" ],
      "ReRouteIsCaseSensitive": false,
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 8001
        }
      ],
      "Key": "booking",
      "RateLimitOptions": {
        "ClientWhitelist": [],
        "EnableRateLimiting": true,
        "Period": "1s",
        "PeriodTimespan": 15,
        "Limit": 1
      }
    },
    {
      "DownstreamPathTemplate": "/api/booking/{pnr}",
      "UpstreamPathTemplate": "/api/getbooking/{pnr}",
      "UpstreamHttpMethod": [ "Get" ],
      "ReRouteIsCaseSensitive": false,
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 8001
        }
      ]
    },
    {
      "DownstreamPathTemplate": "/api/passenger",
      "UpstreamPathTemplate": "/api/getpassenger",
      "UpstreamHttpMethod": [ "Get" ],
      "ReRouteIsCaseSensitive": false,
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 8002
        }
      ],
      "Key": "passenger"
    },
    {
      "DownstreamPathTemplate": "/api/passenger/{id}",
      "UpstreamPathTemplate": "/api/getpassenger/{id}",
      "UpstreamHttpMethod": [ "Get" ],
      "ReRouteIsCaseSensitive": false,
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 8002
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "https://localhost:5000"
  },
  "Aggregates": [
    {
      "ReRouteKeys": [
        "booking",
        "passenger"
      ],
      "UpstreamPathTemplate": "/api/getbookingpassengerinfo"
    }
  ]
}

   API Gateway幫助咱們作的事情是當有請求訪問網關的時候,咱們通過認證受權等一系列操做確保這次訪問是被容許的以後便會轉發到它實際須要請求服務上去,請求結束以後再由Gateway統一將結果返回給客戶端,從模板中咱們能夠看到UpStreamPathTemplate實際上就是咱們上游請求的地址,即網關公開給外部調用的地址(此服務名稱和地址咱們能夠根據須要隨便設置更多的是爲了對外界隱藏咱們真實的服務地址),而實際的調用地址即是DownStreamPathTemplate中給定的實際地址。爲了方便你們理解此配置的含義,我查閱了官方資料,將咱們上面用到的配置文件作了註解:(更齊全的參數請參考官方文檔

{
  "ReRoutes": [ //路由是API網關最基本也是最核心的功能、ReRoutes下就是由多個路由節點組成。
    {
      "DownstreamPathTemplate": "", //下游服務模板
      "UpstreamPathTemplate": "", //上游服務模板
      "UpstreamHttpMethod": [ "Get" ],//上游方法類型Get,Post,Put
      "AddHeadersToRequest": {},//須要在轉發過程當中添加到Header的內容
      "FileCacheOptions": { //能夠對下游請求結果進行緩存,主要依賴於CacheManager實現
        "TtlSeconds": 10,
        "Region": ""
      },
      "ReRouteIsCaseSensitive": false,//重寫路由是否區分大小寫
      "ServiceName": "",//服務名稱
      "DownstreamScheme": "http",//下游服務schema:http, https
      "DownstreamHostAndPorts": [ //下游服務端口號和地址
        {
          "Host": "localhost",
          "Port": 8001
        }
      ],
      "RateLimitOptions": { //限流設置
        "ClientWhitelist": [], //客戶端白名單
        "EnableRateLimiting": true,//是否啓用限流設置
        "Period": "1s", //每次請求時間間隔
        "PeriodTimespan": 15,//恢復的時間間隔
        "Limit": 1 //請求數量
      },
      "QoSOptions": { //服務質量與熔斷,熔斷的意思是中止將請求轉發到下游服務。當下遊服務已經出現故障的時候再請求也是無功而返,
      而且增長下游服務器和API網關的負擔,這個功能是用的Polly來實現的,咱們只須要爲路由作一些簡單配置便可
        "ExceptionsAllowedBeforeBreaking": 0, //容許多少個異常請求
        "DurationOfBreak": 0, //熔斷的時間,單位爲秒
        "TimeoutValue": 0 //若是下游請求的處理時間超過多少則自如將請求設置爲超時
      }
    }
  ],
  "UseServiceDiscovery": false,//是否啓用服務發現
  "Aggregates": [ //請求聚合
    {
      "ReRouteKeys": [ //設置須要聚合的路由key
        "booking",
        "passenger"
      ],
      "UpstreamPathTemplate": "/api/getbookingpassengerinfo" //暴露給外部的聚合請求路徑
    },
  "GlobalConfiguration": { //全局配置節點
    "BaseUrl": "https://localhost:5000" //網關基地址
  }
}

  示例代碼中咱們在Aggregates節點下配置了路由聚合,它將兩個請求的結果combine到一塊兒再返回給客戶端,當咱們請求/api/getbookingpassengerinfo 時就會返回下面結果:

須要注意的是:

  1. 聚合服務目前只支持返回json
  2. 目前只支持Get方式請求下游服務
  3. 任何下游的response header並會被丟棄
  4. 若是下游服務返回404,聚合服務只是這個key的value爲空,它不會返回404

有木有以爲這裏的聚合很相似於GraphQL的功能,但實際上在Ocelot中並不打算實現GraphQL的功能,由於畢竟Ocelot的主要職責是實現網關的功能,聚合只是其中的一個feature,GraphQL提供了一個庫 graphql-dotnet ,咱們能夠用它來完成須要的功能,而在Ocelot中實現相似認證,受權等這樣它擅長的事情: 

{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/graphql",
            "DownstreamScheme": "http",
            "DownstreamHostAndPorts": [
              {
                "Host": "yourgraphqlhost.com",
                "Port": 80
              }
            ],
            "UpstreamPathTemplate": "/graphql",
            "DelegatingHandlers": [
                "GraphQlDelegatingHandler"
            ]
        }
    ]
  }

官方也給出了示例:https://github.com/ThreeMammals/Ocelot/tree/develop/samples/OcelotGraphQL

此框架包含的內容比較多,在此並不一一解釋,下面將談談其餘的幾個功能:

請求限流

你們注意到咱們在上面例子中經過RateLimitOptions節點配置了限流的相關設置,目前咱們配置的是1s鍾以內只容許對booking api訪問一次,不然的話便中止繼續轉發至下游服務,咱們經過測試就會發現當在1s內屢次訪問的時候,網關便會返回下面的信息:

負載均衡

當咱們路由到的下游服務有多個結點的時候,咱們能夠在DownstreamHostAndPorts中進行配置負載

{
    "DownstreamPathTemplate": "/api/posts/{postId}",
    "DownstreamScheme": "https",
    "DownstreamHostAndPorts": [
            {
                "Host": "10.127.1.10",
                "Port": 5001,
            },
            {
                "Host": "10.127.1.11",
                "Port": 5002,
            }
        ],
    "UpstreamPathTemplate": "/posts/{postId}",
    "LoadBalancer": "LeastConnection",
    "UpstreamHttpMethod": [ "Put", "Delete" ]
}

LoadBalancer將決定負載均衡的算法,目前支持下面三種方式

  1. LeastConnection – 將請求發往最空閒的那個服務器
  2. RoundRobin – 輪流發送
  3. NoLoadBalance – 老是發往第一個請求或者是服務發現

Prioirty優先級

當咱們配置多個請求產生衝突的時候,經過路由設置訪問優化級  

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

 萬能模板

若是不但願對請求作任何的處理,則可使用下面的萬能模板:(萬能模板的優先級最低,只要有其它的路由模板,其它的路由模板則會優先生效)

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

  本篇內容就先介紹到這裏,後面將會繼續探究Ocelot的內部實現。我的感受如今.NET Core的生態愈來愈好,愈來愈多的開發人員開始嘗試.NET Core並建立了不少優秀的開源項目,從微軟這幾年的開源策略咱們更多的感覺到了微軟對於擁抱開源的決心,這也更加的讓咱們有信心在Core平臺上去構建愈來愈多的優秀項目。若是你對技術有熱情能夠掃碼加入咱們的微信羣一塊兒探討。

參考資料:

http://ocelot.readthedocs.io/en/latest/index.html
http://www.javashuo.com/article/p-wtfmtrfv-es.html

Polly:
https://github.com/App-vNext/Polly

Consul:
https://github.com/hashicorp/consul

https://github.com/dotnet/home

Microsoft Blog:
https://blogs.msdn.microsoft.com/cesardelatorre/2018/05/15/designing-and-implementing-api-gateways-with-ocelot-in-a-microservices-and-container-based-architecture/

相關文章
相關標籤/搜索