微服务设计之.net core使用ocelot的网关

一、前言

  在微服务学习中,网关是关键的组件,起到服务路由、负载均衡、限流、统一认证授权的功能,目前网关的组件比较多,比如ocelot、apisix、kong、nignx等,其中ocelot是基于C#开发的开源组件,所以选择该组件实践微服务网关的内容。

二、定义

  网关(Gateway)它指的是网络中的一个关键节点或设备,用于在不同网络之间转发数据或提供网络服务,只要基于该定义的都可以称为网关,所以网关是一个广义定义,在计算机网络和物联网络中是至关重要。既然网关是一个广义定义,那具体表现在如下几个场景中。

  1、计算机网络的网关:在计算机中基础知识,计算机网络中提到网关比如路由器,在家庭或企业网络中,路由器通常被视为一种网关,它连接了内部局域网和外部互联网。路由器将来自内部网络的数据包转发到互联网,同时也将来自互联网的数据包转发到内部网络;VPN网关用于建立虚拟私人网络(VPN)的连接,使得远程用户可以安全地访问公司内部资源,比如公司内网不允许外网访问,如果要远程办公就必须通过VPN建立连接,访问公司网络。

  2、物联网中的网关:物联网网关,连接了传感器、执行器等物联网设备与云端服务器或本地网络。它通常负责数据的收集、预处理、协议转换以及安全通信;边缘网关:部署在网络的边缘,处理和分析来自设备的实时数据,降低数据传输延迟,并增强数据安全性。在工业中很多设备数据需要进行采集上传的过程,这个时候通过一个网关对各个设备数据点进行采集传输到云或者系统重,就是起到一个网关作用。

  3、微服务架构中的网关:API网关,作为微服务架构的入口点,负责接收外部请求,并根据路由规则将请求转发到相应的微服务。它还提供了诸如认证、授权、限流和监控等功能;服务网关,类似于API网关,但可能更加关注服务之间的通信和内部流量的管理;本文章总结就是微服务的API网关的内容,比如这个ocelot起到一个API网关的组件。

  4、企业网关中的网关:安全网关:用于保护企业网络免受外部威胁,提供防火墙、入侵检测等功能,应用网关,用于特定应用的网络流量管理,如负载均衡、SSL卸载等。这个是IT网络建设中考虑安全性建设的网关,提供安全的作用,保护公司的网络与防火墙一起构建的安全机制。

三、ocelot

  ocelot是基于C#开发一款开源的网关组件,通过上述的章节学习了通过nacos构建微服务的配置中心和注册中心,本文则通过ocelot学习微服务的网关。

  1、路由和请求聚合:Ocelot的核心功能之一是处理进入的HTTP请求,并根据配置将它们路由到相应的微服务上。它还能够聚合来自多个服务的响应,提供一个统一的响应给客户端。

  2、服务发现:Ocelot支持服务发现机制,能够自动检测并连接到注册的服务实例,这减少了手动配置每个服务的位置的需求。
  3、认证与鉴权:Ocelot提供了身份验证和权限检查的功能,确保只有合法的请求才能访问微服务资源。
  4、限流与熔断:为了防止系统过载,Ocelot提供了限流功能。此外,熔断机制可以在下游服务不可用时保护系统,避免雪崩效应的发生。
  5、负载均衡器:Ocelot内置了负载均衡器,可以帮助分配请求到不同的服务实例,提高系统的伸缩性和可靠性,ocelot运行自定义负载均衡策略。
  6、集成能力:Ocelot能够与其他微服务平台如Service Fabric和Butterfly Tracing集成,提供更灵活的服务管理选项。
  7、配置文件:所有的这些功能可以通过一个简单的配置文件来配置和管理,这使得开发者可以快速地设置和调整API网关的行为而无需深入代码层面进行复杂的修改。

四、集成网关

  先准备三个webApi项目(网关服务、业务服务、统一认证授权服务)其中统一认证授权服务后续章节在介绍,一个客户端进行测试。

  第一步:选择ocelot的包版本并且安装,创建一个网关服务,注入nacos服务管理平台,这里网关基于nacos的服务注册与发现,所以选择ocelot.Provider.Nacos安装包,版本为1.2.2来集成网关,如下图所示。

  

  第二步:对ocelot进行配置项的设置,在nacos配置管理中对网关服务配置Routes节点信息,如下所示:

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/a/{url}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "TestKey",
        "AllowedScopes": []
      },
      
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
            {
                "Host": "172.16.66.118",
                "Port": 5256
            },
            {
                "Host": "172.16.66.118",
                "Port": 5292
            }
        ],
      "UseServiceDiscovery": true,
      "ServiceName": "nacosservicea",
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    },
    {
      "UpstreamPathTemplate": "/b/{url}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "UseServiceDiscovery": true,
      "ServiceName": "nacosserviceb",
      "LoadBalancerOptions": {
        "Type": "LeastConnection"
      }
    },
    {
      "UpstreamPathTemplate": "/c/{url}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "UseServiceDiscovery": true,
      "ServiceName": "nacosservicec",
      "LoadBalancerOptions": {
        "Type": "LeastConnection"
      }
    },
    {
      "UpstreamPathTemplate": "/identity/{url}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "UseServiceDiscovery": true,
      "ServiceName": "identityservice",
      "LoadBalancerOptions": {
        "Type": "LeastConnection"
      }
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "Type": "Nacos"
    }
  },
  "RateLimitOptions": {
    "ClientWhitelist": [],
    "EnableRateLimiting": true,
    "Period": "5m",
    "PeriodTimespan": 30,
    "Limit": 5
  },
  "QoSOptions": {
    "ExceptionsAllowedBeforeBreaking": 3,
    "DurationOfBreak": 10000,
    "TimeoutValue": 5000
  },
  "nacos": {
    "EndPoint": "",
    "ServerAddresses": [ "http://172.16.66.118:8848" ],
    "DefaultTimeOut": 15000,
    "Namespace": "public",
    "ListenInterval": 1000,
    "ServiceName": "gateway",
    "GroupName": "DEFAULT_GROUP",
    "ClusterName": "DEFAULT",
    "Ip": "",
    "PreferredNetworks": "",
    "Port": 0,
    "Weight": 100,
    "RegisterEnabled": true,
    "InstanceEnabled": true,
    "Ephemeral": true,
    "Secure": false,
    "AccessKey": "",
    "SecretKey": "",
    "UserName": "",
    "Password": "",
    "ConfigUseRpc": true,
    "NamingUseRpc": true,
    "NamingLoadCacheAtStart": "",
    "LBStrategy": "WeightRandom",
    "Metadata": {
      "aa": "bbb",
      "cc": "ddd"
    }
  },
  "redis":{
    "ConnectionStrings":"127.0.0.1:6368"
  }
}

  上述内容是配置在nacos配置中心对应的网关服务的配置文件,其中Routes节点是网关的配置项,表示路由列表(上游地址、协议类型、请求类型、是否服务发现,naocs注册的服务名、负载均衡方式、授权),具体内容如下所述:

  1、Upstream:上游服务配置代指API网关接收到客户端请求的部分,进入API网关的流量包括网页端、移动端、客户端向API网关发送请求的统一认为是Downstream。主要包括如下几个重要参数设置,UpstreamPathTemplate是API网关接收请求的URL模式,通过模式匹配任何请求,比如在网页端发起请求地址/products/123进行匹配;UpstreamHttpMethod是允许通过此路由的请求方法(get/post/delete/put);UpstreamHeaderTransform是允许你修改或添加请求头。这对于将某些信息传递给下游服务,或者为了安全原因隐藏或修改信息,非常有用;UpstreamQueryStringTransform是允许你修改或添加查询字符串参数;FileCacheOptions是如果你想要对上游请求进行缓存,你可以在这里配置缓存选项;RateLimitOptions是如果你想要对上游请求进行速率限制,你可以在这里配置相关选项;QoSOptions是服务质量(QoS)选项允许你定义请求的优先级、超时和其他相关设置。

  2、Downstream:下游服务配置代指实际处理请求的API服务,Ocelot将服务转发给下游服务,下游服务如何处理的配置。主要包括如下几个重要参数设置,DownstreamPathTemplate是下游服务的请求路径模板,当Ocelot接收到一个与UpstreamPathTemplate匹配的请求时,它会使用这个模板来构造转发到下游服务的实际请求路径。模板中的占位符(如{postId})会被实际请求中的对应值替换;DownstreamScheme是指定了与下游服务通信时使用的协议,通常是http或者https;DownstreamHostAndPorts是一个包含下游服务主机和端口信息的数组,如果存在多个则可以配合LoadBalancer(负载均衡)方式进行更有效地选择下游服务来处理请求。所以设置LoadBalancer必须确保DownstreamHostAndPorts配置一个以上;DownstreamHeaderTransform是头部信息,由上游服务转入下游服务;

  3、ServiceName(服务名称),LoadBalancer(负载均衡),UseServiceDiscovery(服务发现) 配置服务发现,AuthenticationOptions (配置服务认证)、Aggregates(服务聚合)

备注:具体的配置参数详细参考官网https://ocelot.readthedocs.io/en/latest,对于每一个参数的用法,作用详细的描述。
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",

  "NacosConfig": {
    "Listeners": [
      {
        "Optional": false,
        "DataId": "gateway",
        "Group": "DEFAULT_GROUP"
      }
    ],
    "Namespace": "",
    "ServerAddresses": [ "http://172.16.66.118:8848/" ],
    "UserName": "nacos",
    "Password": "nacos",
    "EndPoint": "",
    "ConfigUseRpc": false,
    "NamingUseRpc": false
  },
  "JwtSettings": {
    "Secret": "ixkeE8eu2345k4zsixkeE8eu2345k4zsixkeE8eu2345k4zsixkeE8eu2345k4zsixkeE8eu2345k4zsixkeE8eu2345k4zsWeatherForecast",
    "Iss": "http://localhost:5013",
    "Aud": "api"
  }
}
踩坑:在安装软件包的时候,安装了ocelot.Provider.Nacos(1.2.2)、ocelot.Provider.Polly(23.2.2),其中polly是容错机制的组件,后续想集成,但是启动网关服务在注入服务时报错提示一个方法不存在,但是编译阶段没有错误,没有版本不兼容提示,后来发现是两个安装的组件依赖的ocelot的版本不一样,前者使用的17,后者使用的23导致错误,所以在包依赖时特别注意版本信息。

  第三步:在完成包安装后,通过上述完成包安装,配置网关服务注入nacos的配置,然后在网关的服务中注入网关服务,启用网关中间件,代码如下所示:

using Microsoft.IdentityModel.Tokens;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Nacos;
using Ocelot.Provider.Nacos.NacosClient.V2;
using System.Text;

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//配置中心
builder.Configuration.AddNacosV2Configuration(builder.Configuration.GetSection("NacosConfig"));
//服务注册
builder.Services.AddNacosAspNet(builder.Configuration);
// 网关
builder.Services.AddOcelot().AddNacosDiscovery();

var jwtConfig = builder.Configuration.GetSection("JwtSettings");
//生成密钥
var symmetricKeyAsBase64 = jwtConfig.GetValue<string>("Secret");
var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
var signingKey = new SymmetricSecurityKey(keyByteArray);
//认证参数
builder.Services.AddAuthentication(o =>
{
    o.DefaultAuthenticateScheme = "TestKey";
}).AddJwtBearer("TestKey", o => {
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,//是否验证签名,不验证的话可以篡改数据,不安全
        IssuerSigningKey = signingKey,//解密的密钥
        ValidateIssuer = true,//是否验证发行人,就是验证载荷中的Iss是否对应ValidIssuer参数
        ValidIssuer = jwtConfig.GetValue<string>("Iss"),//发行人
        ValidateAudience = true,//是否验证订阅人,就是验证载荷中的Aud是否对应ValidAudience参数
        ValidAudience = jwtConfig.GetValue<string>("Aud"),//订阅人
        ValidateLifetime = true,//是否验证过期时间,过期了就拒绝访问
        ClockSkew = TimeSpan.Zero,//这个是缓冲过期时间,也就是说,即使我们配置了过期时间,这里也要考虑进去,过期时间+缓冲,默认好像是7分钟,你可以直接设置为0
        RequireExpirationTime = true,
    };
});

var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
// 认证
app.UseAuthentication();
// 授权
app.UseAuthorization();
// 使用ocelot中间件
app.UseOcelot().Wait();
app.MapControllers();
app.Run();

  使用builder.Services.AddOcelot().AddNacosDiscovery()注入ocelot网关服务,添加服务发现方式nacos;通过app.UseOcelot().Wait();启用中间件。

  第四步、最后cmd运行网关、统一认证授权服务,业务服务按照不同端口号启动两个服务,运行后再nacos服务中心如下图所示:

  其中nacosservicea成功注册两个服务实例,然后在测试客户端中进行调用网关测试服务转发、负载均衡、熔断、限流,验证代码如下所示:

// See https://aka.ms/new-console-template for more information
using tqf.client.test;

Console.WriteLine("Hello, World!");
//
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Clear();
// 网关地址 client.BaseAddress
= new Uri("http://172.16.66.118:5215"); // 1. 需要授权的api访问,没有token时,返回http状态401 var resWithoutToken = client.GetAsync("/a/Index/testa").Result; Console.WriteLine($"Sending Request to /a/Index/testa , without token."); Console.WriteLine($"Result : {resWithoutToken.StatusCode}"); //2. 需要授权的api访问,获取令牌请求api,返回http状态200正常 client.DefaultRequestHeaders.Clear(); Console.WriteLine("\nBegin Auth...."); var jwt = TestHelper.GetJwt(); Console.WriteLine("End Auth...."); Console.WriteLine($"\nToken={jwt}"); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}"); var resWithToken = client.GetAsync("/a/Index/testa?name=tuqunfu").Result; Console.WriteLine($"\nSend Request to /a/Index/testa , with token."); Console.WriteLine($"Result : {resWithToken.StatusCode}"); Console.WriteLine(resWithToken.Content.ReadAsStringAsync().Result); //3.不需要授权的api访问,返回http状态200正常 Console.WriteLine("\nNo Auth Service Here "); client.DefaultRequestHeaders.Clear(); var res = client.GetAsync("/a/Index/testno").Result; Console.WriteLine($"Send Request to /a/Index/testno"); Console.WriteLine($"Result : {res.StatusCode}"); Console.WriteLine(res.Content.ReadAsStringAsync().Result); Console.Read();

  其中网关地址是http://172.16.66.118:5215,请求路径/a/Index/testa,匹配routes节点上游路径模板,访问nacosservicea服务的接口/Index/testa返回结果,通过多次请求访问nacosservicea服务依据负载均衡策略转发到不同实例,如下图所示: 

以上验证了网关的转发功能,负载均衡功能。

四、总结

  通过上述实践,完成对网关的认知,学习了nacos+ocelot的集成。尤其在实践Demo中遇到的版本问题,Routes节点配置项问题,所以必须通过手动编写代码,而不是复制粘贴来完成整个学习过程,否则无法遇到问题,解决问题,提高认识,达到学习的目的。

参考:https://blog.csdn.net/m0_59178355/article/details/136475951、文心一言

posted @ 2024-04-08 09:56  tuqunfu  阅读(23)  评论(0编辑  收藏  举报