05_NET中使用Ocelot網關(負載均衡、限流、認證)

Ocelot是一個用.NET Core實現並且開源的API網關,它功能強大,包括了:路由、請求聚合、服務發現、認證、鑑權、限流熔斷、並內置了負載均衡器與Service Fabric、Butterfly Tracing集成。而且這些功能都只需要簡單的配置即可完成。

官網:GitHub - ThreeMammals/Ocelot: .NET API Gateway

Welcome to Ocelot 23.2 — Ocelot 23.2 documentation

安裝包:Ocelot,命令:Install-Package Ocelot

簡單使用:

1.首先兩個WebApi服務,Service1和Service2用於測試

Service1:

[ApiController]
[Route("[controller]/[action]")]
public class UserController:ControllerBase
{
    [HttpGet]
    public List<UserInfo> GetUsers([FromQuery] UserBo bo)
    {
        List<UserInfo> result = new()
        {
            new(){Id = 1,NickName = "張三"},
            new(){Id = 2,NickName = "李四"}
        };
        return result;
    }

    [HttpGet]
    public string GetStr()
    {
        return "hello world,gateway,server1";
    }
}

Service2:

[ApiController]
[Route("[controller]/[action]")]
public class UserController:ControllerBase
{
    [HttpGet]
    public List<UserInfo> GetUsers([FromHeader]  long userId)
    {
        List<UserInfo> result = new()
        {
            new(){Id = 3,NickName = "王五"},
            new(){Id = 4,NickName = "趙六"}
        };
        return result;
    }
    
    [HttpGet]
    public string GetStr()
    {
        return "hello world,gateway,server2";
    }
}

 

2.添加一個空的Web項目Ocelot,添加Ocelot安裝包。

2.1.添加ocelot.json的配置文件。

Program.cs修改

var builder = WebApplication.CreateBuilder(args);

// 添加配置文件,optional:文件是否可選,reloadOnChange:如果文件發生更改,是否應重載配置。
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
// 添加Ocelot 服務
builder.Services.AddOcelot(builder.Configuration);var app = builder.Build();
// 會阻斷當前項目自己的 /controller/action 路由
app.UseOcelot().Wait();


app.Run();

 

2.2.ocelot.json配置 ( 不推薦這麼使用 )

{
  //這裏注意,以前是ReRoutes現在是Routes
  "Routes": [
    {
      //Upstream表示上游請求,即客戶端請求到API Gateway的請求
      "UpstreamPathTemplate": "/s1/{url}", //請求路徑模板
      "UpstreamHttpMethod": [ "Get", "Post" ], //請求方法數組

      //"UseServiceDiscovery": true, //啓用服務發現

      //Downstream表示下游請求,即API Gateway轉發的目標服務地址
      "DownstreamPathTemplate": "/{url}", //下游請求地址模板
      "DownstreamScheme": "https", //請求協議,目前應該是支持http和https

      //A***************指定單個轉發地址
      "DownstreamHostAndPorts": [ //請求服務地址,可以有多個
        {
          "Host": "localhost",
          "Port": 5146
        }
      ]
    },
    {
      "UpstreamPathTemplate": "/s2/{url}",  
      "UpstreamHttpMethod": [ "Get", "Post" ], 
       
      "DownstreamPathTemplate": "/{url}",  
      "DownstreamScheme": "https",  

      "DownstreamHostAndPorts": [  
        {
          "Host": "localhost",
          "Port": 5211
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5063" //網關地址
  }
}

啓動服務:啓動兩個服務和網關

測試:http://localhost:5063/s1/user/getusers  和  http://localhost:5063/s2/user/getusers

 

--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------

模板介紹:

{
  "DownstreamPathTemplate": "/",
  "UpstreamPathTemplate": "/",
  "UpstreamHttpMethod": [
    "Get",
    "Post"
  ],
  "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
  },
  "UseServiceDiscovery": false
}

 

Downstream是下游服務配置
UpStream是上游服務配置
Aggregates 服務聚合配置
ServiceName, LoadBalancer, UseServiceDiscovery 配置服務發現
AuthenticationOptions 配置服務認證
RouteClaimsRequirement 配置Claims鑑權
RateLimitOptions爲限流配置
FileCacheOptions 緩存配置
QosOptions 服務質量與熔斷
DownstreamHeaderTransform頭信息轉發
DownstreamPathTemplate:下游模板
DownstreamScheme:下游服務http schema
DownstreamHostAndPorts:下游服務的地址,如果使用LoadBalancer的話這裏可以填多項
UpstreamPathTemplate: 上游也就是用戶輸入的請求Url模板
UpstreamHttpMethod: 上游請求http方法,可使用數組

 

--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------

負載均衡

當下遊服務有多個結點的時候,我們可以在DownstreamHostAndPorts中進行配置。

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/s/{url}",
      "UpstreamHttpMethod": [ "Get","Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      },
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5146
        },
        {
          "Host": "localhost",
          "Port": 5211
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5063"
  }
}

LoadBalancerOptions:
LeastConnection – 將請求發往最空閒的那個服務器
RoundRobin – 輪流發送
NoLoadBalance – 總是發往第一個請求或者是服務發現

測試:http://localhost:5063/s/user/getusers

--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------

限流

對請求進行限流可以防止下游服務器因爲訪問過載而崩潰。非常優雅的實現,我們只需要在路由下加一

些簡單的配置即可以完成。

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/s/{url}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      },

      // 限流配置
      "RateLimitOptions": {
        "ClientWhitelist": [],      //白名單,不會被限流,爲空表示訪問的都被限流
        "EnableRateLimiting": true, //是否被開啓
        "Period": "1s",             //1秒鐘超過了Limit定義的會拋異常
        "PeriodTimespan": 1,        //超過一秒後纔可以重新請求
        "Limit": 1
      },
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5146
        },
        {
          "Host": "localhost",
          "Port": 5211
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5063",
    "RateLimitOptions": {
      "DisableRateLimitHeaders": false,
      "QuotaExceededMessage": "你被限流了..........",
      "HttpStatusCode": 999,  //發生異常返回轉態碼
      "ClientIdHeader": "ClientId"
    }
  }
}

DisableRateLimitHeaders Http頭 X-Rate-Limit 和 Retry-After 是否禁用
QuotaExceedMessage 當請求過載被截斷時返回的消息
HttpStatusCode 當請求過載被截斷時返回的http status , 默認爲429(too many requests)
ClientIdHeader 用來識別客戶端的請求頭,默認是 ClientId

 

--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------

認證

添加一個WebApi項目(AuthenticationCente),用於獲取Token,不知道怎麼用JWT 的可以去查資料或 02_Web Api使用Jwt - 野碼 - 博客園 (cnblogs.com)

 

網關項目(Ocelot)中的Program.cs調整

var builder = WebApplication.CreateBuilder(args);

// 添加配置文件
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);


#region 認證
//身份認證--如何鑑權 
builder.Services.AddAuthentication(options =>
{
    /options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    //取出私鑰
    var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["JWT:SecretKey"]);
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        //驗證發佈者
        ValidateIssuer = true,
        ValidIssuer = builder.Configuration["JWT:Issuer"],
        //驗證接收者
        ValidateAudience = true,
        ValidAudience = builder.Configuration["JWT:Audience"],
        //ValidateIssuerSigningKey= true,//是否驗證SigningKey
        //驗證是否過期
        ValidateLifetime = true,
        //驗證私鑰
        IssuerSigningKey = new SymmetricSecurityKey(secretByte)
    };
});
#endregion

// 添加Ocelot 服務
builder.Services.AddOcelot(builder.Configuration);

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

// 會阻斷當前項目自己的 /controller/action 路由
app.UseOcelot().Wait();


app.Run();

修改ocelot.json配置

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/s/{url}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      },

      // 限流配置
      "RateLimitOptions": {
        "ClientWhitelist": [], //白名單,不會被限流,爲空表示訪問的都被限流
        "EnableRateLimiting": true, //是否被開啓
        "Period": "1s", //1秒鐘超過了Limit定義的會拋異常
        "PeriodTimespan": 1, //超過一秒後纔可以重新請求
        "Limit": 1
      },
      "AuthenticationOptions": { //認證
        "AuthenticationProviderKey": "Bearer",
        "AllowedScopes": []
      },
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5146
        },
        {
          "Host": "localhost",
          "Port": 5211
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5063",
    "RateLimitOptions": {
      "DisableRateLimitHeaders": false,
      "QuotaExceededMessage": "你被限流了..........",
      "HttpStatusCode": 999,  //發生異常返回轉態碼
      "ClientIdHeader": "ClientId"
    }
  }
}

然後service1和Service2方法上添加[Authorize] 

測試:http://localhost:5063/s/user/getusers 請求頭帶上Token

 

--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------

子服務獲取參數

外部服務調用子服務,走網關。

參數對象

public class UserInfo
{
    [FromHeader]
    public int UserId{ get; set; }

    public string NickName { get; set; }
}

 

子服務要接收userId

    [HttpGet]
    public List<UserInfo> GetUsers([FromHeader] long userInfo)
    {
        List<UserInfo> result = new()
        {
            new(){UserId = 1,NickName = "張三"},
            new(){UserId = 2,NickName = "李四"}
        };
        return result;
    }

 

上游請求中添加報頭,請在ocelot.json中的路由 GlobalConfiguration 配置中中添加以下內容:

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/s/{url}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      },

      // 限流配置
      "RateLimitOptions": {
        "ClientWhitelist": [], //白名單,不會被限流,爲空表示訪問的都被限流
        "EnableRateLimiting": true, //是否被開啓
        "Period": "1s", //1秒鐘超過了Limit定義的會拋異常
        "PeriodTimespan": 1, //超過一秒後纔可以重新請求
        "Limit": 1
      },
      "AuthenticationOptions": { //認證
        "AuthenticationProviderKey": "Bearer",
        "AllowedScopes": []
      },
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5146
        },
        {
          "Host": "localhost",
          "Port": 5211
        }
      ]
    }
  ],
  //全局
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5063",
    "RateLimitOptions": {
      "DisableRateLimitHeaders": false,
      "QuotaExceededMessage": "你被限流了..........",
      "HttpStatusCode": 999, //發生異常返回轉態碼
      "ClientIdHeader": "ClientId"
    },
    "UpstreamHeaderTransform": {
      "UserId": "{UserId}"
    }
  }
}

測試:http://localhost:5063/s/user/getusers

Params:NickName--->1234

Headers:UserId--->2233

Auth:你的Token

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