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

posted @ 2024-04-15 14:46  野码  阅读(342)  评论(0编辑  收藏  举报