Динамические настройки и переопределения Servicefabric - PullRequest
0 голосов
/ 17 октября 2018

Есть ли способ вообще не рассказывать сервису о настройках и просто предоставлять их на уровне приложения?

Я все еще недоволен тем, как работает конфигурация 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.Тем не менее, это просто принимает настройки, которые он находит;это не причина проблемы переопределения, с которой я сталкиваюсь.

Пример настройки службы. 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, как и ожидалось.

1 Ответ

0 голосов
/ 17 октября 2018

Существует множество способов настройки приложения Service Fabric, и каждый подход ставит вас перед различными задачами.

Команда 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>

Параметры приложения.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);

Вы можете пройти уровень дальше и сохранить все AuthorizationOptions как JSON, но, как уже говорилось ранее, чем более универсальным он станет, тем легче будет совершать ошибки и труднее находить проблемы с конфигурацией.

...