.Net Core Api服務網關、服務注冊、服務發現簡單實現(非集群)


一、簡介

Ocelot:Ocelot是一個用.NET Core實現並且開源的API網關,它功能強大,包括了:路由、請求聚合、服務發現、認證、鑒權、限流熔斷、並內置了負載均衡器與Service Fabric、Butterfly Tracing集成,官方文檔:https://ocelot.readthedocs.io/en/latest/index.html

Consul:Consul本質上是一個Socket通信中間件。它主要實現了兩個功能,服務注冊與發現與自身的負載均衡的集群。官方文檔:https://www.consul.io/docs

二、API網關搭建

1、新建一個ASP.NET Core Web項目,選用空模板創建

2、安裝Ocelot相關包

3、在Startup中配置Ocelot

public void ConfigureServices(IServiceCollection services)
        {
            services.AddLogDashboard(opt =>
            {
                //授權登陸
                opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "123qwE*"));
                //請求追蹤
                opt.CustomLogModel<RequestTraceLogModel>();
            });

            services
                    .AddOcelot()
                    //服務發現
                    .AddConsul()
                    //緩存
                    .AddCacheManager(x =>
                    {
                        x.WithDictionaryHandle();
                    })
                    //服務質量控制
                    .AddPolly();

            services.AddCors(options =>
            {
                options.AddPolicy(_defaultCorsPolicyName,
                    builder => builder
                        .WithOrigins(
                            this.Configuration["App:CorsOrigins"]
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .ToArray()
                        )
                        .AllowAnyMethod()
                        .AllowAnyHeader());
            });
        }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

       app.UseCors(_defaultCorsPolicyName);
            app.UseLogDashboard();

            app.UseOcelot().Wait();
            
        }

網關IdentityServer4鑒權只需要加如下配置即可:

/**
             * IdentityServer4鑒權:
             * 1、安裝IdentityServer4.AccessTokenValidation NuGet包
             * 2、在Routes下加配置文件:
             * "AuthenticationOptions": {
                "AuthenticationProviderKey": "AuthKey",
                "AllowedScopes": []
              }
               3、注冊中間件:
                services.AddAuthentication()
                    .AddIdentityServerAuthentication("AuthKey", options =>
                    {
                        options.Authority = "http://localhost:7889";
                        options.RequireHttpsMetadata = false;
                        options.ApiName = "api";
                        options.SupportedTokens = SupportedTokens.Both;
                        options.ApiSecret = "secret";
                    });
             */

新建Ocelot.json文件,這里開啟了服務發現,后面講解服務發現配置,具體如下:

{
  "Routes": [
    {
      //服務名稱,開啟服務發現時需要配置
      "ServiceName": "web-api",
      //是否開啟服務發現
      "UseServiceDiscovery": true,
      //下游服務路由模板
      "DownstreamPathTemplate": "/{url}",
      //下游服務http schema
      "DownstreamScheme": "http",
      //下游服務的地址,如果使用LoadBalancer的話這里可以填多項
      //"DownstreamHostAndPorts": [
      //  {
      //    "Host": "192.168.1.205",
      //    "Port": 12000
      //  }
      //],
      "UpstreamPathTemplate": "/{url}",
      //上游請求http方法,可使用數組
      "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ],
      /**
       * 負載均衡的算法:
       * LeastConnection        – 跟蹤哪些服務正在處理請求,並將新請求發送到具有最少現有請求的服務。算法狀態沒有分布在Ocelot集群中。
       * RoundRobin             – 遍歷可用服務並發送請求。算法狀態沒有分布在Ocelot集群中。
       * NoLoadBalance          – 從配置或服務發現中獲取第一個可用服務
       * CookieStickySessions   -  使用cookie將所有請求粘貼到特定服務器
       */
      "LoadBalancerOptions": {
        "Type": "LeastConnection"
        //以下配置再設置了 CookieStickySessions 后需要開啟
        //用於粘性會話的cookie的密鑰
        //"Key": "ASP.NET_SessionId",
        //會話被阻塞的毫秒數
        //"Expiry": 1800000
      },
      //緩存
      "FileCacheOptions": {
        "TtlSeconds": 15
        //"Region": ""
      },
      //限流
      "RateLimitOptions": {
        //包含客戶端白名單的數組。這意味着該陣列中的客戶端將不受速率限制的影響
        "ClientWhitelist": [],
        //是否啟用端點速率限制
        "EnableRateLimiting": true,
        //指定限制所適用的期間,例如1s,5m,1h,1d等。如果在該期間內發出的請求超出限制所允許的數量,則需要等待PeriodTimespan過去,然后再發出其他請求
        "Period": "1s",
        //指定可以在一定秒數后重試
        "PeriodTimespan": 1,
        //指定客戶端在定義的時間內可以發出的最大請求數
        "Limit": 10
      },
      //熔斷
      "QoSOptions": {
        //允許多少個異常請求
        "ExceptionsAllowedBeforeBreaking": 3,
        //熔斷的時間,單位為毫秒
        "DurationOfBreak": 1000,
        //如果下游請求的處理時間超過多少則自如將請求設置為超時
        "TimeoutValue": 5000
      },
      "HttpHandlerOptions": {
        //是否開啟路由追蹤
        "UseTracing": false
      }
    }
  ],
  "GlobalConfiguration": {
    "RequestIdKey": "OcelotRequestId",
    //Consul服務發現
    "ServiceDiscoveryProvider": {
      "Scheme": "http",
      "Host": "192.168.1.205",
      "Port": 8500,
      "Type": "Consul"
    },
    //外部暴露的Url
    "BaseUrl": "http://localhost:17450",
    //限流擴展配置
    "RateLimitOptions": {
      //指定是否禁用X-Rate-Limit和Retry-After標頭
      "DisableRateLimitHeaders": false,
      //當請求過載被截斷時返回的消息
      "QuotaExceededMessage": "Oh,Oops!",
      //當請求過載被截斷時返回的http status
      "HttpStatusCode": 4421,
      //用來識別客戶端的請求頭,默認是 ClientId
      "ClientIdHeader": "ClientId"
    }
  }
}

appsettings.json配置如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "App": {
    "CorsOrigins": "http://192.168.1.205:4422"
  }
}

修改Program.cs,安裝NLog,因為網關啟用了日志面板

public class Program
    {
        public static void Main(string[] args)
        {
            var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
            try
            {
                logger.Debug("init main");
                CreateWebHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                //NLog: catch setup errors
                logger.Error(ex, "Stopped program because of exception");
                throw;
            }
            finally
            {
                // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
                NLog.LogManager.Shutdown();
            }
        }

        private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, builder) =>
                {
                    builder
                        .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
                        .AddJsonFile("appsettings.json", true, true)
                        .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
                        .AddJsonFile("Ocelot.json");
                })
                .UseStartup<Startup>()
                .ConfigureLogging(logging =>
                {
                    logging.ClearProviders();
                    logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                })
                .UseNLog();  // NLog: setup NLog for Dependency injection
    }

nlog.config配置如下:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="nlog-internal.log">

  <variable name="myvar" value="myvalue"/>

  <targets>
    <target xsi:type="file" name="File" fileName="App_Data/Logs/${shortdate}.log"
            layout="${longdate}||${level}||${logger}||${message}||${exception:format=ToString:innerFormat=ToString:maxInnerExceptionLevel=10:separator=\r\n} || ${aspnet-traceidentifier} ||end" />
</targets>

  <rules>
    <logger name="*" minlevel="Debug" writeTo="file" />
  </rules>
</nlog>

至此網關配置完成,接下來開始配置服務發現

三、服務注冊、發現

下載consul,使用以下命令啟動服務(這里不講解集群搭建方式):

consul agent -server -ui -bootstrap-expect=1 -data-dir=/tmp/consul -node=consul-1 -client=0.0.0.0 -bind=192.168.1.37 -datacenter=dc1

 瀏覽器打開UI界面如下:

 

 

 新建WebApi項目,同時安裝Consul包到你的項目

 添加服務發現擴展類ServiceDiscoveryExtensions.cs

public static class ServiceDiscoveryExtensions
    {
        public static void AddConsul(this IServiceCollection serviceCollection)
        {
            IConfiguration configuration;
            using (var serviceProvider = serviceCollection.BuildServiceProvider())
            {
                configuration = serviceProvider.GetService<IConfiguration>();
            }

            ConsulOptions option = configuration.GetSection("Consul").Get<ConsulOptions>();

            if (option.Enabled)
            {
                serviceCollection.AddSingleton<IConsulClient>(c => new ConsulClient(cfg =>
                {
                    //Consul主機地址
                    if (!string.IsNullOrEmpty(option.Host))
                    {
                        cfg.Address = new Uri(option.Host);
                    }
                }));
            }
        }

        public static void UseConsul(this IApplicationBuilder app, IApplicationLifetime lifetime)
        {
            using (var scope = app.ApplicationServices.CreateScope())
            {
                var configuration = scope.ServiceProvider.GetService<IConfiguration>();

                ConsulOptions option = configuration.GetSection("Consul").Get<ConsulOptions>();

                if (option.Enabled)
                {
                    Guid serviceId = Guid.NewGuid();
                    string consulServiceID = $"{ option.App.Name }:{ serviceId }";

                    var client = scope.ServiceProvider.GetService<IConsulClient>();

                    //健康檢查
                    var httpCheck = new AgentServiceCheck()
                    {
                        DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服務啟動多久后注冊
                        Interval = TimeSpan.FromSeconds(10),//間隔固定的時間訪問一次
                        HTTP = $"{ option.App.Scheme }://{ option.App.Host }:{ option.App.Port }/api/Health/Check",//健康檢查地址
                        Timeout = TimeSpan.FromSeconds(5)
                    };

                    var consulServiceRistration = new AgentServiceRegistration
                    {
                        ID = consulServiceID,
                        Name = option.App.Name,
                        Address = option.App.Host,//注意:這個地方不能帶Schema,否則網關會找不到服務;網關配置文件里面需要配置DownstreamScheme
                        Port = option.App.Port,
                        Tags = option.App.Tags,
                        Checks = new[] { httpCheck }
                    };

                    client.Agent.ServiceRegister(consulServiceRistration);

                    lifetime.ApplicationStopping.Register(() =>
                    {
                        client.Agent.ServiceDeregister(consulServiceRistration.ID).Wait();
                    });
                }
            }
        }
    }

配置實體ConsulOptions.cs:

/// <summary>
    /// Consul配置項目
    /// </summary>
    public class ConsulOptions
    {
        /// <summary>
        /// 是否啟用
        /// </summary>
        public bool Enabled { get; set; }
        /// <summary>
        /// Consul主機地址
        /// </summary>
        public string Host { get; set; }
        /// <summary>
        /// 應用信息
        /// </summary>
        public AppInfo App { get; set; }
    }

    public class AppInfo
    {
        /// <summary>
        /// 應用名稱
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 協議
        /// </summary>
        public string Scheme { get; set; }
        /// <summary>
        /// 應用主機地址
        /// </summary>
        public string Host { get; set; }
        /// <summary>
        /// 應用監聽端口
        /// </summary>
        public int Port { get; set; }
        /// <summary>
        /// 標簽
        /// </summary>
        public string[] Tags { get; set; }
    }

分別在ConfigureServices和Configure添加如下代碼:

services.AddConsul();

app.UseConsul(lifetime);

appsetting.json添加如下配置項目:

"Consul": {
    //是否啟用
    "Enabled": true,
    //Consul主機地址
    "Host": "http://192.168.1.37:8500",
    "App": {
      //應用名稱
      "Name": "web-api",
      //協議
      "Scheme": "http",
      //應用主機地址
      "Host": "localhost",
      //應用監聽端口
      "Port": 10002,
      //標簽
      "Tags": [
        "web-api-node-1"
      ]
    }
  }

至此代碼部分就完成了,接下來分別發布你的網關、API服務到服務器,訪問你的網關地址:

 可能會遇到的跨域問題解決:

            /**
             * 解決PUT和DELETE請求跨域問題(https://brockallen.com/2012/10/18/cors-iis-and-webdav/):
             * WebDAV 是超文本傳輸協議 (HTTP) 的一組擴展,為 Internet 上計算機之間的編輯和文件管理提供了標准.
             * 利用這個協議用戶可以通過Web進行遠程的基本文件操作,如拷貝、移動、刪除等。
             * 在IIS 7.0中,WebDAV是作為獨立擴展模塊,需要單獨進行下載,而IIS 7.5中將集成WebDAV,
             * 然而WebDav把Put,Delete給移除了,
             * 所以在IIS 7.5上部署的RESTful服務(WCF Data Service,WCF Rest Service,ASP.NET Web API,ASP.Net MVC)就悲劇了,
             * 當發送Put請求就會發生HTTP Error 405.0 – Method Not Allowed錯誤,解決方法也很簡單,在Web.config里面加入如下設置:
             *
             * <system.webServer>
                  <modules>
                    <remove name="WebDAVModule" />
                  </modules>
                  <handlers>
                    <remove name="WebDAV" />
                  </handlers>
                </system.webServer>
             */

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM