• Asp .Net Core 系列:集成 Ocelot+Nacos+Swagger+Cors实现网关、服务注册、服务发现


    简介

    什么是 Ocelot ?

    Ocelot是一个开源的ASP.NET Core微服务网关,它提供了API网关所需的所有功能,如路由、认证、限流、监控等。

    Ocelot是一个简单、灵活且功能强大的API网关,它可以与现有的服务集成,并帮助您保护、监控和扩展您的微服务。

    以下是Ocelot的一些主要功能:

    1. 路由管理:Ocelot允许您定义路由规则,将请求路由到正确的微服务。
    2. 认证和授权:Ocelot支持多种认证机制,如JWT、OAuth等,并允许您定义访问控制策略,确保只有授权的用户才能访问特定的API。
    3. 限流和速率限制:Ocelot提供了一些内置的限流和速率限制功能,以确保您的服务不会受到过度的请求压力。
    4. 监控和日志:Ocelot可以收集和显示各种度量指标,帮助您了解您的服务的性能和行为。此外,它还可以将日志记录到各种日志源,以便您进行分析和故障排除。
    5. 集成:Ocelot可以与现有的服务集成,包括Kubernetes、Consul等。
    6. 易于扩展:Ocelot的设计使其易于扩展,您可以编写自己的中间件来处理特定的逻辑,例如修改请求或响应、添加自定义的认证机制等。
    7. 可扩展的配置:Ocelot使用JSON配置文件进行配置,这意味着您可以轻松地根据需要进行配置更改,而无需重新编译代码。

    总之,Ocelot是一个功能强大且易于使用的API网关,可以帮助您保护、监控和扩展您的微服务。

    官网:https://ocelot.readthedocs.io/en/latest/index.html

    什么是 Nacos ?

    Nacos是一个易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是Dynamic Naming and Configuration Service的首字母简称。Nacos提供了一组简单易用的特性集,包括动态服务发现、服务配置、服务元数据及流量管理等功能,帮助用户快速实现微服务的发现、配置和管理。Nacos还支持多种服务注册方式和服务发现方式,如DNS、RPC、原生SDK和OpenAPI等。

    此外,Nacos致力于提供更敏捷和容易的微服务平台构建、交付和管理。它是构建以“服务”为中心的现代应用架构(例如微服务范式、云原生范式)的服务基础设施,能够支持动态DNS服务权重路由和动态DNS服务等特性。

    官网:https://nacos.io/zh-cn/docs/v2/quickstart/quick-start.html

    什么是 Swagger ?

    Swagger是一种规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。它是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法、参数和模型紧密集成到服务器端的代码,允许API始终保持同步。

    此外,Swagger还提供了一个文档工具,可自动生成Web服务的API文档,使开发人员能够更轻松地理解和使用API。它还提供了一个测试工具,可以模拟对Web服务的API请求并验证响应。

    什么是 Cors ?

    CORS(跨来源资源共享,Cross-Origin Resource Sharing)是一种机制,允许Web应用程序在未经服务器明确许可的情况下,通过浏览器向服务器发送跨域请求。CORS是一种W3C规范,旨在解决Web应用程序中的跨域问题,以促进Web应用程序的安全性和可扩展性。

    在Web应用程序中,浏览器会遵循同源策略(Same-Origin Policy),即默认只允许来自同一域的页面之间进行通信。然而,随着Web应用程序的发展,越来越多的应用程序需要与不同域的资源进行交互,例如使用第三方API或进行跨域请求。为了解决这个问题,CORS规范允许服务器通过设置适当的HTTP标头来明确地允许跨域请求。

    当浏览器向服务器发送跨域请求时,服务器可以在响应头中包含一个Access-Control-Allow-Origin标头,指定允许哪些源进行跨域请求。浏览器会检查这个标头,如果允许的源与请求的源匹配,则允许跨域请求。此外,CORS规范还定义了其他一些相关的标头,如Access-Control-Allow-MethodsAccess-Control-Allow-Headers等,以进一步控制跨域请求的行为。

    通过使用CORS机制,Web应用程序可以更安全、更有效地进行跨域请求,提高了应用程序的可扩展性和用户体验。

    Asp .Net Core 集成 Ocelot

    要在ASP.NET Core中集成Ocelot,您可以按照以下步骤进行操作:

    1. 安装Ocelot NuGet包:
      在您的ASP.NET Core项目中,打开终端或NuGet包管理器控制台,并运行以下命令来安装Ocelot的NuGet包:
    dotnet add package Ocelot
    
    1. 添加Ocelot配置文件:
    {
      "Routes": [ //这里注意一下版本(旧版本用ReRoutes)
        {
          "DownstreamPathTemplate": "/api/{controller}", //下游路径模板
          "DownstreamScheme": "http", //下游方案
          //"DownstreamHostAndPorts": [
          //  {
          //    "Host": "localhost",
          //    "Port": "5014"
          //  }
          //], //下游主机和端口
          "UpstreamPathTemplate": "/api/product/{controller}", //上游路径模板
          "UpstreamHttpMethod": [], //上游请求方法,可以设置特定的 HTTP 方法列表或设置空列表以允许其中任何方法
          "ServiceName": "api-product-service", //请求服务名称
          "LoadBalancerOptions": {
            "Type": "LeastConnection" //负载均衡算法:目前 Ocelot 有RoundRobin 和LeastConnection算法
          }
        }
      ],
      "GlobalConfiguration": {
        "BaseUrl": "http://localhost:5015", //进行标头查找和替换以及某些管理配置
        "ServiceDiscoveryProvider": {
          "Type": "Nacos"
        }
      },
      "Nacos": {
        "ServerAddresses": [ "http://127.0.0.1:8848" ], //服务地址
        "UserName": "nacos",  //用户名
        "Password": "nacos", //密码
        "ServiceName": "api-gateway", //服务名称
        //"Namespace": "",  //命名空间
        //"GroupName": "DEFAULT_GROUP" //组名,
        //"ClusterName": "DEFAULT", // 集群名称
        "ListenInterval": 1000,   //监听
        "RegisterEnabled": true, // 注册是否启动
        "InstanceEnabled": true   //实例是否启动
      },
      "Url": "http://*:5015"
    }
    
    1. 配置Ocelot服务:
    builder.Services.AddOcelot();
    

    Configure方法中配置请求管道并添加Ocelot中间件:

    app.UseOcelot().Wait();
    

    网关集成 Nacos

    要将Naocs集成Ocelot到中,您可以按照以下步骤进行操作:

    1. 下载Ocelot.Provider.Nacos 源码,导入Ocelot.Provider.Nacos 项目

      github:https://github.com/softlgl/Ocelot.Provider.Nacos

      修改Ocelot.Provider.Nacos 源码,在Ocelot 22版本中 IServiceDiscoveryProvider接口中的Get方法变成了GetAsync

      升级各Nuget包

      using System;
      using System.Linq;
      using System.Collections.Generic;
      using System.Threading.Tasks;
      using Ocelot.ServiceDiscovery.Providers;
      using Ocelot.Values;
      using Nacos.V2;
      using Microsoft.Extensions.Options;
      using Ocelot.Provider.Nacos.NacosClient.V2;
      using NacosConstants = Nacos.V2.Common.Constants;
      
      namespace Ocelot.Provider.Nacos
      {
          public class Nacos : IServiceDiscoveryProvider
          {
              private readonly INacosNamingService _client;
              private readonly string _serviceName;
              private readonly string _groupName;
              private readonly List<string> _clusters;
      
              public Nacos(string serviceName, INacosNamingService client, IOptions options)
              {
                  _serviceName = serviceName;
                  _client = client;
                  _groupName = string.IsNullOrWhiteSpace(options.Value.GroupName) ? 
                     NacosConstants.DEFAULT_GROUP : options.Value.GroupName;
                  _clusters = (string.IsNullOrWhiteSpace(options.Value.ClusterName) ? NacosConstants.DEFAULT_CLUSTER_NAME : options.Value.ClusterName).Split(",").ToList();
              }
      
              public async  Task> GetAsync()
              {
                  var services = new List();
      
                  var instances = await _client.GetAllInstances(_serviceName, _groupName, _clusters);
      
                  if (instances != null && instances.Any())
                  {
                      services.AddRange(instances.Select(i => new Service(i.InstanceId, new ServiceHostAndPort(i.Ip, i.Port), "", "", new List<string>())));
                  }
      
                  return await Task.FromResult(services);
              }
          }
      }
      
    2. 配置Ocelot:
      在Ocelot的配置中,您需要指定Nacos作为服务发现和配置的提供者。在Ocelot的配置文件(例如appsettings.json)中,添加以下内容:

    {
      "GlobalConfiguration": {
        "BaseUrl": "http://localhost:5015", //进行标头查找和替换以及某些管理配置
        "ServiceDiscoveryProvider": {
          "Type": "Nacos" //指定Nacos
        }
      },
      "Nacos": {
        "ServerAddresses": [ "http://127.0.0.1:8848" ], //服务地址
        "UserName": "nacos",  //用户名
        "Password": "nacos", //密码
        "ServiceName": "api-gateway", //服务名称
        //"Namespace": "",  //命名空间
        //"GroupName": "DEFAULT_GROUP" //组名,
        //"ClusterName": "DEFAULT", // 集群名称
        "ListenInterval": 1000,   //监听
        "RegisterEnabled": true, // 注册是否启动
        "InstanceEnabled": true   //实例是否启动
      }
    }
    
    1. 启动Ocelot:
      在您的ASP.NET Core应用程序中启动Ocelot。您可以在Startup.cs文件中添加以下代码:
    builder.Services.AddOcelot().AddNacosDiscovery("Nacos");
    

    下游配置 Nacos

    1. 安装必要的NuGet包:

      在Visual Studio中打开你的项目,通过NuGet包管理器安装Nacos.AspNetCore包。可以通过NuGet包管理器控制台运行以下命令来安装:

    Install-Package Nacos.AspNetCore
    
    1. 配置Nacos客户端:

      appsettings.json文件中添加Nacos服务的配置信息,例如服务器地址、端口、命名空间等信息。示例配置如下:

    {
      "Nacos": {
        "ServerAddresses": [ "http://127.0.0.1:8848" ],
        //命名空间GUID,public默认没有
        //"Namesapce": "",
        "UserName": "nacos",
        "Password": "nacos",
        // 配置中心
        //"Listeners": [
        //  {
        //    "Group": "dev",
        //    "DataId": "api-product-service",
        //    "Optional": false
        //  }
        //],
        // 服务发现
        "Ip": "localhost", // Nacos 注册时如果没有指定IP,那么就按照本机的IPv4 Address
        "Port": "5014", //端口
        "ServiceName": "api-product-service" // 服务名称
        //"GroupName": "",
        // 权重
        //"Weight": 100
      }
    }
    
    1. 配置依赖注入:

      如果你需要在你的应用程序中使用Nacos服务,可以在Startup.csConfigureServices方法中注册Nacos服务的依赖注入。示例如下:

    builder.Services.AddNacosAspNet(builder.Configuration,"Nacos");
    

    配置跨域(Cors)

    using Microsoft.AspNetCore.Builder;
    using Microsoft.Extensions.DependencyInjection;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MCode.Common.Extensions.Cors
    {
        public static class CorsServiceExtensions
        {
            private readonly static string PolicyName = "MCodeCors";
    
            /// 
            /// 添加跨域
            /// 
            /// 服务集合
            /// 
            public static IServiceCollection AddMCodeCors(this IServiceCollection services)
            {
                if (services == null) throw new ArgumentNullException(nameof(services));
                //origin microsoft.aspnetcore.cors      
                return services.AddCors(options =>
                {
                    options.AddPolicy(PolicyName, policy =>
                    {
                        policy.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
                    });
                });
            }
            /// 
            /// 使用跨域
            /// 
            /// 应用程序建造者
            /// 
            public static IApplicationBuilder UseMCodeCors(this IApplicationBuilder app)
            {
                return app.UseCors(PolicyName);
            }
        }
    }
    

    网关和微服务中配置Swagger

    SwaggerOptions

    using Microsoft.OpenApi.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MCode.Common.Extensions.Swagger
    {
        /// 
        /// Swagger配置
        /// 
        public class SwaggerOptions
        {
            /// 
            /// 服务名称
            /// 
            public string ServiceName { get; set; }
    
            /// 
            /// API信息
            /// 
            public OpenApiInfo ApiInfo { get; set; }
    
            /// 
            /// Xml注释文件
            /// 
            public string[] XmlCommentFiles { get; set; }
    
            /// 
            /// 构造函数
            /// 
            /// 服务名称
            /// API信息
            /// Xml注释文件
            public SwaggerOptions(string serviceName, OpenApiInfo apiInfo, string[] xmlCommentFiles = null)
            {
                ServiceName = !string.IsNullOrWhiteSpace(serviceName) ? serviceName : throw new ArgumentException("serviceName parameter not config.");
                ApiInfo = apiInfo;
                XmlCommentFiles = xmlCommentFiles;
            }
        }
    }
    

    SwaggerEndPoint

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MCode.Common.Extensions.Swagger
    {
        /// 
        /// Swagger终端
        /// 
        public class SwaggerEndPoint
        {
            /// 
            /// 名称
            /// 
            public string Name { get; set; }
            /// 
            /// 地址
            /// 
            public string Url { get; set; }
        }
    }
    

    OcelotSwaggerOptions

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MCode.Common.Extensions.Swagger
    {
        /// 
        /// 网关Swagger配置
        /// 
        public class OcelotSwaggerOptions
        {
            public List SwaggerEndPoints { get; set; }
        }
    }
    

    SwaggerServiceExtensions

    using Microsoft.AspNetCore.Builder;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.OpenApi.Models;
    
    namespace MCode.Common.Extensions.Swagger
    {
        /// 
        /// Swagger 服务扩展
        /// 
        public static class SwaggerServiceExtensions
        {
            /// 
            /// 添加 Swagger 服务
            /// 
            /// 
            /// 
            /// 
            public static IServiceCollection AddMCodeSwagger(this IServiceCollection services, SwaggerOptions swaggerOptions)
            {
                services.AddSingleton(swaggerOptions);
    
                SwaggerGenServiceCollectionExtensions.AddSwaggerGen(services, c =>
                {
                    c.SwaggerDoc(swaggerOptions.ServiceName, swaggerOptions.ApiInfo);
    
                    if (swaggerOptions.XmlCommentFiles != null)
                    {
                        foreach (string xmlCommentFile in swaggerOptions.XmlCommentFiles)
                        {
                            string str = Path.Combine(AppContext.BaseDirectory, xmlCommentFile);
                            if (File.Exists(str)) c.IncludeXmlComments(str, true);
                        }
                    }
    
                    SwaggerGenOptionsExtensions.CustomSchemaIds(c, x => x.FullName);
    
                    c.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme
                    {
                        Type = SecuritySchemeType.Http,
                        Scheme = "bearer",
                        BearerFormat = "JWT",
                        Description = "请输入 bearer 认证"
                    });
    
    
                    c.AddSecurityRequirement(new OpenApiSecurityRequirement
                                                  {
                                                      {
                                                          new OpenApiSecurityScheme
                                                          {
                                                              Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
                                                          },
                                                          new string[] {}
                                                      }
                                                  });
                });
    
                return services;
            }
    
            /// 
            /// 使用 Swagger UI
            /// 
            /// 
            /// 
            public static IApplicationBuilder UseMCodeSwagger(this IApplicationBuilder app)
            {
                string serviceName = app.ApplicationServices.GetRequiredService().ServiceName;
    
                SwaggerUIBuilderExtensions.UseSwaggerUI(SwaggerBuilderExtensions.UseSwagger(app), c =>
                {
                    c.SwaggerEndpoint("/swagger/" + serviceName + "/swagger.json", serviceName);
                });
                return app;
            }
    
    
            public static IServiceCollection AddMCodeOcelotSwagger(this IServiceCollection services, OcelotSwaggerOptions ocelotSwaggerOptions)
            {
                services.AddSingleton(ocelotSwaggerOptions);
                SwaggerGenServiceCollectionExtensions.AddSwaggerGen(services);
                return services;
            }
    
            public static IApplicationBuilder UseMCodeOcelotSwagger(this IApplicationBuilder app)
            {
                OcelotSwaggerOptions ocelotSwaggerOptions = app.ApplicationServices.GetService();
    
                if (ocelotSwaggerOptions == null || ocelotSwaggerOptions.SwaggerEndPoints == null)
                {
                    return app;
                }
    
                SwaggerUIBuilderExtensions.UseSwaggerUI(SwaggerBuilderExtensions.UseSwagger(app), c =>
                {
                    foreach (SwaggerEndPoint swaggerEndPoint in ocelotSwaggerOptions.SwaggerEndPoints)
                    {
                        c.SwaggerEndpoint(swaggerEndPoint.Url, swaggerEndPoint.Name);
                    }
                });
                return app;
            }
        }
    }
    

    效果

    image

    image

    其他文章:Asp .Net Core 系列:集成 Ocelot+Consul+Swagger+Cors实现网关、服务注册、服务发现

  • 相关阅读:
    mysql基础部分第一次复习(1-8章,到聚合函数)
    番外5:下载+安装+配置Linux
    后疫情时代,云计算如何为在线教育保驾护航
    分布式恢复 | 深入浅出MGR
    如何使用Git的代码托管
    美创科技列为IDC中国数据安全市场代表厂商
    15. SAP ABAP OData 服务里 EntityType 和 EntitySet 的区别
    Mysql8关键字
    面试题:ElasticSearch是什么?应用场景是什么?
    资深IC工程师的快速学习指南《Verilog语言知识学习快速基础学习》
  • 原文地址:https://www.cnblogs.com/vic-tory/p/17973528