zoukankan      html  css  js  c++  java
  • .Net微服务实践(三):Ocelot配置路由和请求聚合

    .Net微服务实践(三):Ocelot配置路由和请求聚合

    在上篇.Net微服务实践(二):Ocelot介绍和快速开始中我们介绍了Ocelot,创建了一个Ocelot Hello World程序,接下来,我们会介绍Oclot的主要特性路由和另外一个特性请求聚合。这些特性都是通过配置来实现的。

    配置

    {
        "ReRoutes": [],
        "GlobalConfiguration": {}
    }
    

    Ocelot的配置文件包含两个节点: ReRoutes和GlobalConfiguration

    • ReRoutes - 告诉Ocelot如何处理上游的请求
    • GlobalConfiguration - 全局配置,此节点的配置允许覆盖ReRoutes里面的配置,你可以在这里进行通用的一些配置信息

    Ocelot的完整配置项如下

    {
              "DownstreamPathTemplate": "/",
              "UpstreamPathTemplate": "/",
              "UpstreamHttpMethod": [
                  "Get"
              ],
              "DownstreamHttpMethod": "",
              "DownstreamHttpVersion": "",
              "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,
                  "MaxConnectionsPerServer": 100
              },
              "DangerousAcceptAnyServerCertificateValidator": false
          }
    

    完整配置项中的每一项具体含义和作用接下来会一一介绍,大的配置项的主要含义如下:

    • Downstream - 下游服务配置
    • UpStream - 上游服务配置
    • Aggregates - 服务聚合配置
    • ServiceName, LoadBalancer, UseServiceDiscovery - 配置服务发现
    • AuthenticationOptions - 配置服务认证
    • RouteClaimsRequirement - 配置Claims鉴权
    • RateLimitOptions - 限流配置
    • FileCacheOptions - 缓存配置
    • QosOptions - 服务质量与熔断
    • DownstreamHeaderTransform - 头信息转发

    路由

    基本配置

    在上一篇的hello world程序中使用的就是基本配置

    {
      "ReRoutes": [
        {
          "DownstreamPathTemplate": "/api/orders",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "localhost",
              "Port": 5001
            }
          ],
          "UpstreamPathTemplate": "/api/orders",
          "UpstreamHttpMethod": [ "Get" ]
        }
      ],
      "GlobalConfiguration": {
        "BaseUrl": "http://localhost:5000"
      }
    }
    
    • BaseUrl - Ocelot的服务运行地址,要特别注意一下BaseUrl是我们外部暴露的Url,比如我们的Ocelot运行在http://localhost:5000,但是前面有一个 nginx绑定了域名http://api.demo.com,那这里我们的BaseUrl就是 http://api.demo.com
    • UpstreamPathTemplate、UpstreamHttpMethod - 配置上游服务器请求URL
    • DownstreamPathTemplate、DownstreamScheme、DownstreamHostAndPorts - 配置下游服务器请求URL

    在基本配置的示例中:要实现的功能就是将 http://localhost:5000/api/orders GET 请求路由到 http://localhost:5001/api/orders GET

    占位符

    在Ocelot中,可以以{something}的形式将变量的占位符添加到模板中。占位符变量需要同时出现在DownstreamPathTemplate和UpstreamPathTemplate属性中。请求时Ocelot将尝试请求时进行替换

    {
          "DownstreamPathTemplate": "/api/{everything}",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "localhost",
              "Port": 5002
            }
          ],
          "UpstreamPathTemplate": "/api/{everything}",
          "UpstreamHttpMethod": [ "Get" ]
    }
    

    示例说明:所有http://localhost:5000/api/XXXXXX的请求都会路由到http://localhost:5002/api/XXXXXX

    例如http://localhost:5000/api/products 路由到 http://localhost:5002/api/products

    例如http://localhost:5000/api/products/1 路由到 http://localhost:5002/api/products/1

    验证

    修改配置,运行示例程序, 访问http://localhost:5000/api/products,返回了产品数据

    注意:在添加Ocelot.json文件时 .AddJsonFile("Ocelot.json",false,true), 第三个参数是指定文件发生变化时,是否重新加载,示例程序中是true. 所以我们只要修改运行目录下的配置文件,不用重新运行示例程序。

    万能模板

    既然占位符可以做通用匹配,自然而然就有一种配置可以匹配所有请求

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

    示例说明: 转发所有的请求到http://localhost:5002

    验证

    修改配置,运行示例程序, 访问http://localhost:5000/api/products,返回了产品数据

    优先级

    如果一个上游请求有多个路由配置都能匹配,到底该使用哪个路由呢? 路由可以配置优先级(Priority), 0最小,路由会使用优先级高的(说明:如果多个匹配路由优先级一样,则按顺序使用第一个)

    • 在product-api中添加一个category api
    [ApiController]
    public class CategoryController : ControllerBase
    {
        // GET: api/Product
        [Route("api/categories")]
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "电子产品", "医护用品" };
        }
    }
    
    • 修改Ocelot.json配置文件如下
    {
      "DownstreamPathTemplate": "/api/products",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5002
        }
      ],
      "UpstreamPathTemplate": "/api/products",
      "UpstreamHttpMethod": [ "Get" ],
      "Priority": 0
    },
    {
      "DownstreamPathTemplate": "/api/categories",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5002
        }
      ],
      "UpstreamPathTemplate": "/api/{everything}",
      "UpstreamHttpMethod": [ "Get" ],
      "Priority": 1
    }
    

    如果这时访问http://localhost:5000/api/products, 大家猜一下,是返回产品数据还是类别数据?

    验证

    修改配置,运行示例程序, 访问http://localhost:5000/api/products,返回了类别数据, 因为类别路由的优先级是1, 优先级更高

    查询参数

    • 在order-api中添加一个订单明细的api
    [Route("api/orders/{id}")]
    [HttpGet]
    public string Get(int id)
    {
        string order = string.Empty;
        switch(id)
        {
            case 1:
                order = "刘明的订单";
                break;
            case 2:
                order = "王天的订单";
                break;
            default:
                order = "没有找到订单";
                break;
        }
        return order;
    
    }
    
    • 修改Ocelot.json配置如下
    {
      "DownstreamPathTemplate": "/api/orders/{id}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/api/orders?id={id}",
      "UpstreamHttpMethod": [ "Get" ]
    }
    

    我们期望的结果是,当访问http://localhost:5000/api/orders?id=1 (下游服务实际没这个接口)时 路由到http://localhost:5001/api/orders/1返回订单明细

    验证

    修改配置,运行示例程序, 访问http://localhost:5000/api/orders?id=1,返回了订单明细数据

    请求聚合

    有一种场景,前端一个页面,调用了多个API,要同时开多个连接几次调用才能全部所需要的数据,为了减少不必要的请求和开销,Ocelot也支持请求聚合

    默认聚合

    • 修改配置文件,在ReRoutes 添加如下配置
    {
      "DownstreamPathTemplate": "/api/orders",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/api/orders",
      "UpstreamHttpMethod": [ "Get" ],
      "Key": "Orders"
    },
    {
      "DownstreamPathTemplate": "/api/products",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5002
        }
      ],
      "UpstreamPathTemplate": "/api/products",
      "UpstreamHttpMethod": [ "Get" ],
      "Priority": 0,
      "Key": "Products"
    }
    

    大家注意一下,这和之前的配置有什么区别? 区别就是再每一个路由配置下多了一个 Key, Key的值可以任意定义(但建议还是按业务含义定义)

    • 在Ocelot.json中添加如下配置
    "Aggregates": [
        {
          "ReRouteKeys": [
            "Orders",
            "Products"
          ],
          "UpstreamPathTemplate": "/api/aggregates"
        }
      ]
    

    注意Aggregates配置是和在ReRoutes配置平级的

    {
        "ReRoutes": [],
        "Aggregates": [],
        "GlobalConfiguration": {}
    }
    

    示例说明: 当访问http://localhost:5000/api/aggregates, 会同时返回订单数据和产品数据

    运行示例进行验证

    既然是多个请求聚合,那么问题来了:

    • 如果其中一个服务宕机,会怎么样?

      我们停止订单服务,再次当访问http://localhost:5000/api/aggregates, 结果返回500
    • 如果其中一个服务不是宕机,而是返回500,会怎么样?

      我们修改order-api代码,在其中抛出异常
    // GET: api/Product
    [Route("api/orders")]
    [HttpGet]
    public  IEnumerable<string> Get()
    {
        throw new Exception("获取所有订单出错");
    }
    

    再次运行示例,访问http://localhost:5000/api/aggregates,Response是200, 但是body中Products节点是正常的产品数据,Orders节点里面的数据是异常信息

    自定义聚合

    如果默认的聚合返回的结果数据结构不是我们想要的,想要修改怎么办?答案是使用自定义聚合

    • 在ocelot-gateway中, 添加一个自动以聚合器FakeDefinedAggregator, 必须实现IDefinedAggregator接口。这个聚合器的功能很简单,就是将两个聚合请求的结果,用逗号拼接起来返回
    public class FakeDefinedAggregator : IDefinedAggregator
    {
        public FakeDefinedAggregator(FakeDepdendency dep)
        {
        }
    
        public async Task<DownstreamResponse> Aggregate(List<DownstreamContext> responses)
        {
            var one = await responses[0].DownstreamResponse.Content.ReadAsStringAsync();
            var two = await responses[1].DownstreamResponse.Content.ReadAsStringAsync();
    
            var merge = $"{one}, {two}";
            var headers = responses.SelectMany(x => x.DownstreamResponse.Headers).ToList();
            return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers, "some reason");
        }
    }
    
    • 注入自定义聚合器
    services.AddOcelot()
            .AddSingletonDefinedAggregator<FakeDefinedAggregator>();
    
    • 在Ocelot.json中修改配置,指定自定义聚合器
    "Aggregates": [
        {
          "ReRouteKeys": [
            "Orders",
            "Products"
          ],
          "UpstreamPathTemplate": "/api/aggregates",
          "Aggregator": "FakeDefinedAggregator"
        }
      ],
    

    与之前的配置相比,多了如下的配置,就是指定自定义聚合器的

    "Aggregator": "FakeDefinedAggregator"
    

    验证

    修改配置,运行示例程序, 访问http://localhost:5000/api/aggregate, 验证返回结果

    最后

    本篇我们介绍了Ocelot配置,只要特性路由,以及请求聚合。接下里我们会介绍Ocelot的其他特性:限流熔断、负载均衡

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

  • 相关阅读:
    TCP/IP的确认号,序列号和超时重传的学习笔记
    Linux进程的虚拟内存
    Linux内存:物理内存管理概述
    Linux进程: task_struct结构体成员
    Linux进程:管理和调度
    Golang基础(8):go interface接口
    技术管理:团队建设
    从分布式一致性谈到CAP理论、BASE理论
    技术管理:项目管理概要
    [译]深入 NGINX: 为性能和扩展所做之设计
  • 原文地址:https://www.cnblogs.com/lihaijia/p/12654792.html
Copyright © 2011-2022 走看看