.NET Core + Ocelot:API 网关

关于 API 网关的作用,核心是 API 请求的收口及控制,如:鉴权、限流、熔断、数据缓存 等都是开发中常见的需求,将此类需求交给网关层处理,可以使每个微服务更聚焦于业务功能开发,同时也可为下游服务的安全及稳定性保驾护航。

在之前的文章 .NET Core + Spring Cloud:API 网关 有介绍过如何基于 Spring Cloud 中的 Zuul 实现 API 网关,功能实现上抛开不提,另外一个较大的特点是 .NET Core 可以完美的拥抱 Java 体系中的部分能力。本文将主要介绍 .NET Core 体系中的 API 网关框架:Ocelot,它包含了 路由、鉴权、限流、熔断、服务发现、请求聚合等非常丰富的功能,这些功能大多基于少量的配置实现,使用起来也并不复杂。

接下来通过简单例子先跑起来,然后再继续延伸更多特性的介绍,下面是 Ocelot 官方给出的一个最基础的架构图:

外网访问 Ocelot API 网关服务(单实例),通过配置的规则(configuration.json),路由到下游的两个微服务实例(Http Service),这也就是最基本的转发能力。

路由转发

以下创建的 .NET Core API 服务均基于 .NET Core 3.1

  1. 创建微服务(ServiceA),并启动2个实例,两个实例使用的配置文件设置不同的 Id,方便后面接口调用识别不同实例。

    [Route("[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {
      public readonly IConfiguration _configuration;
    
      public TestController(IConfiguration configuration)
      {
        _configuration = configuration;
      }
    
      [HttpGet]
      public string Get()
      {
        return $"service-a {_configuration["Id"]}";
      }
    }
    
  2. 创建网关服务

    • 安装 Ocelot NuGet 包;
    • 创建配置文件 configuration.json,内容如下:
      {
        "Routes": [   // 路由规则定义,数组
          {
            "DownstreamPathTemplate": "/{url}",   // 下游路径匹配模板
            "DownstreamScheme": "http", 
            "DownstreamHostAndPorts": [           // 下游服务的 host 和 port 设置,支持多实例
              {
                "Host": "192.168.124.11",
                "Port": 8000
              },
              {
                "Host": "192.168.124.11",
                "Port": 8001
              }
            ],
            "UpstreamPathTemplate": "/servicea/{url}",  //  客户端访问地址路径匹配模板
            "UpstreamHttpMethod": [ "Get" ],            // 支持的 HttpMethod ,如:Get、Post、Put、Delete 等
            "LoadBalancerOptions": {                    // 多实例下负载均衡方式,支持:LeastConnection(最闲)、RoundRobin(轮询)、NoLoadBalance
              "Type": "RoundRobin"
            }
          }
        ]
      }
      
    • Program.cs 中添加 Ocelot 配置文件引用
      public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
          .ConfigureWebHostDefaults(webBuilder =>
          {
            webBuilder.UseStartup<Startup>();
            webBuilder.UseUrls("http://*:9600");
            webBuilder.ConfigureAppConfiguration(c =>
            {
              c.AddJsonFile("configuration.json");
            });
          });
      
    • Startup.cs 中注册服务与管道配置
      public void ConfigureServices(IServiceCollection services)
      {
        services.AddControllers();
        services.AddOcelot();
      }
      
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
        // ....
        
        app.UseOcelot().Wait();
      
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
          endpoints.MapControllers();
        });
      }
      
  3. 网关层接口调用测试
    通过以上服务搭建,就完成了路由转发的功能,即当访问 /servicea/{任意路由地址} 都将自动转发到下游任意一个服务实例中相匹配的路由地址,网关服务访问地址为:http://192.168.124.11:9600192.168.124.11 是本机的 IPV4 地址),测试效果如下(下游服务实例被轮询访问):

服务发现(Consul)

Ocelot 支持与具备 服务发现 功能的一些框架相结合,如:ConsulEureka,下游服务地址可直接从服务注册中心进行获取。接下来将结合 Consul 进行测试,有关 Consul 与 .NET Core 结合请参考文章:.NET Core + Consul 服务注册与发现,这部分内容这里就不重复介绍了,最终注册中心 service-a 有两个实例,如下:

  1. 安装 Ocelot.Provider.Consul NuGet 包;

  2. Startup.cs 中进行服务注册:

    services.AddOcelot().AddConsul();
    
  3. configuration.json 配置修改为如下:

    {
      "GlobalConfiguration": {
        "ServiceDiscoveryProvider": {  // 提供服务发现的 Provider
          "Scheme": "http",
          "Host": "192.168.124.9",     // Consul 服务 host
          "Port": 8500,                // Consul 服务端口
          "Type": "Consul"             // 类型
        }
      },
      "Routes": [
        {
          "DownstreamPathTemplate": "/{url}",
          "DownstreamScheme": "http",
          "ServiceName": "service-a",  // 注册的服务名
          "UpstreamPathTemplate": "/servicea/{url}",
          "UpstreamHttpMethod": [ "Get" ],
          "LoadBalancerOptions": {
            "Type": "RoundRobin"
          }
        }
      ]
    }
    

最终测试结果与上一部分一致,所以 Ocelot 完全可以与服务注册发现相结合应用到项目中。

限流

为了可以防止因请求过载而引起服务不稳定,可为路由规则添加相应的限流配置,如下:

"RateLimitOptions": {
  "ClientWhitelist": [ "clientId1" ],
  "EnableRateLimiting": true, 
  "Period": "5s", 
  "PeriodTimespan": 5,
  "Limit": 5  // 测试设置比较小
}

ClientWhitelist:限流白名单。如上,当请求头中包含 ClientId=clientId1 的请求则不受限流规则控制(ClientId key 名可修改
EnableRateLimiting:开启限流
Period:限流控制时间段,也就是多长时间内。支持 s(秒)、m(分)、h(小时)、d(天)
PeriodTimespan:超过限制次数后,需要等待的时长(秒)
Limit:在 Period 时长内最大访问次数

当超出限流数量时,默认返回如下:

如果需要修改返回值及状态码等可以通过修改 GlobalConfiguration 配置中的 RateLimitOptions 参数。

熔断

熔断是结合 Polly 实现的,在使用之前需要先安装 Ocelot.Provider.Polly NuGet 包,然后添加服务注册,如下:

services.AddOcelot() .AddConsul().AddPolly();

路由规则中增加如下配置:

"QoSOptions": { 
  "ExceptionsAllowedBeforeBreaking": 3,
  "DurationOfBreak": 5000,
  "TimeoutValue": 3000
}

ExceptionsAllowedBeforeBreaking:允许连续发生异常次数
DurationOfBreak:熔断时长(ms)
TimeoutValue:请求超时时间(ms)

当超出允许异常次数时,接口 5s 内都会返回 503:

网关高可用

API 网关是所有请求的唯一入口,压力自然是比较大的,自身的高可用也很关键,所以网关服务在部署上必须多实例,网关上层还需要添加一层 LB,官方架构图如下:

Ocelot 整体主要围绕配置进行功能扩充,本文只介绍了部分 Ocelot 的功能,另外还有 鉴权、缓存、请求合并、与 Kubernetes 结合等都是非常普遍的功能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345