一個小插曲,最近研究 netcore 微服務網關,在使用AddStoreOcelotConfigurationInConsul將配置存到consul後,任何通過網關的請求都出現404,而且沒有任何有用的異常信息打印。這裏先簡單講講這個問題是如何發生的,及如何解決。html
以前在 ASP.NET Core 2 學習筆記(三)中間件 提到過大部分擴展的Middleware都會用一個靜態方法包裝,如:UseMvc()
、UseRewriter()
等,而微服務網關Ocelot 包裝了兩個靜態的異步方法 UseOcelot。git
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 異步問題的討論:
因爲用到了不規範的異步方法,使得線程並無等待 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