【问题标题】:Is there anyway to add services to the service provider at runtime? Or rebuild it? On .NET 5.0 or .NET core 3+无论如何在运行时向服务提供者添加服务?还是重建它?在 .NET 5.0 或 .NET core 3+ 上
【发布时间】:2021-05-27 16:34:58
【问题描述】:

我有一个关于微服务架构设计的多租户应用程序。

我想注入 X 个服务,具体取决于运行的租户数量。

 public void ConfigureServices(IServiceCollection services)
    {
        // ... OTHER DI

        services.AddHttpClient("TenantsService")
            .AddTypedClient<ITenantServiceClient>(c => new TenantServiceClient(new TenantServiceClientSettings()
            {
                AccessKey = Configuration["TenantsService:ApiKey"],
                BaseUrl = new Uri(Configuration["TenantsService:Url"])
            }, c)); 

        foreach (var tenant in TenantsToRegister)
        {
            services
                .AddGraphQLServer($"{tenant.Name}");
        }

        ...

    }

如果我在应用程序启动时有租户列表,上面的代码就可以工作。但我需要从另一个微服务请求该列表。有了这个约束,我需要构建服务提供者才能获得该列表。同时,我需要服务提供者构建之前的列表来注入我需要的服务。

我看到的唯一选项是在运行时添加服务,但我不确定是否可行。

【问题讨论】:

  • 微软的 DI 完全是运行时的。你可以做任何你需要做的事情来在你的ConfigureServices方法中获取TenantsToRegister
  • @KevinKrumwiede 当我说“运行时”时,我真正想说的是“在服务提供者建立之后”。我需要做的是使用ITenantServiceClient 的异步HTTP 调用,我还将它注入到服务集合中。我没有找到任何方法来做到这一点
  • 听起来像是 GetRequiredService 的工作? andrewlock.net/…

标签: .net .net-core graphql hotchocolate


【解决方案1】:

从架构的角度来看,我建议为每个租户使用“真实”服务。例如,为每个租户启动一个 docker 或应用程序进程。就像在以前的答案中写的一样(感谢 sergey)。

如果您真的想在同一进程中为每个租户启动一个新的 GraphQLServer,那么我会这样做: Full example on dotnet fiddle

services
    .AddHttpClient("TenantsService")
    .AddTypedClient<ITenantServiceClient>(c => 
                                    new TenantServiceClient(new TenantServiceClientSettings
                                    {
                                        AccessKey = "THE KEY", // Configuration["TenantsService:ApiKey"], 
                                        BaseUrl = new Uri("https://the-uri-you-need.com") // new Uri(Configuration["TenantsService:Url"])
                                    }, c));
        
// build a temporary service provider before, with all services added until now.
var tempServiceProvider = services.BuildServiceProvider();

// resolve the tenant service and query for tenants
var tenantsToRegister = tempServiceProvider.GetRequiredService<ITenantServiceClient>().GetTenants();

// register needed tenants
foreach (var tenant in tenantsToRegister)
{
    services.AddGraphQLServer($"{tenant}");
}

...当然,必须添加围绕调用ITenantServiceClient 的整个错误处理,但这仅用于演示目的。 这里的特别之处是中间的IServiceProvider,它使您能够使用全套 DI 来查询您的租户,但之后您可以添加所需的。

但是:这也是在启动时,运行时不能添加额外的服务器。

更新 这是updated dotnet fiddle(感谢 joao-figueira)

【讨论】:

  • 感谢您的回复。 dotnet fiddle 上的完整示例非常有帮助,并且暂时解决了我的问题。对于那些可能正在寻找相同解决方案的人,Martin 的代码中唯一缺少的部分是如何在启动时将基于 Main 方法构建的集合传递回 ConfigureServices(以继续添加其他 DI)。我现在发布我在另一条评论中使用的解决方案。
  • [link] dotnetfiddle.net/hi05JN Martin 的解决方案,上面提到了细节(见之前的评论)
  • @joao-figueira 感谢您的更新 :) 。我会把这个添加到答案中
【解决方案2】:

您遇到了问题...您正试图在多租户环境中垂直扩展。你会用 10 个租户做什么? 100?单个节点上的单个进程需要大量内存(如果您 100% 确定您将拥有其中一些,则可能不是这种情况),没有任何水平扩展的机会

我认为您可以从相同的图像为每个租户创建服务,但不同的配置和 api 网关/负载均衡器取决于... 某些东西...(标头、查询参数、用户 ID 等)。它可能需要一些基础设施投资,但将来不会是痛苦

如果您真的想从 http 客户端加载租户信息,然后为每个租户添加 HC gql 服务器,我建议您编写自己的 IConfiguration 提供程序。

这是 Consul 集成,它与您需要 https://github.com/wintoncode/Winton.Extensions.Configuration.Consul 具有相同的 http 往返。它添加自定义配置源并在启动时加载它

之后,您只需在启动时从配置中映射您的租户信息

【讨论】:

    猜你喜欢
    • 2020-10-21
    • 2021-05-25
    • 2019-09-30
    • 2019-04-25
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多