多集群配置

多集群配置决定了哪些集群是当前多集群的一部分。它不会自动改变,而是由操作员控制。因此,它与集群内使用的成员机制完全不同,后者自动确定属于集群的Silo集合。

我们对服务中的群组使用以下术语:

  • 如果一个集群至少有一个活跃的Silo,那么它就是活跃的,否则就是不活跃
  • 如果一个集群是当前多集群配置的一部分,那么它就是加入的,否则就是未加入的。

活跃/不活跃与加入/未加入是独立的:所有四个组合都是可能的。

一个服务的所有集群都由一个Gossip网络连接。Gossip网络负责传播配置和状态信息。

注入配置

操作员通过向多集群网络注入来发布配置变更。配置可以被注入到任何集群,并从那里传播到所有活动集群。每一个新的配置包含一个组成多集群的集群ID的列表。它也有一个UTC时间戳,用来跟踪它在Gossip网络中的传播情况。

最开始,多集群配置是空的,这意味着多集群列表是空的(不包含任何集群)。因此,操作员必须注入一个初始的多集群配置。一旦注入,这个配置就会在所有连接的Silo(在运行的时候)和所有指定的Gossip通道(如果这些通道是持久的)中持续存在。

我们对操作员必须遵循的新配置的注入提出了一些限制:

  • 每个新的配置都可以增加一些集群,或删除一些集群(但不能同时进行)。
  • 操作员不应该在以前的配置变更仍在处理中时发布新的配置。

这些限制确保单例协议等协议即使在配置改变的情况下也能正确地维持激活的互斥。

通过“管理Grain”注入

多集群配置可以在任何集群的任何节点上注入,使用Orleans管理Grain。 例如,要注入一个由三个集群{ us1, eu1, us2 }组成的多集群配置,我们可以向管理Grain传递一个可枚举的字符串:

  1. var clusterlist = "us1,eu1,us2".Split(',');
  2. var mgtGrain = client.GetGrain<IManagementGrain>(0);
  3. mgtGrain.InjectMultiClusterConfiguration(clusterlist, "my comment here"));

InjectMultiClusterConfiguration的第一个参数是一个可枚举的集群ID列表,它将定义新的多集群配置。第二个参数是一个(可选的)注释字符串,可以用来标记配置的任意信息,如谁注入了它们,为什么注入。

有一个可选的第三个参数,一个名为checkForLaggingSilosFirst的布尔值,默认为true。这意味着系统会进行尽力而为的检查,看是否有Silo还没有跟进当前的配置,如果发现这样的Silo,就会拒绝更改。这有助于检测违反限制的行为,即一次只能有一个配置变更(尽管它不能保证在所有情况下都是如此)。

通过默认配置注入

在事先知道多集群配置并且每次部署都是全新的情况下(例如用于测试),我们可能希望提供一个默认配置。全局配置支持一个可选的属性DefaultMultiCluster,它需要一个用逗号分隔的集群ID列表:

  1. var silo = new SiloHostBuilder()
  2. [...]
  3. .Configure<MultiClusterOptions>(options =>
  4. {
  5. [...]
  6. options.DefaultMultiCluster = new[] { "us1", "eu1", "us2" };
  7. [...]
  8. })
  9. [...]

在使用此设置启动Silo后,它会检查当前的多集群配置是否为空,如果为空,则用当前的UTC时间戳注入给定的配置。

警告:持久化的多集群Gossip通道(例如基于AzureTable)会保留最后注入的配置,除非它们被显式删除。在这种情况下,指定DefaultMulticluster在重新部署集群时没有影响,因为存储在Gossip通道中的配置不是空的。

通过Gossip通道注入

操作员也可以将配置直接注入到Gossip通道中。通道中的变化会被周期性的后台Gossip自动接收和传播,尽管可能很慢(使用管理Grain会快得多)。对传播时间的一个粗略估计是30秒(或全局配置中指定的任何Gossip间隔)乘以所有集群中Silo数的以二为底的对数。但是,由于Gossip对是随机选择的,它既可能更快,也可能更慢。

如果使用基于Azure Table的Gossip渠道,操作员可以通过编辑OrleansGossipTable中的配置记录来注入新的配置,例如使用一些工具来编辑Azure Table中的数据。配置记录的格式如下:

名称 类型
PartitionKey String ServiceId
RowKey String “CONFIG”
Clusters String 逗号分隔的集群ID列表,例如:”us1,eu1,us2”
Comment String 可选的注释
GossipTimestamp DateTime 配置的UTC时间戳

注意:当在存储中编辑这条记录时,GossipTimestamp也必须被设置为一个更新的值(否则变化会被忽略)。最方便和推荐的方法是删除GossipTimestamp字段—我们的Gossip通道实现会自动用一个正确的、当前的时间戳替换它(它使用Azure Table的时间戳)。

集群的增/删程序

从多集群中添加或删除一个集群往往需要在一些更大的背景下进行协调。我们建议在从多集群中添加/删除集群时,始终遵循下面描述的程序。

增加集群

  1. 启动一个新的Orleans集群,等到所有Silo都启动并运行起来。
  2. 注入一个包含新集群的配置。
  3. 开始将用户请求路由到新集群。

移除集群

  1. 停止将新的用户请求路由到集群。
  2. 注入一个不再包含此集群的配置。
  3. 停止集群的所有Silo。

一旦以这种方式删除了一个集群,它还可以按照添加一个新集群的程序重新添加回来。

未加入的集群的活动

可能会有临时且短暂的时间,一个集群既是活跃的,又是未加入的:

  • 一个新启动的集群可能在进入多集群配置之前就开始执行代码(在添加集群的步骤1和步骤2之间)。
  • 一个正在退出多集群的集群在Silo关闭之前(在拆除集群的程序的第2和第3步之间),仍然在执行代码。

在这些中间情况下,有可能出现以下情况:

  • 对于全局单例Grain。一个Grain可能在一个未加入的集群上有重复的激活。
  • 对于版本化的Grain:当Grain状态发生变化时,未加入集群上的激活不会收到通知。