.Net微服务实践(四)[网关]:Ocelot限流熔断、缓存以及负载均衡

时间:2020-04-12 20:08:55   收藏:0   阅读:96

在上篇.Net微服务实践(三)[网关]:Ocelot配置路由和请求聚合中我们介绍了Ocelot的配置,主要特性路由以及服务聚合。接下来,我们会介绍Ocelot的限流、熔断、缓存以及负载均衡。

限流

我们先来看限流的配置

Reroute节点中的配置如下:

{
      "DownstreamPathTemplate": "/api/orders",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/api/orders",
      "UpstreamHttpMethod": [ "Get" ],
      "RateLimitOptions": {
        "ClientWhitelist": [],
        "EnableRateLimiting": true,
        "Period": "10m",
        "PeriodTimespan": 3,
        "Limit": 1
      }
    }

GlobalConfiguration中的配置如下:

"GlobalConfiguration": {
    "BaseUrl": "http://localhost:5000",
    //限流
    "RateLimitOptions": {
      "QuotaExceededMessage": "您的请求量超过了配额1/10分钟",
      "HttpStatusCode": 999
    }
}

配置说明

在Reroute和GlobalConfiguration节点中添加了RateLimitOptions节点

示例说明
客户端在10分钟之内只允许请求一次http://localhost:5000/api/orders,在请求之后3秒钟之后可以重试

验证

修改配置,运行示例程序,

访问http://localhost:5000/api/orders,第一次可以正常获取返回结果,再次访时,显示"您的请求量超过了配额1/10分钟, 并且response状态码是999

PeriodTimespan的验证

修改Period为1s, 修改PeriodTimespan为10,这样当前的配置是1秒中允许一个请求,10秒后才能重试。 再次运行示例程序。

访问http://localhost:5000/api/orders,第一次可以正常获取返回结果, 等待两秒,再次访问,大家想一下,这个时候,会不会返回正常结果(已经过了两秒)这时还是返回999,为什么? 因为尽管配额上是允许的,但是因为配置是客户端10秒以后才能重试,而这时只等待了2秒,所以还是返回999.

熔断

Ocelot的熔断使用了Polly来实现,在OcelotGateway项目添加Polly包

注入Polly

services
        .AddOcelot()
        .AddPolly();

修改配置

{
     "DownstreamPathTemplate": "/api/orders",
     "DownstreamScheme": "http",
     "DownstreamHostAndPorts": [
       {
         "Host": "localhost",
         "Port": 5001
       }
     ],
     "UpstreamPathTemplate": "/api/orders",
     "UpstreamHttpMethod": [ "Get" ],
     "QoSOptions": {
       "ExceptionsAllowedBeforeBreaking": 2,
       "DurationOfBreak": 5000,
       "TimeoutValue": 2000
     }
   }

配置说明

在Reroute节点中添加了QoSOptions节点

示例说明

当访问http://localhost:5000/api/orders出现2次异常后,服务熔断5秒,如果服务响应超过2秒,也触发熔断条件

验证

// GET: api/orders
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    Task.Delay(3000).Wait();
    return new string[] { "刘明的订单", "王天的订单" };
}

启动网关,启动order-api,访问http://localhost:5000/api/orders,返回503

// GET: api/orders
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    throw new Exception("获取所有订单出错");
}

启动网关,启动order-api,访问http://localhost:5000/api/orders, 不触发熔断

缓存

缓存使用了CacheManageer来实现,添加CacheManager包

Install-Package Ocelot.Cache.CacheManager

注入缓存组件

services.AddOcelot()
        .AddCacheManager(x =>
        {
            x.WithDictionaryHandle();
        });

Ocelot.json配置文件修改

{
  "DownstreamPathTemplate": "/api/orders",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ],
  "UpstreamPathTemplate": "/api/orders",
  "UpstreamHttpMethod": [ "Get" ],
  "FileCacheOptions": {
    "TtlSeconds": 60,
    "Region": "orders"
  }
}

缓存是根据 downstream service 的URL来缓存的
配置说明

在Reroute节点中添加了FileCacheOptions节点

示例说明

当访问http://localhost:5000/api/orders后,结果会缓存60秒,在缓存有效期内即使原始的order api的返回结果发生变化,通过网关请求时,还是会返回缓存的结果。

验证

"刘明的订单", "王天的订单"
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    return new string[] { "帅的订单", "我的订单" };
}
"刘明的订单", "王天的订单"

因为结果被缓存了

"帅的订单", "我的订单"

因为缓存有效期已经过了

Header转化

Ocelot允许在上游服务的request和下游服务的response的header中添加、替换信息

配置如下:

{
  "DownstreamPathTemplate": "/api/shopping-carts",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ],
  "DownstreamHeaderTransform": {
    "devops": "rdc"
  },
  "UpstreamPathTemplate": "/api/shopping-carts",
  "UpstreamHttpMethod": [ "Get" ],
  "UpstreamHeaderTransform": {
    "lakin": "rdc",
    "CI": "msbuild, jenkins",
    "Location": "http://localhost:5001, {BaseUrl}"
  }

}

配置说明

在Reroute节点中添加了DownstreamHeaderTransform节点和UpstreamHeaderTransform节点

"DownstreamHeaderTransform": {
    "devops": "rdc"
  }

说明:在下游服务的response中添加一个header, key是devops, value是rdc

"UpstreamHeaderTransform": {
    "lakin": "rdc",
    "CI": "msbuild, jenkins",
    "Location": "http://localhost:5001, {BaseUrl}"
  }

示例说明

当访问http://localhost:5000/api/orders后,结果会缓存60秒,在缓存有效期内即使原始的order api的返回结果发生变化,通过网关请求时,还是会返回缓存的结果。

验证

// GET: api/shopping-carts
[Route("api/shopping-carts")]
[HttpGet]
public IEnumerable<string> Get()
{
    Console.WriteLine($"开始打印header信息");
    foreach (var item in this.Request.Headers)
    {
        Console.WriteLine($"{item.Key} - {item.Value}");
    }
    Console.WriteLine($"打印header信息完成");
    return new string[] { "洗发水", "无人机" };
}
    "CI": "msbuild",
    "Location": "http://localhost:5001"
开始打印header信息CI
lakin - rdc
CI - jenkins
Location - http://localhost:5000
打印header信息完成
devops - rdc

HTTP方法转换

Ocelot允许在路由时转化HTTP方法

{
  "DownstreamPathTemplate": "/api/shopping-carts",
  "DownstreamScheme": "http",
  "DownstreamHttpMethod": "POST",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ],
  "UpstreamPathTemplate": "/api/shopping-carts",
  "UpstreamHttpMethod": [ "Get" ]
}

示例说明
上述示例中,将GET /api/shopping-carts 路由到 POST /api/shopping-carts, 将GET转换成了POST

适用场景:例如有些已经存在的的API,因为某些历史原因都是用POST,在通过网关对外提供服务时,就可以按照标准API进行转换

验证

"洗发水", "无人机"
[Route("api/shopping-carts")]
[HttpPost]
public string Post()
{
    return "添加商品到购物车成功";
}
添加商品到购物车成功

负载均衡

Ocelot内置了负载均衡,我们先来看配置

{
     "DownstreamPathTemplate": "/api/orders",
     "DownstreamScheme": "http",
     "DownstreamHostAndPorts": [
       {
         "Host": "localhost",
         "Port": 5001
       },
       {
         "Host": "localhost",
         "Port": 6001
       }
     ],
     "UpstreamPathTemplate": "/api/orders",
     "UpstreamHttpMethod": [ "Get" ],
     "LoadBalancerOptions": {
       "Type": "RoundRobin"
     }
}

配置说明

在DownstreamHostAndPorts指指定多个服务地址

在Reroute节点中添加LoadBalancerOptions,这是负载均衡的配置节点,其中Type属性指定了负载均衡的算法, 它有如下几个值:

验证

[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    return new string[] { "刘明的订单", "王天的订单" };
}
[Route("api/orders")]
[HttpGet]
public  IEnumerable<string> Get()
{
    return new string[] { "帅的订单", "我的订单" };
}

第一次的结果是

"刘明的订单", "王天的订单"

第二次的结果是

"帅的订单", "我的订单"

第三次的结果是

"刘明的订单", "王天的订单"

注入/重写中间件

Ocelot本身是一组中间件,它也提供了方式来注入和重写其中的某些中间件:

下面是注入PreErrorResponderMiddleware中间件的代码示例:

//注入中间件
var configuration = new OcelotPipelineConfiguration
{
    PreErrorResponderMiddleware = async (ctx, next) =>
    {
        ctx.HttpContext.Request.Headers.Add("myreq", "ocelot-request");
        await next.Invoke();
    }
};
app.UseOcelot(configuration).Wait();

注意: Ocelot也是一组中间件,所以可以在Ocelot中间件之前,按常规方式添加任何中间件, 但是不能在Ocelot中间件之后添加,因为Ocelot没有调用 next

后台管理

Ocelot提供了一组后台管理的API, 从前三篇文章可以看出,Ocelot主要也就是配置文件的管理,所以API主要也就是管理配置

最后

本篇我们介绍了Ocelot的限流、熔断、缓存、负载均衡以及其他一些特性。到目前为止,Ocelot的基本配置和功能都已经介绍完了。接下里我们会结合consul来介绍服务发现,以及Ocelot和Consul的集成

示例代码下载地址: https://github.com/lcyhjx/ocelot-demo/tree/master

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!