【问题标题】:Service Fabric with multiple endpoints and dependency injection具有多个终结点和依赖项注入的 Service Fabric
【发布时间】:2018-11-20 18:01:56
【问题描述】:

我目前正在从事一个 POC 项目,我正在尝试弄清楚如何在不同端点之间共享服务依赖项以控制应用程序状态并处理所有服务请求(我们称之为 ControlService) - 特别是当其中一个这些端点是 KestrelCommunicationListener / HttpSysCommunicationListener 并与 FabricTransportServiceRemotingListener(或任何其他类型的自定义侦听器)结合使用

Autofac 看起来很有希望,但是这些示例没有显示如何在启动时构建容器而不是主入口点时让 HTTP 侦听器工作 - 我是否需要将容器传递给 MyFabricService 以便它可以通过到启动注册并被添加到启动注册?

我已经看到使用 container.Update() 或使用 container.BeginLifetimeScope() 动态添加注册的引用,但它们都使用内置于 main 的容器,然后我不确定如何添加 API由 HTTP 侦听器创建到原始容器。

我可能没有很好地解释它,所以总而言之,我希望有类似以下服务的东西,可以通过 n 接收通信。不同的端点 - 处理消息,然后通过 n 发送消息。客户端(也称为其他服务端点)

如果有什么不清楚的地方,很高兴澄清 - 甚至可以使用另一个创意图表 :)

更新:

来自 Program.Main()

   ServiceRuntime.RegisterServiceAsync("ManagementServiceType",
                                context => new ManagementService(context)).GetAwaiter().GetResult();

这是我的面料服务

public ManagementService(StatefulServiceContext context)
        : base(context)
    {
        //this does not work but is pretty much what I'm after
        _managementService = ServiceProviderFactory.ServiceProvider.GetService(typeof(IManagementService)) as IManagementService;
    }

    protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners() =>
       new ServiceReplicaListener[]
       {
           //create external http listener
            ServiceReplicaListenerFactory.CreateExternalListener(typeof(Startup), StateManager, (serviceContext, message) => ServiceEventSource.Current.ServiceMessage(serviceContext, message), "ServiceEndpoint"),

            //create remoting listener with injected dependency
            ServiceReplicaListenerFactory.CreateServiceReplicaListenerFor(() => new RemotingListenerService(_managementService), "ManagmentServiceRemotingEndpoint", "ManagementServiceListener")

       };

ServiceReplicaListener

public static ServiceReplicaListener CreateExternalListener(Type startupType, IReliableStateManager stateManager, Action<StatefulServiceContext, string> loggingCallback, string endpointname)
    {
        return new ServiceReplicaListener(serviceContext =>
        {
            return new KestrelCommunicationListener(serviceContext, endpointname, (url, listener) =>
            {
                loggingCallback(serviceContext, $"Starting Kestrel on {url}");

                return new WebHostBuilder().UseKestrel()
                            .ConfigureServices((hostingContext, services) =>
                            {
                                services.AddSingleton(serviceContext);
                                services.AddSingleton(stateManager);

                                services.AddApplicationInsightsTelemetry(hostingContext.Configuration);
                                services.AddSingleton<ITelemetryInitializer>((serviceProvider) => new FabricTelemetryInitializer(serviceContext));
                            })
                            .ConfigureAppConfiguration((hostingContext, config) =>
                            {
                                config.AddServiceFabricConfiguration(serviceContext);
                            })
                            .ConfigureLogging((hostingContext, logging) =>
                            {
                                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                                logging.AddDebug();
                            })
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                            .UseStartup(startupType)
                            .UseUrls(url)
                            .Build();
            });
        });
    }

启动

public class Startup
{
    private const string apiTitle = "Management Service API";
    private const string apiVersion = "v1";

    private readonly IConfiguration configuration;

    public Startup(IConfiguration configuration)
    {
        this.configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        var modules = new List<ICompositionModule>
                      {
                          new Composition.CompositionModule(),
                          new BusinessCompositionModule()
                      };

        foreach (var module in modules)
        {
            module.AddServices(services, configuration);
        }

        services.AddSwashbuckle(configuration, apiTitle, apiVersion, "ManagementService.xml");
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddApplicationInsights(app.ApplicationServices);

        // app.UseAuthentication();
        //  app.UseSecurityContext();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
          //  app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseCors("CorsPolicy");

        app.UseMvc();

        app.UseSwagger(apiTitle, apiVersion);

        //app.UseMvc(routes =>
        //{
        //    routes.MapRoute(
        //        name: "default",
        //        template: "{controller=Home}/{action=Index}/{id?}");
        //});

    }
}

所有服务依赖项都使用startup.cs中的Microsoft.Extensions.DependencyInjection(不是autofac)添加到CompositionModules中

这很好用并创建了我的 HTTP 侦听器 - 我现在只需要一种方法来访问在我的 http 侦听器/webhost 启动期间添加到容器中的服务。

【问题讨论】:

  • 您能否详细说明一下您的依赖项的实例化位置?您是否需要能够在 WebHostBuilder.ConfigureServices 中的 *Listener 内执行注册?
  • @OlegKarasik 我用一些示例代码编辑了这个问题
  • IManagementService 是否有一些依赖项应该通过正在配置的依赖注入容器来解决?示例:ManagementService(IEnvironmentService) 其中IEnvironmentService 用于KestrelListener 中的WebHost 可以是WebHostEnvironmentServiceRemotingListener - RemotingWebHostEnvironmentService
  • 我认为我的示例代码有点误导抱歉 - IManagementService 是我所有业务逻辑的存放位置,本质上是我示例图中的“ControlService”。 ControlService 可能包含多个依赖项,但我不认为它需要对托管环境的依赖,因为它本质上只是处理在多个端点上收到的请求。

标签: dependency-injection autofac azure-service-fabric


【解决方案1】:

您可以使用 Autofac.Integration.ServiceFabriс,这是一个支持 Service Fabric 的 Autofac 扩展。您需要在 Program.cs 中创建一个容器

var builder = new ContainerBuilder();

builder.RegisterServiceFabricSupport();
builder.RegisterType<SomeService>().As<IManagementService>();

builder.RegisterStatelessService<ManagementService>("ManagementServiceType");
using (builder.Build())
{
   // Prevents this host process from terminating so services keep running.
   Thread.Sleep(Timeout.Infinite);
}

然后您可以将其注入到您的结构服务的构造函数中。您可以在 https://alexmg.com/posts/introducing-the-autofac-integration-for-service-fabric

上找到有关此主题的更多信息

【讨论】:

  • 感谢 Alex 的建议 - 我已经看过那篇文章并阅读了 Autofac 文档,但我不清楚我将如何配置和创建 KestrelCommunicationListener 如果我要在 Program.cs 中创建容器。我需要用 .UseStartup(startupType) 做什么,因为这通常是您为 ASP.Net Core API 配置服务的地方?
  • @Tim,此时您可以在 ConfigureServices 方法中注册监听器创建服务(它将是单例的,但这是您需要的,对吗?)。之后你可以使用 .net core 内置的 DI 容器注入它,如果你觉得更舒服的话,也可以将它与 Autofac 结合使用。
  • 如果 IManagementService aka IControlService 已经在 Program.cs 中的容器中注册,我是否需要在 ConfigureServices 期间再次添加它?它需要进入 Program.cs,这样我就可以将它传递给 FabricService 构造函数,这样我就可以在创建远程端点时使用它。几乎就像我需要将我在 Program.cs 中创建的容器传递到 Startup.cs 中,这样我就可以添加其他服务,例如 UseMvc()、UseCors,但由于 Core 可以控制在 Startup 中调用 Configure 方法,所以我是不知道如何覆盖它。
  • 如原始问题中所述,我可以根据stackoverflow.com/questions/38916620/… 使用 container.update 更新容器,但由于这被广泛认为是不好的做法,我正在寻找“最佳实践”解决方案
  • @Tim 老实说,我认为拥有一些“最佳实践”并不是很常见的问题。在您的情况下还将有几个容器 - 每个容器都用于每个端点。让我详细说明一下我的建议:您可以在 ConfigureServices 方法中添加行 services.AddSingleton(controlService);,其中 controlService 是注入到您的 Service Fabric 实例中的服务
【解决方案2】:

@蒂姆

抱歉,回复晚了。目前我正在研究我们公司用于内部项目的库包。该库简化了 Reliable Services 的配置。我认为我们最近的增强功能可以完成您需要做的事情(希望我能正确理解用例)。

关于库的所有信息都可以在 GitHub 上的project page 上找到,NuGet 包可以在here 上找到(请注意,它是一个预发布版本,但我们计划很快变成完整版本)。

如果您有任何问题或需要更多信息,请随时与我联系。

更新

我创建了一个sample application。请随时尝试。

这是一个代码示例。

public interface IManagementService
{
    string GetImportantValue();
}

public interface IMessageProvider
{
    string GetMessage();
}

public class MessageProvider : IMessageProvider
{
    public string GetMessage()
    {
        return "Value";
    }
}

public class ManagementService : IManagementService
{
    private readonly IMessageProvider provider;

    public ManagementService(
        IMessageProvider provider)
    {
        this.provider = provider;
    }

    public string GetImportantValue()
    {
        // Same instances should have the same hash
        return this.provider.GetMessage() + $"Hash: {this.GetHashCode()}";
    }
}

public interface IRemotingImplementation : IService
{
    Task<string> RemotingGetImportantValue();
}

public class RemotingImplementation : IRemotingImplementation
{
    private readonly IManagementService managementService;

    public RemotingImplementation(
        IManagementService managementService)
    {
        this.managementService = managementService;
    }

    public Task<string> RemotingGetImportantValue()
    {
        return Task.FromResult(this.managementService.GetImportantValue());
    }
}

public class WebApiImplementationController : ControllerBase
{
    private readonly IManagementService managementService;

    public WebApiImplementationController(
        IManagementService managementService)
    {
        this.managementService = managementService;
    }

    [HttpGet]
    public Task<string> WebApiGetImportantValue()
    {
        return Task.FromResult(this.managementService.GetImportantValue());
    }
}

public class WebApiStartup
{
    private readonly IConfiguration configuration;

    public WebApiStartup(
        IConfiguration configuration)
    {
        this.configuration = configuration;
    }

    public void ConfigureServices(
        IServiceCollection services)
    {
        services.AddMvc();
    }

    public void Configure(
        IApplicationBuilder app,
        IHostingEnvironment env,
        ILoggerFactory loggerFactory)
    {
        app.UseMvcWithDefaultRoute();
    }
}

internal static class Program
{
    /// <summary>
    ///     This is the entry point of the service host process.
    /// </summary>
    private static void Main()
    {
        var host = new HostBuilder()
           .ConfigureServices(
                services =>
                {
                    services.AddTransient<IMessageProvider, MessageProvider>();
                    services.AddSingleton<IManagementService, ManagementService>();
                })
           .ConfigureStatefulService(
                serviceBuilder =>
                {
                    serviceBuilder
                       .UseServiceType("StatefulServiceType")
                       .DefineAspNetCoreListener(
                            listenerBuilder =>
                            {
                                listenerBuilder
                                   .UseEndpointName("ServiceEndpoint")
                                   .UseKestrel()
                                   .UseUniqueServiceUrlIntegration()
                                   .ConfigureWebHost(
                                        webHostBuilder =>
                                        {
                                            webHostBuilder.UseStartup<WebApiStartup>();
                                        });
                            })
                       .DefineRemotingListener(
                            listenerBuilder =>
                            {
                                listenerBuilder
                                   .UseEndpointName("ServiceEndpoint2")
                                   .UseImplementation<RemotingImplementation>();
                            });
                })
           .Build()
           .Run();
    }
}

【讨论】:

    猜你喜欢
    • 2016-02-16
    • 2017-02-05
    • 2017-01-31
    • 2016-08-29
    • 1970-01-01
    • 2016-10-15
    • 2018-06-01
    • 2020-12-20
    • 2014-10-06
    相关资源
    最近更新 更多