解決微服務網關Ocelot使用AddStoreOcelotConfigurationInConsul後請求404問題 ASP.NET Core 2 學習筆記(三)中間件

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

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

OcelotMiddlewareExtensions.csgithub

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 是徹底能夠封裝成同步方法,以免誤用。app

誤用情形,以下:asp.net

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() 方法寫成了一個不規範的異步方法,實際上是不被支持的。可是因爲不規範,繞過了檢測。async

規範寫法,以下ide

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,直接異常:post

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

相關文章
相關標籤/搜索