天天看點

Ocelot-基于.NET Core的開源網關實作

寫在前面

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

Ocelot-基于.NET Core的開源網關實作

更多關于網關的資訊請參考前面的一篇文章《API網關模式》

Ocelot是什麼?

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

Ocelot在騰訊的使用:https://customers.microsoft.com/en-us/story/tencent-telecommunications-dotnetcore

微軟官方示例:https://github.com/dotnet-architecture/eShopOnContainers

Ocelot-基于.NET Core的開源網關實作

Ocelot的實作機制

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

代碼示例

我在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網關通路服務

啟動BookingApi PassengerApi這兩個服務,我們可以看到他們分别提供了兩個接口

Ocelot-基于.NET Core的開源網關實作
Ocelot-基于.NET Core的開源網關實作

此時再啟動我們的Api Gateway項目,通過Gateway來通路我們這兩個API

Ocelot-基于.NET Core的開源網關實作

我們可以看到原本我們可以直接通路的兩個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 時就會傳回下面結果:

Ocelot-基于.NET Core的開源網關實作

需要注意的是:

  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内多次通路的時候,網關便會傳回下面的資訊:

Ocelot-基于.NET Core的開源網關實作

負載均衡

當我們路由到的下遊服務有多個結點的時候,我們可以在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

https://www.cnblogs.com/shanyou/p/7787183.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/

如果您覺得本文對你有用,不妨幫忙點個贊,或者在評論裡給我一句贊美。我們正在使用.NET Core / Azure / Xamarin等技術開發基于網際網路和移動應用平台上的各種新産品和商業服務。我們的目标是通過舉辦各種分享活動,交流開發心得和經驗來推動.NET技術棧在西安乃至西北地區的發展。我們是一個開發和自由的社群,歡迎您加入西安.NET社群,微信賬号:dotNetXA

歡迎您持續關注我的部落格

文章出處:.NET西安社群

版權所有,歡迎保留原文連結進行轉載:)