定制Ocelot来满足需求
這篇文章,我們將從Ocelot的中間件源碼分析,目前Ocelot已經(jīng)實(shí)現(xiàn)那些功能,還有那些功能在我們實(shí)際項(xiàng)目中暫時(shí)還未實(shí)現(xiàn),如果我們要使用這些功能,應(yīng)該如何改造等方面來(lái)說(shuō)明。
一、Ocelot源碼解讀
在使用一個(gè)組件前,最好我們要了解其中的一些原理,否則在使用過(guò)程中遇到問(wèn)題,也無(wú)從下手,今天我?guī)е蠹乙黄饋?lái)解讀下Ocelot源碼,并梳理出具體實(shí)現(xiàn)的原理和流程,便于我們根據(jù)需求擴(kuò)展應(yīng)用。
Ocelot源碼地址[https://github.com/ThreeMammals/Ocelot],
Ocelot文檔地址[https://ocelot.readthedocs.io/en/latest/]
查看.NETCORE相關(guān)中間件源碼,我們優(yōu)先找到入口方法,比如Ocelot中間件使用的是app.UseOcelot(),我們直接搜索UserOcelot,我們會(huì)找到OcelotMiddlewareExtensions方法,里面是Ocelot中間件實(shí)際運(yùn)行的方式和流程。
然后繼續(xù)順藤摸瓜,查看詳細(xì)的實(shí)現(xiàn),我們會(huì)發(fā)現(xiàn)如下代碼
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) ? ? ? ?{ ? //創(chuàng)建配置信息var configuration = await CreateConfiguration(builder); ? ? ? ? ? ?//監(jiān)聽(tīng)配置信息ConfigureDiagnosticListener(builder); ? ? ? ? ? ?//創(chuàng)建執(zhí)行管道return CreateOcelotPipeline(builder, pipelineConfiguration);}然后我們繼續(xù)跟蹤到創(chuàng)建管道方法,可以發(fā)現(xiàn)Ocelot的執(zhí)行流程已經(jīng)被找到,現(xiàn)在問(wèn)題變的簡(jiǎn)單了,直接查看
private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration){ ? ?var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices); ? ?//詳細(xì)創(chuàng)建的管道順序在此方法pipelineBuilder.BuildOcelotPipeline(pipelineConfiguration); ? ?var firstDelegate = pipelineBuilder.Build(); ? ?/*inject first delegate into first piece of asp.net middleware..maybe not like thisthen because we are updating the http context in ocelot it comes out correct forrest of asp.net..*/builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";builder.Use(async (context, task) =>{ ? ? ? ? ? ? ? ? ? ?var downstreamContext = new DownstreamContext(context); ? ? ? ? ? ? ? ? ? ?await firstDelegate.Invoke(downstreamContext);}); ? ?return builder; }管道創(chuàng)建流程及實(shí)現(xiàn),會(huì)不會(huì)感覺(jué)到摸到大動(dòng)脈了,核心的功能及原理基本找到了,那以后動(dòng)手術(shù)也就可以避開一些坑了,我們可以對(duì)著這個(gè)執(zhí)行順序,再查看詳細(xì)的源碼,按照這個(gè)執(zhí)行順序查看源碼,您就會(huì)發(fā)現(xiàn)整個(gè)思路非常清晰,每一步的實(shí)現(xiàn)一目了然。為了更直觀的介紹源碼的解讀方式,這里我們就拿我們后續(xù)要操刀的中間件來(lái)講解下中間件的具體實(shí)現(xiàn)。
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 globallybuilder.UseExceptionHandlerMiddleware(); ? ? ? ? ? ?// If the request is for websockets upgrade we fork into a different pipelinebuilder.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 responsebuilder.UseResponderMiddleware(); ? ? ? ? ? ?// Then we get the downstream route informationbuilder.UseDownstreamRouteFinderMiddleware(); ? ? ? ? ? ?// This security module, IP whitelist blacklist, extended security mechanismbuilder.UseSecurityMiddleware(); ? ? ? ? ? ?//Expand other branch pipesif (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 requestbuilder.UseDownstreamRequestInitialiser(); ? ? ? ? ? ?// We check whether the request is ratelimit, and if there is no continue processingbuilder.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 wantsif (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 authorisationbuilder.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 wantsif (pipelineConfiguration.AuthorisationMiddleware == null){//使用自定義認(rèn)證,移除默認(rèn)的認(rèn)證方式//builder.UseAuthorisationMiddleware();}else{ ? ? ? ? ? ? ? ?builder.Use(pipelineConfiguration.AuthorisationMiddleware);} ? ? ? ? ? ?// Now we can run the claims to headers transformation middlewarebuilder.UseClaimsToHeadersMiddleware(); ? ? ? ? ? ?// Allow the user to implement their own query string manipulation logicbuilder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware); ? ? ? ? ? ?// Now we can run any claims to query string transformation middlewarebuilder.UseClaimsToQueryStringMiddleware(); ? ? ? ? ? ?// Get the load balancer for this requestbuilder.UseLoadBalancingMiddleware(); ? ? ? ? ? ?// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be usedbuilder.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 repobuilder.UseHttpRequesterMiddleware(); ? ? ? ? ? ?return builder.Build();} ? ?private static void UseIfNotNull(this IOcelotPipelineBuilder builder,Func<DownstreamContext, Func<Task>, Task> middleware){ ? ? ? ? ? ?if (middleware != null){ ? ? ? ? ? ? ? ?builder.Use(middleware);}}}限流中間件實(shí)現(xiàn)解析
實(shí)現(xiàn)代碼如下builder.UseRateLimiting();,我們轉(zhuǎn)到定義,得到如下代碼,詳細(xì)的實(shí)現(xiàn)邏輯在ClientRateLimitMiddleware方法里,繼續(xù)轉(zhuǎn)定義到這個(gè)方法,我把方法里用到的內(nèi)容注釋了下。
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; ? ? ? ? ? ?// 校驗(yàn)是否啟用限流配置if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting){//未啟用直接進(jìn)入下一個(gè)中間件Logger.LogInformation($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamPathTemplate.Value}"); ? ? ? ? ? ? ? ?await _next.Invoke(context); ? ? ? ? ? ? ? ?return;} ? ? ? ? ? ?// 獲取配置的校驗(yàn)客戶端的方式var identity = SetIdentity(context.HttpContext, options); ? ? ? ? ? ?// 校驗(yàn)是否為白名單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){//限流數(shù)是否大于0// 獲取當(dāng)前客戶端請(qǐng)求情況,這里需要注意_processor是從哪里注入的,后續(xù)重var counter = _processor.ProcessRequest(identity, options); ? ? ? ? ? ? ? ?// 校驗(yàn)請(qǐng)求數(shù)是否大于限流數(shù)if (counter.TotalRequests > rule.Limit){ ? ? ? ? ? ? ? ? ? ?//獲取下次有效請(qǐng)求的時(shí)間,就是避免每次請(qǐng)求,都校驗(yàn)一次var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule); ? ? ? ? ? ? ? ? ? ?// 寫入日志LogBlockedRequest(context.HttpContext, identity, counter, rule, context.DownstreamReRoute); ? ? ? ? ? ? ? ? ? ?var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture); ? ? ? ? ? ? ? ? ? ?// 拋出超出限流異常并把下次可請(qǐng)求時(shí)間寫入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);} ? ? ? ? ? ?//進(jìn)入下一個(gè)中間件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;}}通過(guò)源碼解析,發(fā)現(xiàn)實(shí)現(xiàn)一個(gè)限流還是很簡(jiǎn)單的嗎!再進(jìn)一步解析,IRateLimitCounterHandler?ClientRateLimitProcessor里的相關(guān)接口又是怎么實(shí)現(xiàn)的呢?這時(shí)候我們就需要了解下.NETCORE 的運(yùn)行原理,其中ConfigureServices方法實(shí)現(xiàn)了依賴注入(DI)的配置。這時(shí)候我們看下Ocelot是在哪里進(jìn)行注入的呢?
services.AddOcelot()是不是印象深刻呢?原來(lái)所有的注入信息都寫在這里,那么問(wèn)題簡(jiǎn)單了,Ctrl+F查找AddOcelot方法,馬上就能定位到ServiceCollectionExtensions方法,然后再轉(zhuǎn)到定義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);} }又摸到大動(dòng)脈啦,現(xiàn)在問(wèn)題迎刃而解,原來(lái)所有的注入都寫在這里,從這里可以找下我們熟悉的幾個(gè)接口注入。
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 repositoryServices.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源碼解析就到這里了,其他的具體實(shí)現(xiàn)代碼就根據(jù)流程一個(gè)一個(gè)查看即可,這里就不詳細(xì)講解了,因?yàn)槲覀円呀?jīng)掌握整個(gè)Ocelot代碼的運(yùn)行原理和實(shí)現(xiàn)方式及流程,項(xiàng)目里其他的一大堆的代碼都是圍繞這個(gè)流程去一步一步實(shí)現(xiàn)的。
有沒(méi)有感覺(jué)添加一個(gè)中間件不是很復(fù)雜呢,是不是都躍躍欲試,準(zhǔn)備嘗試開發(fā)自己的自定義中間件啦,本篇就不介紹中間件的具體開發(fā)流程了,后續(xù)實(shí)戰(zhàn)中會(huì)包含部分項(xiàng)目中需要用到的中間件,到時(shí)候會(huì)詳細(xì)講解如何規(guī)劃和開發(fā)一個(gè)滿足自己項(xiàng)目需求的中間件。
二、結(jié)合項(xiàng)目梳理功能
在完整學(xué)習(xí)完Ocelot文檔和源碼后,我們基本掌握了Ocelot目前已經(jīng)實(shí)現(xiàn)的功能,再結(jié)合我們實(shí)際項(xiàng)目需求,我們梳理下還有哪些功能可能需要自己擴(kuò)展實(shí)現(xiàn)。
項(xiàng)目設(shè)計(jì)網(wǎng)關(guān)基本需求包括路由、認(rèn)證、授權(quán)、限流、緩存,仔細(xì)學(xué)習(xí)文檔和源碼后發(fā)現(xiàn)功能都已經(jīng)存在,那是不是我們就可以直接拿來(lái)使用呢?這時(shí)候我們需要拿出一些復(fù)雜業(yè)務(wù)場(chǎng)景來(lái)對(duì)號(hào)入座,看能否實(shí)現(xiàn)復(fù)雜場(chǎng)景的一些應(yīng)用。
1、授權(quán)
能否為每一個(gè)客戶端設(shè)置獨(dú)立的訪問(wèn)權(quán)限,如果客戶端A可以訪問(wèn)服務(wù)A、服務(wù)B,客戶端B只能訪問(wèn)服務(wù)A,從網(wǎng)關(guān)層面直接授權(quán),不滿足需求不路由到具體服務(wù)。從文檔和代碼分析后發(fā)現(xiàn)暫時(shí)未實(shí)現(xiàn)。
2、限流
能否為每一個(gè)客戶端設(shè)置不能限流規(guī)則,例如客戶端A為我們內(nèi)容應(yīng)用,我希望對(duì)服務(wù)A不啟用限流,客戶端B為第三方接入應(yīng)用,我需要B訪問(wèn)服務(wù)A訪問(wèn)進(jìn)行單獨(dú)限流(30次/分鐘),看能否通過(guò)配置實(shí)現(xiàn)自定義限流。從文檔和代碼分析后發(fā)現(xiàn)暫時(shí)未實(shí)現(xiàn)。
3、緩存
通過(guò)代碼發(fā)現(xiàn)目前緩存實(shí)現(xiàn)的只是Dictionary方式實(shí)現(xiàn)的緩存,不能實(shí)現(xiàn)分布式結(jié)構(gòu)的應(yīng)用。
通過(guò)分析我們發(fā)現(xiàn)列舉的5個(gè)基本需求,盡然有3個(gè)在我們實(shí)際項(xiàng)目應(yīng)用中可能會(huì)存在問(wèn)題,如果不解決這些問(wèn)題,很難直接拿這個(gè)完美的網(wǎng)關(guān)項(xiàng)目應(yīng)用到正式項(xiàng)目,所以我們到通過(guò)擴(kuò)展Ocelot方法來(lái)實(shí)現(xiàn)我們的目的。
如何擴(kuò)展呢
為了滿足我們項(xiàng)目應(yīng)用的需要,我們需要為每一個(gè)路由進(jìn)行單獨(dú)設(shè)置,如果還采用配置文件的方式,肯定無(wú)法滿足需求,且后續(xù)網(wǎng)關(guān)動(dòng)態(tài)增加路由、授權(quán)、限流等無(wú)法控制,所以我們需要把網(wǎng)關(guān)配置信息從配置文件中移到數(shù)據(jù)庫(kù)中,由數(shù)據(jù)庫(kù)中的路由表、限流表、授權(quán)表等方式記錄當(dāng)前網(wǎng)關(guān)的應(yīng)用,且后續(xù)擴(kuò)展直接在數(shù)據(jù)庫(kù)中增加或減少相關(guān)配置,然后動(dòng)態(tài)更新網(wǎng)關(guān)配置實(shí)現(xiàn)網(wǎng)關(guān)的高可用。
想一想是不是有點(diǎn)小激動(dòng),原來(lái)只要稍微改造下寶駿瞬間變寶馬,那接下來(lái)的課程就是網(wǎng)關(guān)改造之旅,我會(huì)從設(shè)計(jì)、思想、編碼等方面講解下如何實(shí)現(xiàn)我們的第一輛寶馬。
本系列文章我也是邊想邊寫邊實(shí)現(xiàn),如果發(fā)現(xiàn)中間有任何描述或?qū)崿F(xiàn)不當(dāng)?shù)牡胤?#xff0c;也請(qǐng)各位大神批評(píng)指正,我會(huì)第一時(shí)間整理并修正,避免讓后續(xù)學(xué)習(xí)的人走彎路。
相關(guān)文章:
AspNetCore中使用Ocelot之 IdentityServer4
Ocelot-基于.NET Core的開源網(wǎng)關(guān)實(shí)現(xiàn)
.NET Core微服務(wù)之基于Ocelot+IdentityServer實(shí)現(xiàn)統(tǒng)一驗(yàn)證與授權(quán)
Swagger如何訪問(wèn)Ocelot中帶權(quán)限驗(yàn)證的API
Ocelot.JwtAuthorize:一個(gè)基于網(wǎng)關(guān)的Jwt驗(yàn)證包
.NET Core微服務(wù)之基于Ocelot實(shí)現(xiàn)API網(wǎng)關(guān)服務(wù)
.NET Core微服務(wù)之基于Ocelot實(shí)現(xiàn)API網(wǎng)關(guān)服務(wù)(續(xù))
.NET微服務(wù)體系結(jié)構(gòu)中為什么使用Ocelot實(shí)現(xiàn)API網(wǎng)關(guān)
Ocelot簡(jiǎn)易教程(一)之Ocelot是什么
Ocelot簡(jiǎn)易教程(二)之快速開始1
Ocelot簡(jiǎn)易教程(二)之快速開始2
Ocelot簡(jiǎn)易教程(三)之主要特性及路由詳解
Ocelot簡(jiǎn)易教程(四)之請(qǐng)求聚合以及服務(wù)發(fā)現(xiàn)
Ocelot簡(jiǎn)易教程(五)之集成IdentityServer認(rèn)證以及授權(quán)
Ocelot簡(jiǎn)易教程(六)之重寫配置文件存儲(chǔ)方式并優(yōu)化響應(yīng)數(shù)據(jù)
Ocelot簡(jiǎn)易教程(七)之配置文件數(shù)據(jù)庫(kù)存儲(chǔ)插件源碼解析
ASP.NET Core中Ocelot的使用:API網(wǎng)關(guān)的應(yīng)用
ASP.NET Core中Ocelot的使用:基于Spring Cloud Netflix Eureka的動(dòng)態(tài)路由
ASP.NET Core中Ocelot的使用:基于服務(wù)發(fā)現(xiàn)的負(fù)載均衡
原文地址: https://www.cnblogs.com/jackcao/p/9937213.html
.NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的定制Ocelot来满足需求的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 服务器win2008 R2 x64 部署
- 下一篇: 【.NET Core项目实战-统一认证平