zoukankan      html  css  js  c++  java
  • Ocelot 配置学习笔记

    Ocelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由、请求聚合、服务发现、认证、鉴权、限流熔断、并内置了负载均衡器与Service Fabric、Butterfly Tracing集成。这些功能只都只需要简单的配置即可完成,下面我们会对这些功能的配置一一进行说明。

    在asp.net core 2.0里通过nuget即可完成集成,或者命令行dotnet add package Ocelot以及通过vs2017 UI添加Ocelot nuget引用都可以。

    Install-Package Ocelot


    基本的配置信息。

    {
        "ReRoutes": [],
        "GlobalConfiguration": {
        "BaseUrl": "https://api.mybusiness.com"
        }
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration( (hostingContext,builder) => {
                builder
                .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
                .AddJsonFile("Ocelot.json");
            })
            .UseStartup<Startup>()
            .Build();


    配置依赖注入与中间件

    在startup.cs中我们首先需要引用两个命名空间

    using Ocelot.DependencyInjection;
    using Ocelot.Middleware;

    接下来就是添加依赖注入和中间件

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOcelot();
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseOcelot().Wait();
    }


    说明:
    • Downstream是下游服务配置
    • UpStream是上游服务配置
    • Aggregates 服务聚合配置
    • ServiceName, LoadBalancer, UseServiceDiscovery 配置服务发现
    • AuthenticationOptions 配置服务认证
    • RouteClaimsRequirement 配置Claims鉴权
    • RateLimitOptions为限流配置
    • FileCacheOptions 缓存配置
    • QosOptions 服务质量与熔断
    • DownstreamHeaderTransform头信息转发
    • DownstreamPathTemplate:下游戏
    • DownstreamScheme:下游服务http schema
    • DownstreamHostAndPorts:下游服务的地址,如果使用LoadBalancer的话这里可以填多项
    • UpstreamPathTemplate: 上游也就是用户输入的请求Url模板
    • UpstreamHttpMethod: 上游请求http方法,可使用数组
    • Prioirty优先级
      对多个产生冲突的路由设置优化级
    • 路由负载均衡

      当下游服务有多个结点的时候,我们可以在DownstreamHostAndPorts中进行配置。

      {
          "DownstreamPathTemplate": "/api/posts/{postId}",
          "DownstreamScheme": "https",
          "DownstreamHostAndPorts": [
                  {
                      "Host": "10.0.1.10",
                      "Port": 5000,
                  },
                  {
                      "Host": "10.0.1.11",
                      "Port": 5000,
                  }
              ],
          "UpstreamPathTemplate": "/posts/{postId}",
          "LoadBalancer": "LeastConnection",
          "UpstreamHttpMethod": [ "Put", "Delete" ]
      }

     

    LoadBalancer将决定负载均衡的算法

    • LeastConnection – 将请求发往最空闲的那个服务器
    • RoundRobin – 轮流发送
    • NoLoadBalance – 总是发往第一个请求或者是服务发现

      限流

    对请求进行限流可以防止下游服务器因为访问过载而崩溃,这个功能就是我们的张善友张队进添加进去的。非常优雅的实现,我们只需要在路由下加一些简单的配置即可以完成。

    "RateLimitOptions": {
        "ClientWhitelist": [],
        "EnableRateLimiting": true,
        "Period": "1s",
        "PeriodTimespan": 1,
        "Limit": 1
    }
    • ClientWihteList 白名单
    • EnableRateLimiting 是否启用限流
    • Period 统计时间段:1s, 5m, 1h, 1d
    • PeroidTimeSpan 多少秒之后客户端可以重试
    • Limit 在统计时间段内允许的最大请求数量

    在 GlobalConfiguration下我们还可以进行以下配置

    "RateLimitOptions": {
      "DisableRateLimitHeaders": false,
      "QuotaExceededMessage": "Customize Tips!",
      "HttpStatusCode": 999,
      "ClientIdHeader" : "Test"
    }
    • Http头  X-Rate-Limit 和 Retry-After 是否禁用
    • QuotaExceedMessage 当请求过载被截断时返回的消息
    • HttpStatusCode 当请求过载被截断时返回的http status
    • ClientIdHeader 用来识别客户端的请求头,默认是 ClientId

      服务质量与熔断

    • 熔断的意思是停止将请求转发到下游服务。当下游服务已经出现故障的时候再请求也是功而返,并且增加下游服务器和API网关的负担。这个功能是用的Pollly来实现的,我们只需要为路由做一些简单配置即可

      "QoSOptions": {
          "ExceptionsAllowedBeforeBreaking":3,
          "DurationOfBreak":5,
          "TimeoutValue":5000
      }
    • ExceptionsAllowedBeforeBreaking 允许多少个异常请求
    • DurationOfBreak 熔断的时间,单位为秒
    • TimeoutValue 如果下游请求的处理时间超过多少则自如将请求设置为超时

    认证

    如果我们需要对下游API进行认证以及鉴权服务的,则首先Ocelot 网关这里需要添加认证服务。这和我们给一个单独的API或者ASP.NET Core Mvc添加认证服务没有什么区别。

    public void ConfigureServices(IServiceCollection services)
    {
        var authenticationProviderKey = "TestKey";
    
        services.AddAuthentication()
            .AddJwtBearer(authenticationProviderKey, x =>
            {
            });
    }

    然后在ReRoutes的路由模板中的AuthenticationOptions进行配置,只需要我们的AuthenticationProviderKey一致即可。

    "ReRoutes": [{
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 51876,
                }
            ],
            "DownstreamPathTemplate": "/",
            "UpstreamPathTemplate": "/",
            "UpstreamHttpMethod": ["Post"],
            "ReRouteIsCaseSensitive": false,
            "DownstreamScheme": "http",
            "AuthenticationOptions": {
                "AuthenticationProviderKey": "TestKey",
                "AllowedScopes": []
            }
        }]

    JWT Tokens

    要让网关支持JWT 的认证其实和让API支持JWT  Token的认证是一样的

    public void ConfigureServices(IServiceCollection services)
    {
        var authenticationProviderKey = "TestKey";
    
        services.AddAuthentication()
            .AddJwtBearer(authenticationProviderKey, x =>
            {
                x.Authority = "test";
                x.Audience = "test";
            });
    
        services.AddOcelot();
    }

    Identity Server Bearer Tokens

    添加Identity Server的认证也是一样

    public void ConfigureServices(IServiceCollection services)
    {
        var authenticationProviderKey = "TestKey";
        var options = o =>
            {
                o.Authority = "https://whereyouridentityserverlives.com";
                o.ApiName = "api";
                o.SupportedTokens = SupportedTokens.Both;
                o.ApiSecret = "secret";
            };
    
        services.AddAuthentication()
            .AddIdentityServerAuthentication(authenticationProviderKey, options);
    
        services.AddOcelot();
    }

    Allowed Scopes

    这里的Scopes将从当前 token 中的 claims中来获取,我们的鉴权服务将依靠于它来实现 。当前路由的下游API需要某个权限时,我们需要在这里声明 。和oAuth2中的 scope意义一致。

    鉴权

    我们通过认证中的AllowedScopes 拿到claims之后,如果要进行权限的鉴别需要添加以下配置

    "RouteClaimsRequirement": {
        "UserType": "registered"
    }

    当前请求上下文的token中所带的claims如果没有 name=”UserType” 并且 value=”registered” 的话将无法访问下游服务。

    请求头转化

    请求头转发分两种:转化之后传给下游和从下游接收转化之后传给客户端。在Ocelot的配置里面叫做Pre  Downstream Request和Post Downstream Request。目前的转化只支持查找和替换。我们用到的配置主要是 UpstreamHeaderTransform 和 DownstreamHeaderTransform

    Pre Downstream Request

    "Test": "http://www.bbc.co.uk/, http://ocelot.com/"

    比如我们将客户端传过来的Header中的 Test 值改为 http://ocelot.com/之后再传给下游

     "UpstreamHeaderTransform": {
        "Test": "http://www.bbc.co.uk/, http://ocelot.com/"
    },

    Post Downstream Request

    而我们同样可以将下游Header中的Test再转为 http://www.bbc.co.uk/之后再转给客户端。

    "DownstreamHeaderTransform": {
        "Test": "http://www.bbc.co.uk/, http://ocelot.com/"
    },

    变量

    在请求头转化这里Ocelot为我们提供了两个变量:BaseUrl和DownstreamBaseUrl。BaseUrl就是我们在GlobalConfiguration里面配置的BaseUrl,后者是下游服务的Url。这里用301跳转做一个示例如何使用这两个变量。

    默认的301跳转,我们会返回一个Location的头,于是我们希望将http://www.bbc.co.uk 替换为 http://ocelot.com,后者者网关对外的域名。

    "DownstreamHeaderTransform": {
        "Location": "http://www.bbc.co.uk/, http://ocelot.com/"
    },
     "HttpHandlerOptions": {
        "AllowAutoRedirect": false,
    },

    我们通过DownstreamHeaderTranfrom将下游返回的请求头中的Location替换为了网关的域名,而不是下游服务的域名。所以在这里我们也可以使用BaseUrl来做为变量替换。

    "DownstreamHeaderTransform": {
        "Location": "http://localhost:6773, {BaseUrl}"
    },
     "HttpHandlerOptions": {
        "AllowAutoRedirect": false,
    },

    当我们的下游服务有多个的时候,我们就没有办法找到前面的那个http://localhost:6773,因为它可能是多个值。所以这里我们可以使用DownstreamBaseUrl。

    "DownstreamHeaderTransform": {
        "Location": "{DownstreamBaseUrl}, {BaseUrl}"
    },
     "HttpHandlerOptions": {
        "AllowAutoRedirect": false,
    },


    Claims转化

    Claims转化

     

    Claims转化功能可以将Claims中的值转化到请求头、Query String、或者下游的Claims中,对于Claims的转化,比较特殊的一点是它提供了一种对字符串进行解析的方法。举个例子,比如我们有一个sub的claim。这个claims的 name=”sub” value=”usertypevalue|useridvalue”,实际上我们不会弄这么复杂的value,它是拼接来的,但是我们为了演示这个字符串解析的功能,所以使用了这么一个复杂的value。

    Ocelot为我们提供的功能分为三段,第一段是Claims[sub],很好理解[] 里面是我们的claim的名称。第二段是  > 表示对字符串进行拆分, 后面跟着拆分完之后我们要取的那个数组里面的某一个元素用 value[index]来表示,取第0位元素也可以直接用value。第三段也是以 > 开头后面跟着我们的分隔符,在我们上面的例子分隔符是 |

    所以在这里如果我们要取  usertype这个claim就会这样写: Claims[sub] > value[0] > |

    Claim取到之后我们如果要放到请求头、QueryString、以及Claim当中对应有以下三个配置。

    Claims to Claims 

    "AddClaimsToRequest": {
        "UserType": "Claims[sub] > value[0] > |",
        "UserId": "Claims[sub] > value[1] > |"
    }

    Claims to Headers 

    "AddHeadersToRequest": {
        "CustomerId": "Claims[sub] > value[1] > |"
    }

    这里我们还是用的上面那个 sub = usertypevalue|useridvalue 的claim来进行处理和转化。

    Claims to Query String

    "AddQueriesToRequest": {
        "LocationId": "Claims[LocationId] > value",
    }

    这里没有进行分隔,所以直接取了value。

    NanoFabric 配置

      1 {
      2   "ReRoutes": [
      3     {
      4       "DownstreamPathTemplate": "/api/values",
      5       "DownstreamScheme": "http",
      6       "UpstreamPathTemplate": "/api/values",
      7       "UpstreamHttpMethod": [ "Get" ],
      8       "ServiceName": "SampleService_Kestrel",
      9       "LoadBalancerOptions": {
     10         "Type": "LeastConnection"
     11       },
     12       "UseServiceDiscovery": true,
     13       "FileCacheOptions": { "TtlSeconds": 15 }
     14     },
     15     {
     16       "DownstreamPathTemplate": "/",
     17       "DownstreamScheme": "http",
     18       "DownstreamHostAndPorts": [
     19         {
     20           "Host": "localhost",
     21           "Port": 52876
     22         }
     23       ],
     24       "UpstreamPathTemplate": "/identityserverexample",
     25       "UpstreamHttpMethod": [ "Get" ],
     26       "QoSOptions": {
     27         "ExceptionsAllowedBeforeBreaking": 3,
     28         "DurationOfBreak": 10,
     29         "TimeoutValue": 5000
     30       },
     31       "AuthenticationOptions": {
     32         "AuthenticationProviderKey": "TestKey",
     33         "AllowedScopes": [
     34           "openid",
     35           "offline_access"
     36         ]
     37       },
     38       "AddHeadersToRequest": {
     39         "CustomerId": "Claims[CustomerId] > value",
     40         "LocationId": "Claims[LocationId] > value",
     41         "UserType": "Claims[sub] > value[0] > |",
     42         "UserId": "Claims[sub] > value[1] > |"
     43       },
     44       "AddClaimsToRequest": {
     45         "CustomerId": "Claims[CustomerId] > value",
     46         "LocationId": "Claims[LocationId] > value",
     47         "UserType": "Claims[sub] > value[0] > |",
     48         "UserId": "Claims[sub] > value[1] > |"
     49       },
     50       "AddQueriesToRequest": {
     51         "CustomerId": "Claims[CustomerId] > value",
     52         "LocationId": "Claims[LocationId] > value",
     53         "UserType": "Claims[sub] > value[0] > |",
     54         "UserId": "Claims[sub] > value[1] > |"
     55       },
     56       "RouteClaimsRequirement": {
     57         "UserType": "registered"
     58       },
     59       "RequestIdKey": "OcRequestId"
     60     },
     61     {
     62       "DownstreamPathTemplate": "/posts",
     63       "DownstreamScheme": "https",
     64       "DownstreamHostAndPorts": [
     65         {
     66           "Host": "jsonplaceholder.typicode.com",
     67           "Port": 443
     68         }
     69       ],
     70       "UpstreamPathTemplate": "/posts",
     71       "UpstreamHttpMethod": [ "Get" ],
     72       "QoSOptions": {
     73         "ExceptionsAllowedBeforeBreaking": 3,
     74         "DurationOfBreak": 10,
     75         "TimeoutValue": 5000
     76       }
     77     },
     78     {
     79       "DownstreamPathTemplate": "/posts/{postId}",
     80       "DownstreamScheme": "http",
     81       "DownstreamHostAndPorts": [
     82         {
     83           "Host": "jsonplaceholder.typicode.com",
     84           "Port": 80
     85         }
     86       ],
     87       "UpstreamPathTemplate": "/posts/{postId}",
     88       "UpstreamHttpMethod": [ "Get" ],
     89       "RequestIdKey": "ReRouteRequestId",
     90       "QoSOptions": {
     91         "ExceptionsAllowedBeforeBreaking": 3,
     92         "DurationOfBreak": 10,
     93         "TimeoutValue": 5000
     94       }
     95     },
     96     {
     97       "DownstreamPathTemplate": "/posts/{postId}/comments",
     98       "DownstreamScheme": "http",
     99       "DownstreamHostAndPorts": [
    100         {
    101           "Host": "jsonplaceholder.typicode.com",
    102           "Port": 80
    103         }
    104       ],
    105       "UpstreamPathTemplate": "/posts/{postId}/comments",
    106       "UpstreamHttpMethod": [ "Get" ],
    107       "QoSOptions": {
    108         "ExceptionsAllowedBeforeBreaking": 3,
    109         "DurationOfBreak": 10,
    110         "TimeoutValue": 5000
    111       }
    112     },
    113     {
    114       "DownstreamPathTemplate": "/comments",
    115       "DownstreamScheme": "http",
    116       "DownstreamHostAndPorts": [
    117         {
    118           "Host": "jsonplaceholder.typicode.com",
    119           "Port": 80
    120         }
    121       ],
    122       "UpstreamPathTemplate": "/comments",
    123       "UpstreamHttpMethod": [ "Get" ],
    124       "QoSOptions": {
    125         "ExceptionsAllowedBeforeBreaking": 3,
    126         "DurationOfBreak": 10,
    127         "TimeoutValue": 5000
    128       }
    129     },
    130     {
    131       "DownstreamPathTemplate": "/posts",
    132       "DownstreamScheme": "http",
    133       "DownstreamHostAndPorts": [
    134         {
    135           "Host": "jsonplaceholder.typicode.com",
    136           "Port": 80
    137         }
    138       ],
    139       "UpstreamPathTemplate": "/posts",
    140       "UpstreamHttpMethod": [ "Post" ],
    141       "QoSOptions": {
    142         "ExceptionsAllowedBeforeBreaking": 3,
    143         "DurationOfBreak": 10,
    144         "TimeoutValue": 5000
    145       }
    146     },
    147     {
    148       "DownstreamPathTemplate": "/posts/{postId}",
    149       "DownstreamScheme": "http",
    150       "DownstreamHostAndPorts": [
    151         {
    152           "Host": "jsonplaceholder.typicode.com",
    153           "Port": 80
    154         }
    155       ],
    156       "UpstreamPathTemplate": "/posts/{postId}",
    157       "UpstreamHttpMethod": [ "Put" ],
    158       "QoSOptions": {
    159         "ExceptionsAllowedBeforeBreaking": 3,
    160         "DurationOfBreak": 10,
    161         "TimeoutValue": 5000
    162       }
    163     },
    164     {
    165       "DownstreamPathTemplate": "/posts/{postId}",
    166       "DownstreamScheme": "http",
    167       "DownstreamHostAndPorts": [
    168         {
    169           "Host": "jsonplaceholder.typicode.com",
    170           "Port": 80
    171         }
    172       ],
    173       "UpstreamPathTemplate": "/posts/{postId}",
    174       "UpstreamHttpMethod": [ "Patch" ],
    175       "QoSOptions": {
    176         "ExceptionsAllowedBeforeBreaking": 3,
    177         "DurationOfBreak": 10,
    178         "TimeoutValue": 5000
    179       }
    180     },
    181     {
    182       "DownstreamPathTemplate": "/posts/{postId}",
    183       "DownstreamScheme": "http",
    184       "DownstreamHostAndPorts": [
    185         {
    186           "Host": "jsonplaceholder.typicode.com",
    187           "Port": 80
    188         }
    189       ],
    190       "UpstreamPathTemplate": "/posts/{postId}",
    191       "UpstreamHttpMethod": [ "Delete" ],
    192       "QoSOptions": {
    193         "ExceptionsAllowedBeforeBreaking": 3,
    194         "DurationOfBreak": 10,
    195         "TimeoutValue": 5000
    196       }
    197     },
    198     {
    199       "DownstreamPathTemplate": "/api/products",
    200       "DownstreamScheme": "http",
    201       "DownstreamHostAndPorts": [
    202         {
    203           "Host": "jsonplaceholder.typicode.com",
    204           "Port": 80
    205         }
    206       ],
    207       "UpstreamPathTemplate": "/products",
    208       "UpstreamHttpMethod": [ "Get" ],
    209       "QoSOptions": {
    210         "ExceptionsAllowedBeforeBreaking": 3,
    211         "DurationOfBreak": 10,
    212         "TimeoutValue": 5000
    213       },
    214       "FileCacheOptions": { "TtlSeconds": 15 }
    215     },
    216     {
    217       "DownstreamPathTemplate": "/api/products/{productId}",
    218       "DownstreamScheme": "http",
    219       "DownstreamHostAndPorts": [
    220         {
    221           "Host": "jsonplaceholder.typicode.com",
    222           "Port": 80
    223         }
    224       ],
    225       "UpstreamPathTemplate": "/products/{productId}",
    226       "UpstreamHttpMethod": [ "Get" ],
    227       "FileCacheOptions": { "TtlSeconds": 15 }
    228     },
    229     {
    230       "DownstreamPathTemplate": "/api/products",
    231       "DownstreamScheme": "http",
    232       "DownstreamHostAndPorts": [
    233         {
    234           "Host": "jsonplaceholder.typicode.com",
    235           "Port": 80
    236         }
    237       ],
    238       "UpstreamPathTemplate": "/products",
    239       "UpstreamHttpMethod": [ "Post" ],
    240       "QoSOptions": {
    241         "ExceptionsAllowedBeforeBreaking": 3,
    242         "DurationOfBreak": 10,
    243         "TimeoutValue": 5000
    244       }
    245     },
    246     {
    247       "DownstreamPathTemplate": "/api/products/{productId}",
    248       "DownstreamScheme": "http",
    249       "DownstreamHostAndPorts": [
    250         {
    251           "Host": "jsonplaceholder.typicode.com",
    252           "Port": 80
    253         }
    254       ],
    255       "UpstreamPathTemplate": "/products/{productId}",
    256       "UpstreamHttpMethod": [ "Put" ],
    257       "QoSOptions": {
    258         "ExceptionsAllowedBeforeBreaking": 3,
    259         "DurationOfBreak": 10,
    260         "TimeoutValue": 5000
    261       },
    262       "FileCacheOptions": { "TtlSeconds": 15 }
    263     },
    264     {
    265       "DownstreamPathTemplate": "/posts",
    266       "DownstreamScheme": "http",
    267       "DownstreamHostAndPorts": [
    268         {
    269           "Host": "jsonplaceholder.typicode.com",
    270           "Port": 80
    271         }
    272       ],
    273       "UpstreamPathTemplate": "/posts/",
    274       "UpstreamHttpMethod": [ "Get" ],
    275       "QoSOptions": {
    276         "ExceptionsAllowedBeforeBreaking": 3,
    277         "DurationOfBreak": 10,
    278         "TimeoutValue": 5000
    279       },
    280       "FileCacheOptions": { "TtlSeconds": 15 }
    281     },
    282     {
    283       "DownstreamPathTemplate": "/",
    284       "DownstreamScheme": "http",
    285       "DownstreamHostAndPorts": [
    286         {
    287           "Host": "www.bbc.co.uk",
    288           "Port": 80
    289         }
    290       ],
    291       "UpstreamPathTemplate": "/bbc/",
    292       "UpstreamHttpMethod": [ "Get" ]
    293     }
    294   ],
    295   "GlobalConfiguration": {
    296     "RequestIdKey": "ot-traceid",
    297     "BaseUrl": "http://localhost:8000",
    298     "ServiceDiscoveryProvider": {
    299       "Host": "localhost",
    300       "Port": 8500
    301     }
    302   }
    303 }

     




  • 相关阅读:
    Django学习笔记8
    Django杂记
    Django学习笔记7
    MongoDB
    python爬虫pyquery库详解
    python爬虫urllib库详解
    Django学习笔记13
    OpenCV(二)
    [译文]casperjs的API-colorizer模块
    什么是探索性测试?
  • 原文地址:https://www.cnblogs.com/gavinhuang/p/9187704.html
Copyright © 2011-2022 走看看