本文介绍了动态Servicefabric设置和替代的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有一种方法可以不告诉服务所有设置,而仅在应用程序级别提供设置?

我仍然对servicefabric配置的工作方式不满意.

据我所知,我必须在服务的settings.xml中指定所有可能的配置值.然后,我可以在应用程序的ApplicationParameters中覆盖它们.对于每个文档,这看起来也适用于环境变量.

造成的复杂之处在于,在许多情况下,我们的配置用于合并带有数组的选项.

例如考虑json:

{
  "AuthorizationOptions": {
    "Policies": [
      {
        "Name": "User",
        "Groups": [ "Domain Users" ]
      }
    ]
  }  
}

有2个数组;这是必要和有用的.要在服务结构配置中表达这一点,它可以转换为:

<Section Name="AuthorizationOptions">
    <Parameter Name="Policies:0:Name" Value="User"/>
    <Parameter Name="Policies:0:Groups:0" Value="Domain Users"/>
</Section>

尽管翻译与结构化对象相比并不令人满意,但它完全可用.

但是,如果我未在服务中指定部分和参数,则似乎无法在应用程序中覆盖它们.因此,在这种情况下,我将必须定义服务中每个策略的确切策略和组数,应用程序可以修改策略名称或组值,但不能修改策略总数或组总数.

是否有一种方法可以不告诉服务所有设置,而仅在应用程序级别提供设置?

如果没有其他选择可以使服务在我可能想用来不同地提供这种动态配置的应用程序之间重用的话?

可能有助于回答此问题的难题的最后一部分是我正在使用某些预发布代码,用于将服务结构设置转换为Microsoft.Extensions.Configuration.IConfiguration.但是,这只是采用它找到的设置.这不是我遇到的覆盖问题的原因.

示例服务Settings.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
  <Section Name="AuthorizationOptions">
    <!-- I should not have to provide these at the application level!
         However, it fails to deploy if I don't. -->
    <Parameter Name="Policies:0:Name" Value="User"/>
    <Parameter Name="Policies:0:Groups:0" Value="Domain Users"/>
  </Section>
</Settings>

示例应用程序ApplicationManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ApplicationTypeName="ServiceFabric.ExampleType" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
  <Parameters>
    <Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" DefaultValue="" />
    <Parameter Name="Service.Example_InstanceCount" DefaultValue="-1" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Name" DefaultValue="Users" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Groups_0" DefaultValue="Domain Users" />
  </Parameters>
  <ServiceManifestImport>
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="Service.ExamplePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides>
      <ConfigOverride Name="Config">
        <Settings>
          <Section Name="AuthorizationOptions">
            <Parameter Name="Policies:0:Name" Value="[Service.Example_AuthorizationOptions_Policies_0_Name]" />
            <Parameter Name="Policies:0:Groups:0" Value="[Service.Example_AuthorizationOptions_Policies_0_Groups_0]" />
          </Section>
        </Settings>
      </ConfigOverride>
    </ConfigOverrides>
    <EnvironmentOverrides CodePackageRef="code">
      <EnvironmentVariable Name="ASPNETCORE_ENVIRONMENT" Value="[Service.Example_ASPNETCORE_ENVIRONMENT]" />
    </EnvironmentOverrides>
  </ServiceManifestImport>
  <DefaultServices>
    <Service Name="Service.Example" ServicePackageActivationMode="ExclusiveProcess">
      <StatelessService ServiceTypeName="Service.ExampleType" InstanceCount="[Service.Example_InstanceCount]">
        <SingletonPartition />
      </StatelessService>
    </Service>
  </DefaultServices>
</ApplicationManifest>

示例应用程序ApplicationParameters(Local.1Node.xml):

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="fabric:/ServiceFabric.Example" xmlns="http://schemas.microsoft.com/2011/01/fabric">
  <Parameters>
    <Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" Value="Development" />
    <Parameter Name="Service.Example_InstanceCount" Value="-1" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Name" Value="Users" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Groups_0" Value="Domain Users" />
  </Parameters>
</Application>

示例应用程序PublishProfiles(Local.1Node.xml):

<?xml version="1.0" encoding="utf-8"?>
<PublishProfile xmlns="http://schemas.microsoft.com/2015/05/fabrictools">
  <ClusterConnectionParameters />
  <ApplicationParameterFile Path="..\ApplicationParameters\Local.1Node.xml" />
</PublishProfile>

应该无关,但示例使用设置:

internal sealed class Example : StatelessService
{
    public Example(StatelessServiceContext context)
        : base(context)
    { }

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return new ServiceInstanceListener[]
        {
            new ServiceInstanceListener(serviceContext =>
                new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                {
                    ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting HttpSys on {url}");

                    return new WebHostBuilder()
                            .UseHttpSys(options =>
                            {
                                options.Authentication.Schemes = AuthenticationSchemes.Negotiate; // Microsoft.AspNetCore.Server.HttpSys
                                                                                        options.Authentication.AllowAnonymous = false;
                            }).ConfigureServices(services => services.AddSingleton<StatelessServiceContext>(serviceContext))
                                        .UseContentRoot(Directory.GetCurrentDirectory())
                                        .ConfigureAppConfiguration((hostingContext, config) =>
                                            {
                                                config.SetBasePath(Directory.GetCurrentDirectory());
                                                config.AddServiceFabricConfiguration(FabricRuntime.GetActivationContext(), options => {
                                                    options.IncludePackageName=false;
                                                });
                                            })
                                        .UseStartup<Startup>()
                                        .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                                        .UseUrls(url)
                                        .Build();
                }))
        };
    }
}

从那时起,一切都按预期在IConfiguration对象中.

解决方案

配置服务结构应用程序的方法有很多,每种方法都会给您带来不同的挑战.

SF团队在文档中推荐了这种方法,因为您可以更好地控制配置的版本,并且由于在文件中明确声明了错误,因此更容易犯错误,由于限制,我使用了几种不同的方法像您一样,以下方法可能会解决您的问题:

与原始方法一样进行配置,但将复杂类型存储为JSON值:它是与推荐设计最接近的解决方案,您仍然可以在源代码控制中控制配置版本. >

那会是这样的:

Settings.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns... namespaces here...>
  <Section Name="AuthorizationOptions">
    <Parameter Name="Policies"/>
  </Section>
</Settings>

ApplicationManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest ApplicationTypeName="ServiceFabric.ExampleType" ApplicationTypeVersion="1.0.0" xmlns:...namespaces....>
  <Parameters>
    <Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" DefaultValue="" />
    <Parameter Name="Service.Example_InstanceCount" DefaultValue="-1" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies" DefaultValue="[]" />
  </Parameters>
  <ServiceManifestImport>
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="Service.ExamplePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides>
      <ConfigOverride Name="Config">
        <Settings>
          <Section Name="AuthorizationOptions">
            <Parameter Name="Policies" Value="[Service.Example_AuthorizationOptions_Policies]" />
          </Section>
        </Settings>
      </ConfigOverride>
    </ConfigOverrides>
    <EnvironmentOverrides CodePackageRef="code">
      <EnvironmentVariable Name="ASPNETCORE_ENVIRONMENT" Value="[Service.Example_ASPNETCORE_ENVIRONMENT]" />
    </EnvironmentOverrides>
  </ServiceManifestImport>
  <DefaultServices>
    <Service Name="Service.Example" ServicePackageActivationMode="ExclusiveProcess">
      <StatelessService ServiceTypeName="Service.ExampleType" InstanceCount="[Service.Example_InstanceCount]">
        <SingletonPartition />
      </StatelessService>
    </Service>
  </DefaultServices>
</ApplicationManifest>

ApplicationParameters.xml

<?xml version="1.0" encoding="utf-8"?>
<Application Name="fabric:/ServiceFabric.Example" xmlns:...namespaces....>
  <Parameters>
    <Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" Value="Development" />
    <Parameter Name="Service.Example_InstanceCount" Value="-1" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies" Value="[{'Name': 'User','Groups': ['Domain Users']}, {'Name': 'Admin','Groups': ['Administrators']}]" />
  </Parameters>
</Application>

在您的服务代码中:

public class Policy
{
    public string Name { get; set; }
    public string[] Groups { get; set; }
}


var settings = this.Context.CodePackageActivationContext.GetConfigurationPackageObject("Config").Settings;
var authOptions = settings.Sections["AuthorizationOptions"].Parameters["Policies"].Value;
var obj = JsonConvert.DeserializeObject<Policy[]>(authOptions);

Is there a way to not tell the service about the settings at all and just provide them at the application level?

I am still unhappy with how servicefabric configuration seems to work.

Near as I can tell I have to specify in the service’s settings.xml all of the possible configuration values. Then I can override those in the application’s ApplicationParameters. Per documentation this looks like it holds true for environment variables also.

The complication that creates is that our configuration is used to hydrate options in many cases with arrays.

For example consider the json:

{
  "AuthorizationOptions": {
    "Policies": [
      {
        "Name": "User",
        "Groups": [ "Domain Users" ]
      }
    ]
  }  
}

There are 2 arrays; that are necessary and useful. To express this in the service fabric configuration it translates to:

<Section Name="AuthorizationOptions">
    <Parameter Name="Policies:0:Name" Value="User"/>
    <Parameter Name="Policies:0:Groups:0" Value="Domain Users"/>
</Section>

While the translation is not pleasant in comparison to the structured object it is completely usable.

However, If I don’t specify the section and parameters in the service, I can’t seem to override them in the application. So in this case I would have to define the exact number of policies and groups per policy in the service and the application could modify the policy name, or the group values, but not the number of policies total or number of groups total.

Is there a way to not tell the service about the settings at all and just provide them at the application level?

If not what alternatives exist to make the service reusable across applications that I may want to use to provide this type of dynamic configuration differently?

The last part of the puzzle that may assist in answering this question is I am using some pre-release code to translate the service fabric settings into Microsoft.Extensions.Configuration.IConfiguration. However, that is just taking the settings it finds; it isn't the cause of the override issue I am running into.

Example Service Settings.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
  <Section Name="AuthorizationOptions">
    <!-- I should not have to provide these at the application level!
         However, it fails to deploy if I don't. -->
    <Parameter Name="Policies:0:Name" Value="User"/>
    <Parameter Name="Policies:0:Groups:0" Value="Domain Users"/>
  </Section>
</Settings>

Example Application ApplicationManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ApplicationTypeName="ServiceFabric.ExampleType" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
  <Parameters>
    <Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" DefaultValue="" />
    <Parameter Name="Service.Example_InstanceCount" DefaultValue="-1" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Name" DefaultValue="Users" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Groups_0" DefaultValue="Domain Users" />
  </Parameters>
  <ServiceManifestImport>
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="Service.ExamplePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides>
      <ConfigOverride Name="Config">
        <Settings>
          <Section Name="AuthorizationOptions">
            <Parameter Name="Policies:0:Name" Value="[Service.Example_AuthorizationOptions_Policies_0_Name]" />
            <Parameter Name="Policies:0:Groups:0" Value="[Service.Example_AuthorizationOptions_Policies_0_Groups_0]" />
          </Section>
        </Settings>
      </ConfigOverride>
    </ConfigOverrides>
    <EnvironmentOverrides CodePackageRef="code">
      <EnvironmentVariable Name="ASPNETCORE_ENVIRONMENT" Value="[Service.Example_ASPNETCORE_ENVIRONMENT]" />
    </EnvironmentOverrides>
  </ServiceManifestImport>
  <DefaultServices>
    <Service Name="Service.Example" ServicePackageActivationMode="ExclusiveProcess">
      <StatelessService ServiceTypeName="Service.ExampleType" InstanceCount="[Service.Example_InstanceCount]">
        <SingletonPartition />
      </StatelessService>
    </Service>
  </DefaultServices>
</ApplicationManifest>

Example Application ApplicationParameters (Local.1Node.xml):

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="fabric:/ServiceFabric.Example" xmlns="http://schemas.microsoft.com/2011/01/fabric">
  <Parameters>
    <Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" Value="Development" />
    <Parameter Name="Service.Example_InstanceCount" Value="-1" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Name" Value="Users" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Groups_0" Value="Domain Users" />
  </Parameters>
</Application>

Example Application PublishProfiles (Local.1Node.xml):

<?xml version="1.0" encoding="utf-8"?>
<PublishProfile xmlns="http://schemas.microsoft.com/2015/05/fabrictools">
  <ClusterConnectionParameters />
  <ApplicationParameterFile Path="..\ApplicationParameters\Local.1Node.xml" />
</PublishProfile>

Should be unrelated, but example consumption of the settings:

internal sealed class Example : StatelessService
{
    public Example(StatelessServiceContext context)
        : base(context)
    { }

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        return new ServiceInstanceListener[]
        {
            new ServiceInstanceListener(serviceContext =>
                new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                {
                    ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting HttpSys on {url}");

                    return new WebHostBuilder()
                            .UseHttpSys(options =>
                            {
                                options.Authentication.Schemes = AuthenticationSchemes.Negotiate; // Microsoft.AspNetCore.Server.HttpSys
                                                                                        options.Authentication.AllowAnonymous = false;
                            }).ConfigureServices(services => services.AddSingleton<StatelessServiceContext>(serviceContext))
                                        .UseContentRoot(Directory.GetCurrentDirectory())
                                        .ConfigureAppConfiguration((hostingContext, config) =>
                                            {
                                                config.SetBasePath(Directory.GetCurrentDirectory());
                                                config.AddServiceFabricConfiguration(FabricRuntime.GetActivationContext(), options => {
                                                    options.IncludePackageName=false;
                                                });
                                            })
                                        .UseStartup<Startup>()
                                        .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                                        .UseUrls(url)
                                        .Build();
                }))
        };
    }
}

From that point forward everything is in the IConfiguration object as expected.

解决方案

There are many ways to configure a service fabric application, and each approach will bring you to a different challenges.

SF team recommend the approach in the docs, because you can have a better version control of configurations, and makes harder to commit mistakes, as it is explicitly declared in a file, I've used a few different approaches because of limitations like yours, the follwoing approach might solve your problem:

Configure like the original approach, but with complex types stored as JSON values: it is the closest solution to the recommended design and you still can keep control of the configuration versions on source control.

It would be something like:

Settings.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns... namespaces here...>
  <Section Name="AuthorizationOptions">
    <Parameter Name="Policies"/>
  </Section>
</Settings>

ApplicationManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest ApplicationTypeName="ServiceFabric.ExampleType" ApplicationTypeVersion="1.0.0" xmlns:...namespaces....>
  <Parameters>
    <Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" DefaultValue="" />
    <Parameter Name="Service.Example_InstanceCount" DefaultValue="-1" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies" DefaultValue="[]" />
  </Parameters>
  <ServiceManifestImport>
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="Service.ExamplePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides>
      <ConfigOverride Name="Config">
        <Settings>
          <Section Name="AuthorizationOptions">
            <Parameter Name="Policies" Value="[Service.Example_AuthorizationOptions_Policies]" />
          </Section>
        </Settings>
      </ConfigOverride>
    </ConfigOverrides>
    <EnvironmentOverrides CodePackageRef="code">
      <EnvironmentVariable Name="ASPNETCORE_ENVIRONMENT" Value="[Service.Example_ASPNETCORE_ENVIRONMENT]" />
    </EnvironmentOverrides>
  </ServiceManifestImport>
  <DefaultServices>
    <Service Name="Service.Example" ServicePackageActivationMode="ExclusiveProcess">
      <StatelessService ServiceTypeName="Service.ExampleType" InstanceCount="[Service.Example_InstanceCount]">
        <SingletonPartition />
      </StatelessService>
    </Service>
  </DefaultServices>
</ApplicationManifest>

ApplicationParameters.xml

<?xml version="1.0" encoding="utf-8"?>
<Application Name="fabric:/ServiceFabric.Example" xmlns:...namespaces....>
  <Parameters>
    <Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" Value="Development" />
    <Parameter Name="Service.Example_InstanceCount" Value="-1" />
    <Parameter Name="Service.Example_AuthorizationOptions_Policies" Value="[{'Name': 'User','Groups': ['Domain Users']}, {'Name': 'Admin','Groups': ['Administrators']}]" />
  </Parameters>
</Application>

In your service code:

public class Policy
{
    public string Name { get; set; }
    public string[] Groups { get; set; }
}


var settings = this.Context.CodePackageActivationContext.GetConfigurationPackageObject("Config").Settings;
var authOptions = settings.Sections["AuthorizationOptions"].Parameters["Policies"].Value;
var obj = JsonConvert.DeserializeObject<Policy[]>(authOptions);

这篇关于动态Servicefabric设置和替代的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-22 21:50