【.NET Core項目實戰-統一認證平臺】第二章網關篇-重構Ocelot來滿足需求

来源:https://www.cnblogs.com/jackcao/archive/2018/11/09/9937213.html
-Advertisement-
Play Games

" 【.NET Core項目實戰 統一認證平臺】開篇及目錄索引 " 這篇文章,我們將從Ocelot的中間件源碼分析,目前Ocelot已經實現那些功能,還有那些功能在我們實際項目中暫時還未實現,如果我們要使用這些功能,應該如何改造等方面來說明。 一、Ocelot源碼解讀 在使用一個組件前,最好我們要了 ...


【.NET Core項目實戰-統一認證平臺】開篇及目錄索引

這篇文章,我們將從Ocelot的中間件源碼分析,目前Ocelot已經實現那些功能,還有那些功能在我們實際項目中暫時還未實現,如果我們要使用這些功能,應該如何改造等方面來說明。

一、Ocelot源碼解讀

在使用一個組件前,最好我們要瞭解其中的一些原理,否則在使用過程中遇到問題,也無從下手,今天我帶著大家一起來解讀下Ocelot源碼,並梳理出具體實現的原理和流程,便於我們根據需求擴展應用。
Ocelot源碼地址[https://github.com/ThreeMammals/Ocelot],
Ocelot文檔地址[https://ocelot.readthedocs.io/en/latest/]

查看.NETCORE相關中間件源碼,我們優先找到入口方法,比如Ocelot中間件使用的是app.UseOcelot(),我們直接搜索UserOcelot,我們會找到OcelotMiddlewareExtensions方法,裡面是Ocelot中間件實際運行的方式和流程。

然後繼續順藤摸瓜,查看詳細的實現,我們會發現如下代碼

public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
        {   
            //創建配置信息
            var configuration = await CreateConfiguration(builder);
            //監聽配置信息
            ConfigureDiagnosticListener(builder);
            //創建執行管道
            return CreateOcelotPipeline(builder, pipelineConfiguration);
        }

然後我們繼續跟蹤到創建管道方法,可以發現Ocelot的執行流程已經被找到,現在問題變的簡單了,直接查看

private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
{
    var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices);
    //詳細創建的管道順序在此方法
    pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration);

    var firstDelegate = pipelineBuilder.Build();

    /*
            inject first delegate into first piece of asp.net middleware..maybe not like this
            then because we are updating the http context in ocelot it comes out correct for
            rest of asp.net..
            */

    builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";

    builder.Use(async (context, task) =>
                {
                    var downstreamContext = new DownstreamContext(context);
                    await firstDelegate.Invoke(downstreamContext);
                });

    return builder;
}

管道創建流程及實現,會不會感覺到摸到大動脈了,核心的功能及原理基本找到了,那以後動手術也就可以避開一些坑了,我們可以對著這個執行順序,再查看詳細的源碼,按照這個執行順序查看源碼,您就會發現整個思路非常清晰,每一步的實現一目瞭然。為了更直觀的介紹源碼的解讀方式,這裡我們就拿我們後續要操刀的中間件來講解下中間件的具體實現。

public static class OcelotPipelineExtensions
    {
        public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuilder builder,
            OcelotPipelineConfiguration pipelineConfiguration)
        {
            // This is registered to catch any global exceptions that are not handled
            // It also sets the Request Id if anything is set globally
            builder.UseExceptionHandlerMiddleware();

            // If the request is for websockets upgrade we fork into a different pipeline
            builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest,
                app =>
                {
                    app.UseDownstreamRouteFinderMiddleware();
                    app.UseDownstreamRequestInitialiser();
                    app.UseLoadBalancingMiddleware();
                    app.UseDownstreamUrlCreatorMiddleware();
                    app.UseWebSocketsProxyMiddleware();
                });

            // Allow the user to respond with absolutely anything they want.
            builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);

            // This is registered first so it can catch any errors and issue an appropriate response
            builder.UseResponderMiddleware();

            // Then we get the downstream route information
            builder.UseDownstreamRouteFinderMiddleware();

            // This security module, IP whitelist blacklist, extended security mechanism
            builder.UseSecurityMiddleware();

            //Expand other branch pipes
            if (pipelineConfiguration.MapWhenOcelotPipeline != null)
            {
                foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline)
                {
                    builder.MapWhen(pipeline);
                }
            }

            // Now we have the ds route we can transform headers and stuff?
            builder.UseHttpHeadersTransformationMiddleware();

            // Initialises downstream request
            builder.UseDownstreamRequestInitialiser();

            // We check whether the request is ratelimit, and if there is no continue processing
            builder.UseRateLimiting();

            // This adds or updates the request id (initally we try and set this based on global config in the error handling middleware)
            // If anything was set at global level and we have a different setting at re route level the global stuff will be overwritten
            // This means you can get a scenario where you have a different request id from the first piece of middleware to the request id middleware.
            builder.UseRequestIdMiddleware();

            // Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
            builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware);

            // Now we know where the client is going to go we can authenticate them.
            // We allow the ocelot middleware to be overriden by whatever the
            // user wants
            if (pipelineConfiguration.AuthenticationMiddleware == null)
            {
                builder.UseAuthenticationMiddleware();
            }
            else
            {
                builder.Use(pipelineConfiguration.AuthenticationMiddleware);
            }

            // The next thing we do is look at any claims transforms in case this is important for authorisation
            builder.UseClaimsToClaimsMiddleware();

            // Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
            builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);

            // Now we have authenticated and done any claims transformation we 
            // can authorise the request
            // We allow the ocelot middleware to be overriden by whatever the
            // user wants
            if (pipelineConfiguration.AuthorisationMiddleware == null)
            {//使用自定義認證,移除預設的認證方式
                //builder.UseAuthorisationMiddleware();
            }
            else
            {
                builder.Use(pipelineConfiguration.AuthorisationMiddleware);
            }

            // Now we can run the claims to headers transformation middleware
            builder.UseClaimsToHeadersMiddleware();

            // Allow the user to implement their own query string manipulation logic
            builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);

            // Now we can run any claims to query string transformation middleware
            builder.UseClaimsToQueryStringMiddleware();

            // Get the load balancer for this request
            builder.UseLoadBalancingMiddleware();

            // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
            builder.UseDownstreamUrlCreatorMiddleware();

            // Not sure if this is the best place for this but we use the downstream url 
            // as the basis for our cache key.
            builder.UseOutputCacheMiddleware();

            //We fire off the request and set the response on the scoped data repo
            builder.UseHttpRequesterMiddleware();

            return builder.Build();
        }
    private static void UseIfNotNull(this IOcelotPipelineBuilder builder,
            Func<DownstreamContext, Func<Task>, Task> middleware)
        {
            if (middleware != null)
            {
                builder.Use(middleware);
            }
        }
    }

限流中間件實現解析

實現代碼如下builder.UseRateLimiting();,我們轉到定義,得到如下代碼,詳細的實現邏輯在ClientRateLimitMiddleware方法里,繼續轉定義到這個方法,我把方法里用到的內容註釋了下。

public static class RateLimitMiddlewareExtensions
{
    public static IOcelotPipelineBuilder UseRateLimiting(this IOcelotPipelineBuilder builder)
    {
        return builder.UseMiddleware<ClientRateLimitMiddleware>();
    }
}

public class ClientRateLimitMiddleware : OcelotMiddleware
{
        private readonly OcelotRequestDelegate _next;
        private readonly IRateLimitCounterHandler _counterHandler;
        private readonly ClientRateLimitProcessor _processor;

        public ClientRateLimitMiddleware(OcelotRequestDelegate next,
            IOcelotLoggerFactory loggerFactory,
            IRateLimitCounterHandler counterHandler)
                :base(loggerFactory.CreateLogger<ClientRateLimitMiddleware>())
        {
            _next = next;
            _counterHandler = counterHandler;
            _processor = new ClientRateLimitProcessor(counterHandler);
        }
        //熟悉的Tnvoke方法,所有的邏輯都在此方法里。
        public async Task Invoke(DownstreamContext context)
        {
            var options = context.DownstreamReRoute.RateLimitOptions;

            // 校驗是否啟用限流配置
            if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting)
            {//未啟用直接進入下一個中間件
                Logger.LogInformation($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamPathTemplate.Value}");
                await _next.Invoke(context);
                return;
            }

            // 獲取配置的校驗客戶端的方式
            var identity = SetIdentity(context.HttpContext, options);

            // 校驗是否為白名單
            if (IsWhitelisted(identity, options))
            {//白名單直接放行
                Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} is white listed from rate limiting");
                await _next.Invoke(context);
                return;
            }

            var rule = options.RateLimitRule;
            if (rule.Limit > 0)
            {//限流數是否大於0
                // 獲取當前客戶端請求情況,這裡需要註意_processor是從哪裡註入的,後續重
                var counter = _processor.ProcessRequest(identity, options);

                // 校驗請求數是否大於限流數
                if (counter.TotalRequests > rule.Limit)
                {
                    //獲取下次有效請求的時間,就是避免每次請求,都校驗一次
                    var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule);

                    // 寫入日誌
                    LogBlockedRequest(context.HttpContext, identity, counter, rule, context.DownstreamReRoute);

                    var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);

                    // 拋出超出限流異常並把下次可請求時間寫入header里。
                    await ReturnQuotaExceededResponse(context.HttpContext, options, retrystring);

                    return;
                }
            }

            //如果啟用了限流頭部
            if (!options.DisableRateLimitHeaders)
            {
                var headers = _processor.GetRateLimitHeaders(context.HttpContext, identity, options);
                context.HttpContext.Response.OnStarting(SetRateLimitHeaders, state: headers);
            }
            //進入下一個中間件
            await _next.Invoke(context);
        }

        public virtual ClientRequestIdentity SetIdentity(HttpContext httpContext, RateLimitOptions option)
        {
            var clientId = "client";
            if (httpContext.Request.Headers.Keys.Contains(option.ClientIdHeader))
            {
                clientId = httpContext.Request.Headers[option.ClientIdHeader].First();
            }

            return new ClientRequestIdentity(
                clientId,
                httpContext.Request.Path.ToString().ToLowerInvariant(),
                httpContext.Request.Method.ToLowerInvariant()
                );
        }

        public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option)
        {
            if (option.ClientWhitelist.Contains(requestIdentity.ClientId))
            {
                return true;
            }

            return false;
        }

        public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamReRoute downstreamReRoute)
        {
            Logger.LogInformation(
                $"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { downstreamReRoute.UpstreamPathTemplate.OriginalValue }, TraceIdentifier {httpContext.TraceIdentifier}.");
        }

        public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
        {
            var message = string.IsNullOrEmpty(option.QuotaExceededMessage) ? $"API calls quota exceeded! maximum admitted {option.RateLimitRule.Limit} per {option.RateLimitRule.Period}." : option.QuotaExceededMessage;

            if (!option.DisableRateLimitHeaders)
            {
                httpContext.Response.Headers["Retry-After"] = retryAfter;
            }

            httpContext.Response.StatusCode = option.HttpStatusCode;
            return httpContext.Response.WriteAsync(message);
        }

        private Task SetRateLimitHeaders(object rateLimitHeaders)
        {
            var headers = (RateLimitHeaders)rateLimitHeaders;

            headers.Context.Response.Headers["X-Rate-Limit-Limit"] = headers.Limit;
            headers.Context.Response.Headers["X-Rate-Limit-Remaining"] = headers.Remaining;
            headers.Context.Response.Headers["X-Rate-Limit-Reset"] = headers.Reset;

            return Task.CompletedTask;
        }
   }

通過源碼解析,發現實現一個限流還是很簡單的嗎!再進一步解析,IRateLimitCounterHandler ClientRateLimitProcessor里的相關介面又是怎麼實現的呢?這時候我們就需要瞭解下.NETCORE 的運行原理,其中ConfigureServices方法實現了依賴註入(DI)的配置。這時候我們看下Ocelot是在哪裡進行註入的呢?

services.AddOcelot()是不是印象深刻呢?原來所有的註入信息都寫在這裡,那麼問題簡單了,Ctrl+F查找AddOcelot方法,馬上就能定位到ServiceCollectionExtensions方法,然後再轉到定義OcelotBuilder

public static class ServiceCollectionExtensions
{
    public static IOcelotBuilder AddOcelot(this IServiceCollection services)
    {
        var service = services.First(x => x.ServiceType == typeof(IConfiguration));
        var configuration = (IConfiguration)service.ImplementationInstance;
        return new OcelotBuilder(services, configuration);
    }

    public static IOcelotBuilder AddOcelot(this IServiceCollection services, IConfiguration configuration)
    {
        return new OcelotBuilder(services, configuration);
    }
}

又摸到大動脈啦,現在問題迎刃而解,原來所有的註入都寫在這裡,從這裡可以找下我們熟悉的幾個介面註入。

public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
{
    Configuration = configurationRoot;
    Services = services;
    Services.Configure<FileConfiguration>(configurationRoot);

    Services.TryAddSingleton<IOcelotCache<FileConfiguration>, InMemoryCache<FileConfiguration>>();
    Services.TryAddSingleton<IOcelotCache<CachedResponse>, InMemoryCache<CachedResponse>>();
    Services.TryAddSingleton<IHttpResponseHeaderReplacer, HttpResponseHeaderReplacer>();
    Services.TryAddSingleton<IHttpContextRequestHeaderReplacer, HttpContextRequestHeaderReplacer>();
    Services.TryAddSingleton<IHeaderFindAndReplaceCreator, HeaderFindAndReplaceCreator>();
    Services.TryAddSingleton<IInternalConfigurationCreator, FileInternalConfigurationCreator>();
    Services.TryAddSingleton<IInternalConfigurationRepository, InMemoryInternalConfigurationRepository>();
    Services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
    Services.TryAddSingleton<HostAndPortValidator>();
    Services.TryAddSingleton<IReRoutesCreator, ReRoutesCreator>();
    Services.TryAddSingleton<IAggregatesCreator, AggregatesCreator>();
    Services.TryAddSingleton<IReRouteKeyCreator, ReRouteKeyCreator>();
    Services.TryAddSingleton<IConfigurationCreator, ConfigurationCreator>();
    Services.TryAddSingleton<IDynamicsCreator, DynamicsCreator>();
    Services.TryAddSingleton<ILoadBalancerOptionsCreator, LoadBalancerOptionsCreator>();
    Services.TryAddSingleton<ReRouteFluentValidator>();
    Services.TryAddSingleton<FileGlobalConfigurationFluentValidator>();
    Services.TryAddSingleton<FileQoSOptionsFluentValidator>();
    Services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
    Services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
    Services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
    Services.TryAddSingleton<IRequestIdKeyCreator, RequestIdKeyCreator>();
    Services.TryAddSingleton<IServiceProviderConfigurationCreator,ServiceProviderConfigurationCreator>();
    Services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
    Services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>();
    Services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
    Services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
    Services.TryAddSingleton<IRegionCreator, RegionCreator>();
    Services.TryAddSingleton<IFileConfigurationRepository, DiskFileConfigurationRepository>();
    Services.TryAddSingleton<IFileConfigurationSetter, FileAndInternalConfigurationSetter>();
    Services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
    Services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
    Services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
    Services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
    Services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
    Services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
    Services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
    Services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
    Services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
    Services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
    Services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
    Services.TryAddSingleton<IClaimsParser, ClaimsParser>();
    Services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
    Services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
    Services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
    Services.TryAddSingleton<IDownstreamRouteProvider, DownstreamRouteFinder>();
    Services.TryAddSingleton<IDownstreamRouteProvider, DownstreamRouteCreator>();
    Services.TryAddSingleton<IDownstreamRouteProviderFactory, DownstreamRouteProviderFactory>();
    Services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
    Services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
    Services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
    Services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
    Services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
    Services.TryAddSingleton<IRequestMapper, RequestMapper>();
    Services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
    Services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
    Services.TryAddSingleton<IDelegatingHandlerHandlerFactory, DelegatingHandlerHandlerFactory>();
    Services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();

    // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
    // could maybe use a scoped data repository
    Services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    Services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>();
    Services.AddMemoryCache();
    Services.TryAddSingleton<OcelotDiagnosticListener>();
    Services.TryAddSingleton<IMultiplexer, Multiplexer>();
    Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
    Services.TryAddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
    Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
    Services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
    Services.TryAddSingleton<IPlaceholders, Placeholders>();
    Services.TryAddSingleton<IResponseAggregatorFactory, InMemoryResponseAggregatorFactory>();
    Services.TryAddSingleton<IDefinedAggregatorProvider, ServiceLocatorDefinedAggregatorProvider>();
    Services.TryAddSingleton<IDownstreamRequestCreator, DownstreamRequestCreator>();
    Services.TryAddSingleton<IFrameworkDescription, FrameworkDescription>();
    Services.TryAddSingleton<IQoSFactory, QoSFactory>();
    Services.TryAddSingleton<IExceptionToErrorMapper, HttpExeptionToErrorMapper>();

    //add security 
    this.AddSecurity();

    //add asp.net services..
    var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;

    Services.AddMvcCore()
        .AddApplicationPart(assembly)
        .AddControllersAsServices()
        .AddAuthorization()
        .AddJsonFormatters();

    Services.AddLogging();
    Services.AddMiddlewareAnalysis();
    Services.AddWebEncoders();
}

至此Ocelot源碼解析就到這裡了,其他的具體實現代碼就根據流程一個一個查看即可,這裡就不詳細講解了,因為我們已經掌握整個Ocelot代碼的運行原理和實現方式及流程,項目里其他的一大堆的代碼都是圍繞這個流程去一步一步實現的。

有沒有感覺添加一個中間件不是很複雜呢,是不是都躍躍欲試,準備嘗試開發自己的自定義中間件啦,本篇就不介紹中間件的具體開發流程了,後續實戰中會包含部分項目中需要用到的中間件,到時候會詳細講解如何規劃和開發一個滿足自己項目需求的中間件。

二、結合項目梳理功能

在完整學習完Ocelot文檔和源碼後,我們基本掌握了Ocelot目前已經實現的功能,再結合我們實際項目需求,我們梳理下還有哪些功能可能需要自己擴展實現。

項目設計網關基本需求包括路由、認證、授權、限流、緩存,仔細學習文檔和源碼後發現功能都已經存在,那是不是我們就可以直接拿來使用呢?這時候我們需要拿出一些複雜業務場景來對號入座,看能否實現複雜場景的一些應用。

1、授權

能否為每一個客戶端設置獨立的訪問許可權,如果客戶端A可以訪問服務A、服務B,客戶端B只能訪問服務A,從網關層面直接授權,不滿足需求不路由到具體服務。從文檔和代碼分析後發現暫時未實現。

2、限流

能否為每一個客戶端設置不能限流規則,例如客戶端A為我們內容應用,我希望對服務A不啟用限流,客戶端B為第三方接入應用,我需要B訪問服務A訪問進行單獨限流(30次/分鐘),看能否通過配置實現自定義限流。從文檔和代碼分析後發現暫時未實現。

3、緩存

通過代碼發現目前緩存實現的只是Dictionary方式實現的緩存,不能實現分散式結構的應用。

通過分析我們發現列舉的5個基本需求,盡然有3個在我們實際項目應用中可能會存在問題,如果不解決這些問題,很難直接拿這個完美的網關項目應用到正式項目,所以我們到通過擴展Ocelot方法來實現我們的目的。

如何擴展呢

為了滿足我們項目應用的需要,我們需要為每一個路由進行單獨設置,如果還採用配置文件的方式,肯定無法滿足需求,且後續網關動態增加路由、授權、限流等無法控制,所以我們需要把網關配置信息從配置文件中移到資料庫中,由資料庫中的路由表、限流表、授權表等方式記錄當前網關的應用,且後續擴展直接在資料庫中增加或減少相關配置,然後動態更新網關配置實現網關的高可用。

想一想是不是有點小激動,原來只要稍微改造下寶駿瞬間變寶馬,那接下來的課程就是網關改造之旅,我會從設計、思想、編碼等方面講解下如何實現我們的第一輛寶馬。

本系列文章我也是邊想邊寫邊實現,如果發現中間有任何描述或實現不當的地方,也請各位大神批評指正,我會第一時間整理並修正,避免讓後續學習的人走彎路。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基礎很重要,基礎很重要,基礎很重要。重要的事情說三遍,。 程式設計中的控制語句主要有三種:順序、分支和迴圈。我們每天寫的代碼,除了業務相關,裡面會包含大量的控制語句。但是控制語句的基本使用,是否有些坑我們需要避免,以及一些常見的規範我們是否遵守了呢,我們來簡單複習一下。 控制語句包含: 1、分支語句 ...
  • 1 class Base: 2 pass 3 4 class Foo(Base): 5 pass 6 7 class Bar(Foo): 8 pass 9 10 print(issubclass(Bar,Foo)) # True 11 print(issubclass(Foo,Bar)) # Fal ...
  • 相信大家在寫項目的時候遇到過,資料庫里的時間戳類型(時間類型),後臺獲取了,返回前臺,但是前臺的格式不是你想要的格式。例如: 這樣是有毫秒,我在網上找了個簡單又方便的方法: 1、導入JSTL的jar包 2、在jsp頁面加入 3、本來我們輸出是 現在我們輸出是 結果: 最後,如果我們要的格式只要年月日 ...
  • 函數屬性 python中的函數是一種對象,它有屬於對象的屬性。除此之外,函數還可以自定義自己的屬性。註意,屬性是和對象相關的,和作用域無關。 自定義屬性 自定義函數自己的屬性方式很簡單。假設函數名稱為myfunc,那麼為這個函數添加一個屬性var1: 那麼這個屬性var1就像是全局變數一樣被訪問、修 ...
  • 1.利用Filter來過濾的時候大都是Http請求和Http響應,在doFilter()方法中,參數類是ServletRequest和ServletResponse ,使用的時候一般需要強制轉換為HttpServletRequest和HttpServletResponse 。針對這個問題,可以仿照G ...
  • ########模板層######## 模板層其實就是templates文件夾里的html文件 其實這裡的每個html不是真正意義的上html代碼,只有經過模板渲染過後才算的上真正的html頁面。 一、模板語言(變數,深度查詢,過濾器,標簽) 1、變數 在django模板里通過{{ name }} ... ...
  • 一、三個內置函數 1、issubclass(a, b) 判斷a類是否是b類的子類 1 class Foo: 2 pass 3 4 class Zi(Foo): 5 pass 6 7 class Sun(Zi): 8 pass 9 print(issubclass(Zi,Foo)) #True 10 ...
  • 元組 列表非常適合用於存儲在程式運行期間可能變化的數據集。列表是可以修改的,而不可變的列表被稱為元組。元組使用圓括弧來標識。定義元組後可以使用索引來訪問元素。 例如: 遍歷元組中的所有值 像列表一樣,可以使用for迴圈來遍歷元組中的所有值 修改元組變數 雖然不能修改元組的元素,但是可以給元組的變數賦 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...