zoukankan      html  css  js  c++  java
  • 解决微服务网关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

  • 相关阅读:
    Notes of Daily Scrum Meeting(12.18)
    Notes of Daily Scrum Meeting(12.17)
    Notes of Daily Scrum Meeting(12.16)
    Notes of Daily Scrum Meeting(12.8)
    Notes of Daily Scrum Meeting(12.5)
    Notes of Daily Scrum Meeting(12.3)
    Notes of Daily Scrum Meeting(11.12)
    Linux中profile、bashrc、bash_profile之间的区别和联系
    Linux GCC编译
    mysql 5.7.16 远程连接
  • 原文地址:https://www.cnblogs.com/snaildev/p/9111652.html
Copyright © 2011-2022 走看看