副本排名#
警告
此 API 处于实验阶段,可能会在 Ray 的次要版本之间发生变化。
副本排名为部署中的每个副本提供了一个唯一的标识符。每个副本都会收到一个ReplicaRank 对象,其中包含排名信息和世界大小(副本总数)。排名对象包括全局排名(0 到 N-1 的整数)、节点排名和节点上的本地排名。
访问副本排名#
您可以通过副本上下文使用 serve.get_replica_context() 在部署内部访问排名和世界大小。
以下示例展示了如何访问副本排名信息
from ray import serve
@serve.deployment(num_replicas=4)
class ModelShard:
def __call__(self):
context = serve.get_replica_context()
return {
"rank": context.rank.rank, # Access the integer rank value
"world_size": context.world_size,
}
app = ModelShard.bind()
h = serve.run(app)
# Test that we can get rank information from replicas
seen_ranks = set()
for _ in range(20):
res = h.remote().result()
print(f"Output from __call__: {res}")
assert res["rank"] in [0, 1, 2, 3]
assert res["world_size"] == 4
seen_ranks.add(res["rank"])
# Verify we hit all replicas
print(f"Saw ranks: {sorted(seen_ranks)}")
# Output from __call__: {'rank': 2, 'world_size': 4}
# Output from __call__: {'rank': 1, 'world_size': 4}
# Output from __call__: {'rank': 3, 'world_size': 4}
# Output from __call__: {'rank': 0, 'world_size': 4}
# Output from __call__: {'rank': 0, 'world_size': 4}
# Output from __call__: {'rank': 0, 'world_size': 4}
# Output from __call__: {'rank': 0, 'world_size': 4}
# Output from __call__: {'rank': 3, 'world_size': 4}
# Output from __call__: {'rank': 1, 'world_size': 4}
# Output from __call__: {'rank': 1, 'world_size': 4}
# Output from __call__: {'rank': 0, 'world_size': 4}
# Output from __call__: {'rank': 1, 'world_size': 4}
# Output from __call__: {'rank': 3, 'world_size': 4}
# Output from __call__: {'rank': 2, 'world_size': 4}
# Output from __call__: {'rank': 0, 'world_size': 4}
# Output from __call__: {'rank': 0, 'world_size': 4}
# Output from __call__: {'rank': 2, 'world_size': 4}
# Output from __call__: {'rank': 1, 'world_size': 4}
# Output from __call__: {'rank': 3, 'world_size': 4}
# Output from __call__: {'rank': 0, 'world_size': 4}
# Saw ranks: [0, 1, 2, 3]
该 ReplicaContext 提供了两个关键字段:
rank:一个ReplicaRank对象,包含此副本的排名信息。使用.rank访问整数排名值。world_size:部署的目标副本数量。
该 ReplicaRank 对象包含三个字段:
rank:全局排名(0 到 N-1 的整数),表示此副本在所有节点上的唯一标识符。node_rank:此副本运行所在的节点的排名(0 到 M-1 的整数,其中 M 是节点数)。local_rank:此副本在其节点上的排名(0 到 K-1 的整数,其中 K 是此节点上的副本数)。
注意
访问排名值
要在代码中使用排名,请访问 .rank 属性以获取整数值。
context = serve.get_replica_context()
my_rank = context.rank.rank # Get the integer rank value
my_node_rank = context.rank.node_rank # Get the node rank
my_local_rank = context.rank.local_rank # Get the local rank on this node
大多数用例只需要全局 rank 值。 node_rank 和 local_rank 对于高级场景很有用,例如协调同一节点上的副本。
通过 reconfigure 处理排名更改#
当副本的排名发生变化时(例如在缩减规模期间),Ray Serve 可以自动调用部署类的 reconfigure 方法来通知它新的排名。这允许您在排名更改时更新特定于副本的状态。
以下示例展示了如何实现 reconfigure 来处理排名更改。
from typing import Any
from ray import serve
from ray.serve.schema import ReplicaRank
@serve.deployment(num_replicas=4, user_config={"name": "model_v1"})
class RankAwareModel:
def __init__(self):
context = serve.get_replica_context()
self.rank = context.rank.rank # Extract integer rank value
self.world_size = context.world_size
self.model_name = None
print(f"Replica rank: {self.rank}/{self.world_size}")
async def reconfigure(self, user_config: Any, rank: ReplicaRank):
"""Called when user_config or rank changes."""
self.rank = rank.rank # Extract integer rank value from ReplicaRank object
self.world_size = serve.get_replica_context().world_size
self.model_name = user_config.get("name")
print(f"Reconfigured: rank={self.rank}, model={self.model_name}")
def __call__(self):
return {"rank": self.rank, "model_name": self.model_name}
app2 = RankAwareModel.bind()
h = serve.run(app2)
for _ in range(20):
res = h.remote().result()
assert res["rank"] in [0, 1, 2, 3]
assert res["model_name"] == "model_v1"
seen_ranks.add(res["rank"])
# (ServeReplica:default:RankAwareModel pid=1231505) Replica rank: 0/4
# (ServeReplica:default:RankAwareModel pid=1231505) Reconfigured: rank=0, model=model_v1
# (ServeReplica:default:RankAwareModel pid=1231504) Replica rank: 1/4
# (ServeReplica:default:RankAwareModel pid=1231504) Reconfigured: rank=1, model=model_v1
# (ServeReplica:default:RankAwareModel pid=1231502) Replica rank: 3/4
# (ServeReplica:default:RankAwareModel pid=1231502) Reconfigured: rank=3, model=model_v1
# (ServeReplica:default:RankAwareModel pid=1231503) Replica rank: 2/4
# (ServeReplica:default:RankAwareModel pid=1231503) Reconfigured: rank=2, model=model_v1
调用 reconfigure 时#
Ray Serve 会在以下情况下自动调用您的 reconfigure 方法:
在副本启动时:当副本启动时,如果您的部署既有
reconfigure方法又有user_config,Ray Serve 会在运行__init__之后调用reconfigure。这允许您初始化感知排名的状态,而无需在__init__和reconfigure之间重复代码。更新 user_config 时:当您使用新的
user_config重新部署时,Ray Serve 会在所有正在运行的副本上调用reconfigure。如果您的reconfigure方法包含rank作为参数,Ray Serve 会将新的user_config和当前排名作为一个ReplicaRank对象传递。当副本排名更改时:在缩减规模期间,可能会重新分配排名以保持连续性(0 到 N-1)。如果您的
reconfigure方法包含rank作为参数,并且您的部署具有user_config,Ray Serve 会使用现有的user_config和新的排名(作为ReplicaRank对象)调用reconfigure。
注意
接收排名更新的要求
要通过 reconfigure 获取排名更改,您的部署需要:
基于类的部署(函数部署不支持
reconfigure)一个带有
rank作为参数的reconfigure方法:def reconfigure(self, user_config, rank: ReplicaRank)您的部署中有一个
user_config(即使它只是一个空字典:user_config={})。
没有 user_config,Ray Serve 将不会为排名更改调用 reconfigure。
提示
如果您希望在调用 reconfigure 进行排名更改时有不同的行为,请打开一个 GitHub issue与 Ray Serve 团队讨论您的用例。
副本排名如何工作#
注意
排名重新分配是最终一致的
当副本在缩减规模期间被移除时,为了保持连续性(0 到 N-1)的排名重新分配不会立即发生。控制器会在部署在其更新循环中达到 HEALTHY 状态时执行排名一致性检查和重新分配。这意味着在缩减规模后可能会有一段短暂的时间排名不连续,然后控制器才会重新分配它们。
此设计选择可防止排名重新分配干扰正在进行的部署更新和回滚。如果您需要立即的排名重新分配或不同的行为,请打开一个 GitHub issue与 Ray Serve 团队讨论您的用例。
注意
排名不影响调度或驱逐决策
副本排名与调度和驱逐决策无关。部署调度程序在将副本放置在节点上时不会考虑排名,因此不能保证具有连续排名的副本(例如排名 0 和排名 1)将在同一节点上。同样,在缩减规模期间,自动扩缩器的驱逐决策不考虑副本排名—任何副本都可以被选择移除,而不管其排名如何。
如果您需要基于排名的调度或驱逐(例如,为了共置连续排名的副本),请打开一个 GitHub issue与 Ray Serve 团队讨论您的需求。
Ray Serve 在整个部署生命周期中自动管理副本排名。系统维护这些不变性:
排名是 0 到 N-1 的连续整数。
每个正在运行的副本恰好有一个排名。
没有两个副本共享相同的排名。
排名分配生命周期#
下表显示了在不同事件中排名和世界大小的行为:
事件 |
本地排名 |
世界大小 |
|---|---|---|
扩缩 |
现有副本无变化 |
增加到目标数量 |
缩减 |
可以更改以保持连续性 |
减少到目标数量 |
其他副本死亡(将被重启) |
无变化 |
无变化 |
自身副本死亡 |
无变化 |
无变化 |
注意
世界大小始终反映部署配置的目标副本数,而不是当前运行副本数。在扩缩操作期间,即使副本仍在启动或停止,世界大小也会立即更新到新的目标。
排名生命周期状态机#
┌─────────────────────────────────────────────────────────────┐
│ DEPLOYMENT LIFECYCLE │
└─────────────────────────────────────────────────────────────┘
Initial Deployment / Upscaling:
┌──────────┐ assign ┌──────────┐
│ No Rank │ ───────────────> │ Rank: N-1│
└──────────┘ └──────────┘
(Contiguous: 0, 1, 2, ..., N-1)
Replica Crash:
┌──────────┐ release ┌──────────┐ assign ┌──────────┐
│ Rank: K │ ───────────────> │ Released │ ────────────> │ Rank: K │
│ (Dead) │ │ │ │ (New) │
└──────────┘ └──────────┘ └──────────┘
(K can be any rank from 0 to N-1)
:::{note}
When a replica crashes, Ray Serve automatically starts a replacement replica and assigns it the **same rank** as the crashed replica. This ensures rank contiguity is maintained without reassigning other replicas.
:::
Downscaling:
┌──────────┐ release ┌──────────┐
│ Rank: K │ ───────────────> │ Released │
│ (Stopped)│ │ │
└──────────┘ └──────────┘
│
└──> Remaining replicas may be reassigned to maintain
contiguity: [0, 1, 2, ..., M-1] where M < N
(K can be any rank from 0 to N-1)
Controller Recovery:
┌──────────┐ recover ┌──────────┐
│ Running │ ───────────────> │ Rank: N │
│ Replicas │ │(Restored)│
└──────────┘ └──────────┘
(Controller queries replicas to reconstruct rank state)
详细生命周期事件#
启动时排名分配:当副本启动时分配排名,例如在初始部署、冷启动或扩缩期间。控制器在初始化过程中分配排名并将其传播给副本。新副本获得最低可用排名。
关闭时排名释放:排名仅在副本完全停止后释放,这发生在正常关闭或缩减规模期间。Ray Serve 尽可能保留现有的排名分配,以最大程度地减少干扰。
处理副本崩溃:如果副本意外崩溃,系统会释放其排名并为替换副本分配相同的排名。这意味着如果排名为 3 的副本崩溃,新的替换副本也将获得排名 3。替换副本在其初始化过程中获得排名,而其他副本保持其现有排名不变。
控制器崩溃和恢复:当控制器从崩溃中恢复时,它通过查询所有正在运行的副本以获取其分配的排名来重建排名状态。排名不会被检查点;系统在恢复期间直接从副本重新学习它们。
维护排名连续性:缩减规模后,系统可能会重新分配排名给剩余的副本以保持连续性(0 到 N-1)。Ray Serve 通过仅在必要时更改排名来最小化重新分配。