终止 Actor
当所有指向某个 Actor 的句柄(handle)在 Python 中被销毁或离开作用域,或者创建该 Actor 的主进程(creator process)退出时,相应的 Actor 进程也会自动终止。
如果你希望在 Actor 被正常(优雅)终止时执行一些资源清理操作(如释放连接、关闭文件等),可以在 Actor 类中实现 __ray_shutdown__() 方法。当 Actor 被销毁时,Ray 会自动调用该方法。
注意
目前 Java 和 C++ 的 Actor 还不支持自动终止机制。
通过 Actor 句柄手动终止
在大多数情况下,Ray 会自动终止那些已经离开作用域的 Actor,但有时你可能需要强制终止一个 Actor。
这种情况通常包括:
- Actor 出现异常卡死(hanging)
- Actor 存在资源泄漏(resource leak)
- 对于 detached 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.
这将导致 Actor 立即退出其所在进程,从而使当前、正在排队以及未来的所有任务都失败,并抛出 RayActorError。
如果你希望 Ray 自动重启该 Actor,需要做到以下两点:
- 在
@ray.remote的选项中为该 Actor 设置一个非零的max_restarts - 在调用
ray.kill时传入参数:no_restart=False
使用 State API 查看已死亡 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 相关或分配给它的资源。
@ray.remote
class Actor:
def exit(self):
ray.actor.exit_actor()
actor = Actor.remote()
actor.exit.remote()
通常来说,这种方式一般没有必要使用,因为 Actor 会被自动进行垃圾回收。
由该任务返回的 ObjectRef 可以用来等待 Actor 退出(如果对其调用 ray.get(),将会抛出 RayActorError)。
这种终止方式会等待所有之前提交的任务执行完成,然后通过 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_shutdown__() 方法,Ray 会自动调用该方法。
这使你可以在 Actor 退出时执行资源清理操作,例如:关闭数据库连接、释放文件句柄、清理其他外部资源等。
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()时
❌ 不会调用的情况:
- 强制杀死(Force kill):使用
ray.kill(actor),Actor 会被立即终止,不会执行清理逻辑 - 异常终止:Actor 进程崩溃或异常退出,例如:段错误(segfault)被 OOM killer 杀死(内存不足)
Note
__ray_shutdown__()会在 所有 Actor 任务执行完成之后 才运行- 默认情况下,Ray 会等待 30 秒 来完成优雅关闭流程(包括执行
__ray_shutdown__()),如果超过这个时间,Actor 会被强制终止。可以通过以下方式修改超时时间:ray.init(_system_config={"actor_graceful_shutdown_timeout_ms": 60000}) -
在
__ray_shutdown__()中抛出的异常会被捕获并记录日志,但不会阻止 Actor 终止 -
__ray_shutdown__()必须是同步方法(即使是 async Actor 也一样)