演员模型pykka④-Actor类
pykka.Actor()类
创建一个演员
- 一个Actor的子类实现:ThreadingActor
- 通常实现你的方法,包括__init__函数
- 调用你的actor类中的Actor.start()方法,传递你的任意参数给你的构造函数
停止一个演员,调用演员本身的Actor.stop方法,或者演员引用对象的ActorRef.stop方法
例如
import threading
import time
import pykka
class MyActor(pykka.ThreadingActor):
def __init__(self, my_arg=None):
super().__init__()
show_log(f"MyActor参数是:{my_arg}")
def on_start(self):
show_log("MyActor on_start方法")
def on_stop(self):
show_log("MyActor on_stop方法")
def on_failure(self, exception_type, exception_value, traceback):
show_log("MyActor on_failure方法")
def on_receive(self, message):
show_log("MyActor on_receive方法")
show_log(f"MyActor 收到的信息:{message}")
time.sleep(5)
return "MyActor success"
def a_method(self):
show_log("MyActor 自定义方法")
class MyActor2(pykka.ThreadingActor):
def __init__(self, my_arg=None):
super().__init__()
show_log(f"MyActor2 参数是:{my_arg}")
self.my_arg = my_arg
def on_start(self):
show_log("MyActor2 on_start方法")
def on_stop(self):
show_log("MyActor2 on_stop方法")
def on_failure(self, exception_type, exception_value, traceback):
show_log("MyActor2 on_failure方法")
def on_receive(self, message):
show_log("MyActor2 on_receive方法")
show_log(f"MyActor2 收到的信息:{message}")
time.sleep(5)
return "MyActor2 success"
def a_method(self):
show_log("MyActor2 自定义方法")
def show_log(msg):
log = threading.currentThread().name
print(f"[{log}]{msg}")
if __name__ == '__main__':
my_actor_ref = MyActor.start(my_arg="XXX")
my_actor_proxy = my_actor_ref.proxy()
my_actor_proxy.a_method()
my_actor_ref.tell("MyActor 你好")
ret = my_actor_ref.ask("MyActor 你好")
show_log(f"MyActor 获取到的返回值是:{ret}")
show_log("MyActor end test")
my_actor_ref2 = MyActor2.start(my_arg="Actor2")
my_actor_proxy2 = my_actor_ref2.proxy()
my_actor_proxy2.a_method()
my_actor_ref2.tell("MyActor2 hello")
ret = my_actor_ref2.ask("MyActor2 你好")
show_log(f"MyActor2 获取到的返回值是:{ret}")
show_log("MyActor2 end test2")
运行
如果把阻塞的ask去掉,更直观的可看出演员各自开线程执行各自方法
自我理解:
①只有代理对象能访问演员的属性或方法,演员引用访问抛出AttributeError异常
②演员引用调用tell方法或者ask方法后,会开启一个线程去执行演员的方法,包括on_start,on_stop,on_failure,on_receive以及自定义的方法
③所有演员的start方法传递参数后,会执行到init方法,init方法则是执行在主线程上,演员的其他方法是另外开一个线程去执行
④演员是基于队列的,所以处理完一条消息,才会处理下一条
start方法
开始一个演员,并且注册它到ActorRegistry类中
任何传递给start的参数都会被传递到init方法中
在幕后,当你调用start方法时,发生了这些事:
-
创建了演员
- actor_urn被初始化通过分配的URN
- actor_inbox被初始化通过一个新的actor inbox
-
Actor_ref被初始化通过pykka.ActorRef对象,为了能安全得与演员交流
-
演员接收循环开始,通过演员联合的线程和greenlet启动
返回一个安全的演员引用,能访问演员
actor_urn= None
这个字符串是演员的唯一身份标识,它可以通过指定演员使用get_by_urn()方法查找
actor_inbox= None
演员的消息盒子,使用tell方法或者ask方法,和朋友来放置信息到信息盒子
actor_stopped= None
一个threading.Event来表明是否演员继续传递消息,使用stop方法来改变它
actor_ref= None
演员的引用实例
stop()
停止演员
等价于调用ActorRef.stop方法,传递参数block=False
on_start()
在启动演员之后应该做的一些事情,但是需要在处理消息之前。
对于ThreadingActor而言,这个方法被执行在演员自己的线程,而init是被执行在创建演员的线程
如果引发了异常,这个方法的栈将被记录,并且演员将会停止
on_stop()
这是一个钩子,做一些清理动作,在演员已经处理了最后一条消息,并且在演员停止之前。
这个函数不会被调用当一个未被捕获的异常出现,换句话说,将会调用on_failure函数来替代它
对于ThreadingActor而言,这个方法是被调用在自己的线程里,在线程还存在的时候立刻调用
如果引发了异常,这个方法的栈将被记录,并且演员将会停止
on_failure(exception_type, exception_value, traceback)
钩子函数去处理一些清理,在一个未被处理的异常被抛出时,并且在演员停止之前
对于ThreadingActor而言,这个方法是被调用在自己的线程里,在线程还存在的时候立刻调用
这个函数的参数是来自于sys.exc_info
如果引发了异常,这个方法的栈将被记录,并且演员将会停止
on_receive(message)
处理非代理的信息
ActorRef
对可以安全传递的正在运行的actor的引用。
ActorRef实例是通过Actor.start的返回,可以通过ActorRegistry查找方法,你从不需要自己创建ActorRef。
is_alive()方法
检查演员是否活着
tell()方法
发送信息给演员,不等待返回
通常不会阻塞,但如果底层队列已满,它将阻塞,直到有空闲槽可用。
ask()方法
发送信息给演员,等待返回
信息可以是任意类型,如果参数block是false,则立即返回一个Future对象,而不阻塞
如果block
isTrue
和timeout
is None
,默认情况下,该方法将阻塞,直到它得到回复,可能永远。如果timeout
是整数或浮点数,该方法将等待timeout
几秒钟的回复,然后 raise pykka.Timeout
。
stop()方法
发送一个消息给演员,告诉它停止
返回True如果演员停止了或者正在停止,否则返回False如果演员已经死亡了,如果block=False则返回一个包装结果的Furue对象。
发送信息告诉演员停止在演员停止之前,并且将被正常执行,在演员停止之前。
停止后不能在重启
使用ask方法,所以是阻塞的。通过设置block=False跳过等待
proxy()方法
用ActorProxy代理包装ActorRef
这么使用
proxy = AnActor.start().proxy()
等同于
proxy = ActorProxy(AnActor.start())