为什么服务结构服务的分区策略绑定到分区而不是服务?

Why is the partitioning strategy for a service fabric service tied to the partition instead of to the service?

我刚刚开始为我的 Service Fabric 应用程序编写一些动态端点发现,并且正在寻找有关如何解析服务端点的示例。我在 Whosebug 上找到了以下代码示例:

我对此做了一些小改动,所以这是我的代码:

private readonly FabricClient m_fabricClient

public async Task RefreshEndpointList()
{
        var appList = await m_fabricClient.QueryManager.GetApplicationListAsync();
        var app = appList.Single(x => x.ApplicationName.ToString().Contains("<MyFabricDeploymentName>"));

        // Go through all running services
        foreach (var service in await m_fabricClient.QueryManager.GetServiceListAsync(app.ApplicationName))
        {
            var partitions = await m_fabricClient.QueryManager.GetPartitionListAsync(service.ServiceName);

            // Go through all partitions
            foreach (var partition in partitions)
            {
                // Check what kind of service we have - depending on that the resolver figures out the endpoints.
                // E.g. Singleton is easy as it is just one endpoint, otherwise we need some load balancing later on
                ServicePartitionKey key;
                switch (partition.PartitionInformation.Kind)
                {
                    case ServicePartitionKind.Singleton:
                        key = ServicePartitionKey.Singleton;
                        break;
                    case ServicePartitionKind.Int64Range:
                        var longKey = (Int64RangePartitionInformation)partition.PartitionInformation;
                        key = new ServicePartitionKey(longKey.LowKey);
                        break;
                    case ServicePartitionKind.Named:
                        var namedKey = (NamedPartitionInformation)partition.PartitionInformation;
                        key = new ServicePartitionKey(namedKey.Name);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException($"Can't resolve partition kind for partition with id {partition.PartitionInformation.Id}");
                }

                var resolvedServicePartition = await ServicePartitionResolver.GetDefault().ResolveAsync(service.ServiceName, key, CancellationToken.None);

                m_endpointCache.PutItem(service.ServiceTypeName, new ServiceDetail(service.ServiceTypeName, service.ServiceKind, ServicePartitionKind.Int64Range, resolvedServicePartition.Endpoints));
            }
        }
    }
}

我很高兴找到这个片段,但在研究它时,我发现了一件事让我有点困惑。

因此,在阅读了 SF 文档之后,据我所知,这似乎是它从上到下遵循的架构:

Service Fabric 集群 -> Service Fabric 应用程序(例如 myApp_Fabric)-> 服务(例如前端服务、头像微服务、后端服务)

从服务中我们可以深入到分区,而分区基本上类似于我集群中节点上的 "container",多个实例(副本)可以驻留在该节点上,实例是服务的实际部署。

不过我不太确定节点/分区/副本差异是否正确。

然而,回到我的困惑和实际问题:

为什么有关分区策略(单例、整数范围、命名)的信息附加到分区信息,而不是服务本身?据我了解,分区基本上是我如何配置我的服务以跨服务结构节点分布的产物。

那么,为什么分区策略不直接绑定到服务?

关于Service Fabric中的服务,有两种类型:有状态服务和无状态服务。

无状态服务不使用可靠集合处理状态。如果他们需要维护状态,他们必须依赖外部持久性解决方案,如数据库等。由于他们不处理可靠集合提供的状态,因此他们被分配了 Singelton 分区类型。

有状态服务能够将状态存储在可靠的集合中。为了能够扩展这些服务,这些集合中的数据应该划分为多个分区。每个服务实例都分配有一个特定的分区。分区数量按服务指定,如下例所示:

<Service Name="Processing">
    <StatefulService ServiceTypeName="ProcessingType" TargetReplicaSetSize="3" MinReplicaSetSize="3">
        <UniformInt64Partition PartitionCount="26" LowKey="0" HighKey="25" />
    </StatefulService>
</Service>

所以,鉴于上面的例子,我不明白你最后关于分区策略不直接绑定到服务的评论。

鉴于上述情况,该服务将有 26 个实例 运行ning,每个分区一个,乘以 副本数。

在无状态服务的情况下,只有一个分区(单例分区),因此实际实例数为 1 * 3(副本数)= 3。(3 个副本只是一个示例。大多数无状态服务的实例计数设置为 -1 的次数,这意味着集群中的每个节点都有 1 个实例。)

另一件事:在你的代码中,你在分区的代码迭代中有一个注释行:

// E.g. Singleton is easy as it is just one endpoint, otherwise we need some load balancing later on

此评论错误地指出分区与负载平衡有关。它不是,它与数据如何在服务实例上分区有关,您需要获取处理特定分区的服务的地址。假设我有一个包含 26 个分区的服务,我想获取存储在比方说第 5 个分区中的数据。然后,我需要获取为该分区提供服务的实例的端点。

您可能已经读过 the docs。如果没有,我也建议阅读它。

处理您的意见:

I was just wondering, is it not possible that multiple services run on the same partition?

可靠的集合与使用它们的服务耦合,底层分区也是如此。因此,同一分区上不能有超过一项服务 运行。

但是,服务实例可以。如果服务的副本大小为 3,那么将有 3 个实例为该分区提供服务。但是只有 1 是主实例,读取和写入复制到辅助实例的数据。

将您的服务想象成比萨饼,当您要求比萨饼时,您要求比萨饼的味道(服务类型),您通常不会指定比萨饼的切片方式(即:8 块),通常,比萨店会为您处理,有些可能会切成 4、8 或更多,具体取决于比萨的大小。

当您创建服务的实例时,您可以以类似的方式看到,您需要一个服务,这个服务将保存您的数据,您不应该关心数据是如何存储的。

作为消费者,当您需要了解服务的划分时,就像您打电话给比萨店,要求他们将比萨切成 4 片,而不是 8 片,您仍然得到相同的比萨,但现在你关心的是它会被切成多少块。服务分区的主要问题是,许多应用程序设计将此分区泄露给客户端,客户端需要在使用之前知道它有多少个分区或它们放置在哪里。

作为消费者,您不应该关心服务分区,但作为提供者(比萨店)应该关心,假设您订购了一个大比萨饼,比萨饼店 运行 开箱即用(节点)来放置披萨,他们可以把披萨分成两个小盒子。最后,消费者收到相同的比萨饼,但在不同的盒子中,并且必须处理它才能找到其中的切片。

以此类推,我们可以看到比较:

  • 风味 = 服务类型
  • 披萨 = 服务
  • 大小和切片方式 = 分区方案
  • 切片=分区
  • 盒子=节点
  • 披萨数量 = 副本

在 Service Fabric 中,之所以要解耦,是因为消费者可以请求服务,而提供者可以决定如何对其进行分区,在大多数情况下,分区是在应用程序创建时静态定义的,但它可以是动态的,如 UniformInt64Partition 中所示,您可以定义特定服务实例需要多少个分区,您可以在不更改代码行的情况下拥有具有不同分区或不同方案的同一服务的多个实例。您将如何向客户端公开这些分区是一个实现细节。