解決微服務網關Ocelot使用AddStoreOcelotConfigurationInConsul后請求404問題


一個小插曲,最近研究 netcore 微服務網關,在使用AddStoreOcelotConfigurationInConsul將配置存到consul后,任何經過網關的請求都出現404,並且沒有任何有用的異常信息打印。這里先簡單講講這個問題是如何發生的,及如何解決。

之前在 ASP.NET Core 2 學習筆記(三)中間件 提到過大部分擴展的Middleware都會用一個靜態方法包裝,如:UseMvc()UseRewriter()等,而微服務網關Ocelot 包裝了兩個靜態的異步方法 UseOcelot

OcelotMiddlewareExtensions.cs

public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder)
{
    await builder.UseOcelot(new OcelotPipelineConfiguration());

    return builder;
}

public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
{
    var configuration = await CreateConfiguration(builder);
            
    CreateAdministrationArea(builder, configuration);

    if(UsingRafty(builder))
    {
        SetUpRafty(builder);
    }

    if (UsingEurekaServiceDiscoveryProvider(configuration))
    {
        builder.UseDiscoveryClient();
    }

    ConfigureDiagnosticListener(builder);

    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;
}  

為什么會封裝成異步的方法,可能是因為從底層一步一步寫上來的。但是我個人認為 UseOcelot 是完全可以封裝成同步方法,以避免誤用。

誤用情形,如下:

Startup.cs

public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    await app.UseOcelot();
}

這里將 Startup.Configure() 方法寫成了一個不規范的異步方法,其實是不被支持的。但是由於不規范,繞過了檢測。

規范寫法,如下

Startup.cs

public async Task Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    await app.UseOcelot();
}

檢測到Configure返回類型非Void,直接異常:

System.InvalidOperationException:“The 'Configure' method in the type 'OcelotConsul.ApiGateway.Core.Startup' must have a return type of 'Void'.”

這里可以參考aspnet/Hosting關於Configure 異步問題的討論:

aspnet/Hosting#27

aspnet/Hosting#29

aspnet/Hosting#373

aspnet/Hosting#1088

由於用到了不規范的異步方法,使得線程並沒有等待 UseOcelot 內部初始化完成就Host了起來,而造成了一些奇怪的異常,諸如:AddStoreOcelotConfigurationInConsul后請求404等問題。

解決方案就是用 Task.Wait() 方法 阻塞線程,等待 UseOcelot 執行完成,再Host。如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseOcelot().Wait();
}

 

也許 UseOcelot 封裝成同步方法會更好。已經給作者提了Issue


免責聲明!

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



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