终止 Actor#

当 Python 中 Actor 句柄的所有副本都超出作用域,或者原始创建者进程死亡时,Actor 进程将自动终止。当 Actor 正常终止时,Ray 会调用 Actor 的 __ray_shutdown__() 方法(如果已定义),从而允许清理资源(有关详细信息,请参阅 使用 __ray_shutdown__ 清理 Actor)。

请注意,Java 或 C++ 尚不支持 Actor 的自动终止。

通过 Actor 句柄手动终止#

在大多数情况下,Ray 会自动终止超出作用域的 Actor,但有时您可能需要强制终止 Actor。这应该保留给 Actor 意外挂起或泄漏资源的情况,以及必须手动销毁的 独立 Actor

import ray

@ray.remote
class Actor:
    pass

actor_handle = Actor.remote()

ray.kill(actor_handle)
# Force kill: the actor exits immediately without cleanup.
# This will NOT call __ray_shutdown__() or atexit handlers.
actorHandle.kill();
// This will not go through the normal Java System.exit teardown logic, so any
// shutdown hooks installed in the actor using ``Runtime.addShutdownHook(...)`` will
// not be called.
actor_handle.Kill();
// This will not go through the normal C++ std::exit
// teardown logic, so any exit handlers installed in
// the actor using ``std::atexit`` will not be called.

这将导致 Actor 立即退出其进程,从而导致任何当前、待处理和将来的任务都因 RayActorError 而失败。如果您希望 Ray 自动重启 Actor,请确保在 Actor 的 @ray.remote 选项中设置非零 max_restarts,然后将标志 no_restart=False 传递给 ray.kill

对于 命名 Actor 和独立 Actor,在 Actor 句柄上调用 ray.kill 会销毁 Actor 并允许其名称被重用。

使用 State API 中的 ray list actors --detail 来查看已死 Actor 的死亡原因。

# This API is only available when you download Ray via `pip install "ray[default]"`
ray list actors --detail
---
-   actor_id: e8702085880657b355bf7ef001000000
    class_name: Actor
    state: DEAD
    job_id: '01000000'
    name: ''
    node_id: null
    pid: 0
    ray_namespace: dbab546b-7ce5-4cbb-96f1-d0f64588ae60
    serialized_runtime_env: '{}'
    required_resources: {}
    death_cause:
        actor_died_error_context: # <---- You could see the error message w.r.t why the actor exits.
            error_message: The actor is dead because `ray.kill` killed it.
            owner_id: 01000000ffffffffffffffffffffffffffffffffffffffffffffffff
            owner_ip_address: 127.0.0.1
            ray_namespace: dbab546b-7ce5-4cbb-96f1-d0f64588ae60
            class_name: Actor
            actor_id: e8702085880657b355bf7ef001000000
            never_started: true
            node_ip_address: ''
            pid: 0
            name: ''
    is_detached: false
    placement_group_id: null
    repr_name: ''

在 Actor 内部手动终止#

如有必要,您可以从 Actor 方法内部手动终止 Actor。这将杀死 Actor 进程并释放与 Actor 相关联或分配给 Actor 的资源。

@ray.remote
class Actor:
    def exit(self):
        ray.actor.exit_actor()

actor = Actor.remote()
actor.exit.remote()

通常不需要这种方法,因为 Actor 会被自动垃圾回收。可以等待任务产生的 ObjectRef 以等待 Actor 退出(对其调用 ray.get() 将引发 RayActorError)。

Ray.exitActor();

Actor 的垃圾回收尚未实现,因此这是目前优雅终止 Actor 的唯一方法。可以等待任务产生的 ObjectRef 以等待 Actor 退出(对其调用 ObjectRef::get 将抛出 RayActorException)。

ray::ExitActor();

Actor 的垃圾回收尚未实现,因此这是目前优雅终止 Actor 的唯一方法。可以等待任务产生的 ObjectRef 以等待 Actor 退出(对其调用 ObjectRef::Get 将抛出 RayActorException)。

请注意,此终止方法会等待直到所有先前提交的任务执行完毕,然后通过 sys.exit 正常退出进程。

您可以看到 Actor 已死,这是由于用户调用了 exit_actor()

# This API is only available when you download Ray via `pip install "ray[default]"`
ray list actors --detail
---
-   actor_id: 070eb5f0c9194b851bb1cf1602000000
    class_name: Actor
    state: DEAD
    job_id: '02000000'
    name: ''
    node_id: 47ccba54e3ea71bac244c015d680e202f187fbbd2f60066174a11ced
    pid: 47978
    ray_namespace: 18898403-dda0-485a-9c11-e9f94dffcbed
    serialized_runtime_env: '{}'
    required_resources: {}
    death_cause:
        actor_died_error_context:
            error_message: 'The actor is dead because its worker process has died.
                Worker exit type: INTENDED_USER_EXIT Worker exit detail: Worker exits
                by a user request. exit_actor() is called.'
            owner_id: 02000000ffffffffffffffffffffffffffffffffffffffffffffffff
            owner_ip_address: 127.0.0.1
            node_ip_address: 127.0.0.1
            pid: 47978
            ray_namespace: 18898403-dda0-485a-9c11-e9f94dffcbed
            class_name: Actor
            actor_id: 070eb5f0c9194b851bb1cf1602000000
            name: ''
            never_started: false
    is_detached: false
    placement_group_id: null
    repr_name: ''

使用 __ray_shutdown__ 清理 Actor#

当 Actor 正常终止时,Ray 会调用 __ray_shutdown__() 方法(如果存在),从而允许清理数据库连接或文件句柄等资源。

import ray
import tempfile
import os

@ray.remote
class FileProcessorActor:
    def __init__(self):
        self.temp_file = tempfile.NamedTemporaryFile(delete=False)
        self.temp_file.write(b"processing data")
        self.temp_file.flush()

    def __ray_shutdown__(self):
        # Clean up temporary file
        if hasattr(self, 'temp_file'):
            self.temp_file.close()
            os.unlink(self.temp_file.name)

    def process(self):
        return "done"

actor = FileProcessorActor.remote()
ray.get(actor.process.remote())
del actor  # __ray_shutdown__() is called automatically

调用 __ray_shutdown__()

  • 自动终止:当所有 Actor 句柄超出作用域时(del actor 或自然退出作用域)

  • 手动优雅终止:当您调用 actor.__ray_terminate__.remote()

调用 __ray_shutdown__()

  • 强制终止:当您使用 ray.kill(actor) 时 - Actor 会立即被杀死,不进行清理。

  • 意外终止:当 Actor 进程崩溃或意外退出时(例如段错误或被 OOM killer 杀死)。

重要提示

  • __ray_shutdown__() 在所有 Actor 任务完成后运行。

  • 默认情况下,Ray 等待 30 秒完成优雅关机过程(包括 __ray_shutdown__())。如果 Actor 在此超时内未退出,则会被强制杀死。可以使用 ray.init(_system_config={"actor_graceful_shutdown_timeout_ms": 60000}) 进行配置。

  • __ray_shutdown__() 中的异常进行捕获和记录,但不会阻止 Actor 终止。

  • __ray_shutdown__() 必须是同步方法,包括异步 Actor。