演员模型pykka④-Actor类

pykka.Actor()类

创建一个演员

  1. 一个Actor的子类实现:ThreadingActor
  2. 通常实现你的方法,包括__init__函数
  3. 调用你的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")

运行

image-20220127112304674

如果把阻塞的ask去掉,更直观的可看出演员各自开线程执行各自方法

image-20220127112513271

自我理解:

①只有代理对象能访问演员的属性或方法,演员引用访问抛出AttributeError异常

②演员引用调用tell方法或者ask方法后,会开启一个线程去执行演员的方法,包括on_start,on_stop,on_failure,on_receive以及自定义的方法

③所有演员的start方法传递参数后,会执行到init方法,init方法则是执行在主线程上,演员的其他方法是另外开一个线程去执行

④演员是基于队列的,所以处理完一条消息,才会处理下一条

start方法

开始一个演员,并且注册它到ActorRegistry类中

任何传递给start的参数都会被传递到init方法中

在幕后,当你调用start方法时,发生了这些事:

  1. 创建了演员

    1. actor_urn被初始化通过分配的URN
    2. actor_inbox被初始化通过一个新的actor inbox
  2. Actor_ref被初始化通过pykka.ActorRef对象,为了能安全得与演员交流

  3. 演员接收循环开始,通过演员联合的线程和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对象,而不阻塞

如果blockisTruetimeoutis 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())
posted @ 2022-02-14 19:08  南风丶轻语  阅读(114)  评论(0编辑  收藏  举报