Exemple #1
0
    def __init__(self,
                 name,
                 loop=asyncio.get_event_loop(),
                 address=None,
                 mailbox_size=1000,
                 inbox=None):
        """
        The constructor

        :Keyword Arguments:
            *publisher (BaseActor): The publishing actor
            *router (BaseActor): a router for subscribers
        """
        super().__init__(name, loop, address, mailbox_size, inbox)
        self._subscription_router = RoundRobinRouter()

        if self._subscription_router.get_state() is ActorState.LIMBO:
            self._subscription_router.start()

        if self._subscription_router.get_state() is not ActorState.RUNNING:
            raise ActorStateError("Router not Started in PubSub")

        self.register_handler(Subscribe, self.subscribe)
        self.register_handler(Publish, self.do_publish)
        self.register_handler(DeSubscribe, self.desubscribe)
Exemple #2
0
def test__round_robin_tell_at_load():
    print("Load Testing With Tell")
    sys = ActorSystem("tests")
    rr = RoundRobinRouter("test_router")
    rr.start()
    rr.set_actor_system(sys, "tests")

    actors = []

    async def say_hello(i):
        await rr.route_tell(StringMessage("Hello {}".format(i)))

    async def comp(funcs):
        await asyncio.gather(*funcs)

    print("Starting Actor")
    for i in range(0, 100):
        a = StringTestActor()
        a.start()
        rr.add_actor(a)
        actors.append(a)

    print("Start Adding")
    funcs = [say_hello(i) for i in range(0, len(actors))]
    print("Waiting")
    asyncio.get_event_loop().run_until_complete(comp(funcs))
    sys.close()
    del sys
    gc.collect()
    del gc.garbage[:]
    assert(rr.get_state() is ActorState.TERMINATED), "Router Not Terminated"
    print("Load Testing With Tell Complete")
Exemple #3
0
def test_round_robin_at_load():
    print("Load Testing")
    sys = ActorSystem("tests")
    rr = RoundRobinRouter("test_router")
    rr.start()
    rr.set_actor_system(sys, "tests")

    actors = []

    async def get_addition():
        res = await rr.route_ask(AddIntMessage(1))
        return res

    async def get_sum(funcs):
        res = await asyncio.gather(*funcs)
        return sum(res) / 2

    print("Starting Actor")
    for i in range(0, 100):
        a = AddTestActor("testa")
        a.start()
        rr.add_actor(a)
        actors.append(a)

    print("Start Adding")
    funcs = [get_addition() for x in actors]
    print("Waiting")
    res = asyncio.get_event_loop().run_until_complete(get_sum(funcs))
    print(res)
    assert(int(res) is len(funcs)), "Result {} is not {}".format(res, len(funcs))
    sys.close()
    assert(rr.get_state() is ActorState.TERMINATED), "Router Not Terminated"
    print("Done Load Testing")
Exemple #4
0
def test_creation():
    print("Create Router Test")
    sys = ActorSystem("tests")
    kwargs = {'name': 'test_router'}
    args = []
    rr = RoundRobinRouter(*args, **kwargs)
    rr.start()
    assert (rr.get_name() == 'test_router')
    rr.set_actor_system(sys, "tests")
    sys.close()
    assert (rr.get_state() == ActorState.TERMINATED), "Router Not Terminated"
    print("Completed Router Creation Test")
Exemple #5
0
    def create_router(self, concurrency):
        """
        Create the initial router with existing actors.

        :param concurrency: Number of routers to start
        :type concurrency: int()
        """
        if self.router is None:
            self.router = RoundRobinRouter()
            self.router.start()
            for i in range(0, concurrency):
                try:
                    pyname = __name__
                    router_name = "{}_{}".format(pyname, i)
                    ta = TaskActor(name=router_name, on_call=self.on_pull,
                                   loop=self.loop)
                    ta.start()
                    self.router.add_actor(ta)
                except Exception:
                    self.handle_fail()
Exemple #6
0
class NodePubSub(PubSub):
    """
    A standard graph stage pub sub.  This pub sub receives pull requests which
    wait on a push to then complete a task and send back to the sender.
    Non-blocking io should assure maximal concurrency.
    """

    def __init__(self, name, providers=[], loop=asyncio.get_event_loop(),
                 address=None, mailbox_size=1000, inbox=None,
                 empty_demand_logic = "broadcast", concurrency=cpu_count(),
                 tick_delay=120):
        """
        Constructor

        :param name: Name of the actor
        :type name: str()
        :param loop: Asyncio loop for the actor
        :type loop: AbstractEventLoop()
        :param address: Address for the actor
        :type address: str()
        :param mailbox_size: Size of the mailbox
        :type mailbox_size: int)
        :param inbox: Actor inbox
        :type inbox: asyncio.Queue()
        :param empty_demand_logic: round_robin or broadcast
        :type empty_demand_logic: str()
        :param concurrency: Number concurrent tasks to run
        :type concurrency: int()
        """
        super().__init__(name, loop, address, mailbox_size, inbox)
        self.register_handler(Tick, self.__pull_tick)
        self.subscribers = []
        self.__providers = providers
        self.__current_provider = 0
        self.__task_q = PyQueue()
        self.__empty_logic = empty_demand_logic
        self.__result_q = PyQueue()
        self.router = None
        self.create_router(concurrency)
        self.set_handlers()
        self.tick_delay = tick_delay
        self.__concurrency = concurrency
        self.__pull_tick()

    def set_handlers(self):
        """
        Set the handlers for the actor
        """
        self.register_handler(Publish, self.__push)
        self.register_handler(Push, self.__push)
        self.register_handler(Pull, self.__pull)
        self.register_handler(Tick, self.__do_pull_tick)
        self.register_handler(DeSubscribe, self.__de_subscribe_upstream)
        self.register_handler(Subscribe, self.__subscribe_upstream)
        self.register_handler(TaskMessage, self.__append_result)

    def create_router(self, concurrency):
        """
        Create the initial router with existing actors.

        :param concurrency: Number of routers to start
        :type concurrency: int()
        """
        if self.router is None:
            self.router = RoundRobinRouter()
            self.router.start()
            for i in range(0, concurrency):
                try:
                    pyname = __name__
                    router_name = "{}_{}".format(pyname, i)
                    ta = TaskActor(name=router_name, on_call=self.on_pull,
                                   loop=self.loop)
                    ta.start()
                    self.router.add_actor(ta)
                except Exception:
                    self.handle_fail()

    def start(self):
        """
        Start the actor  
        """
        super().start()
        if len(self.__providers) > 0:
            for i in range(0, self.__concurrency):
                if self.__empty_logic == "broadcast":
                    for provider in self.__providers:
                        self.loop.run_until_complete(
                            self.tell(provider, Pull(None, self)))
                else:
                    if len(self.__providers) > i:
                        if i < len(self.__providers):
                            lprv = len(self.__providers)
                            if self.__current_provider >= lprv:
                                self.__current_provider = 0
                            prov = self.__providers[self.__current_provider]
                            self.loop.run_until_complete(
                                self.tell(prov, Pull(None, self)))
                            self.__current_provider += 1

    def call_tick(self):
        """
        Perform a tick call.
        """
        def handle_tick():
            """
            Handle the tick
            """
            try:
                asyncio.run_coroutine_threadsafe(
                    self.tell(self, Tick(None, self)), loop=self.loop)
            except Exception:
                self.handle_fail()
        try:
            self.loop.call_later(
                delay = self.tick_delay, callback=handle_tick)
        except Exception:
            self.handle_fail()

    async def __do_pull_tick(self, message=None):
        """
        Do a pull tick

        :param message: The message for the pull tick
        :type message: Message()
        """
        try:
            if self.__task_q.full() is False:
                if self.__providers and len(self.__providers) > 0:
                    for provider in self.__providers:
                        if isinstance(provider, AbstractActor):
                            if provider.get_state() != ActorState.RUNNING:
                                self.__providers.remove(provider)
                    if len(self.__providers) > 0:
                        sender = self.__providers[self.__current_provider]
                        await self.tell(sender,Pull(None, self))
                        self.__current_provider += 1
                        if self.__current_provider >= len(self.__providers):
                            self.__current_provider = 0
                    else:
                        self.__current_provider = 0
                else:
                    self.__current_provider = 0
        except Exception:
            self.handle_fail()
        try:
            if self.get_state() != ActorState.TERMINATED:
                self.call_tick()
        except Exception:
            self.handle_fail()

    def __pull_tick(self):
        """
        Perform a periodic pull.
        """
        self.loop.run_until_complete(self.__do_pull_tick())

    async def __subscribe_upstream(self, message):
        """
        Subscribes to an the upstream publisher

        :param message: The provided Subscribe message
        :type message: Subscribe()
        """
        payload = message.payload
        if isinstance(payload, AbstractActor):
            if payload not in self.subscribers:
                self.subscribers.append(payload)
        else:
            msg = "Can Only Subscribe Object of Abstract Actor to StreamPubSub"
            logging.error(msg)

    async def __de_subscribe_upstream(self, message):
        """
        De-subscribe from the upstream actors.

        :param message:  The DeSubscribe message
        :type message: DeSubscribe() 
        """
        try:
            if isinstance(message, DeSubscribe):
                actor = message.payload
                if isinstance(actor, AbstractActor):
                    if actor in self.subscribers:
                        self.subscribers.remove(actor)
                        if self.__current_provider >= len(self.subscribers):
                            self.__current_provider = 0
        except Exception:
            self.handle_fail()

    async def __append_result(self, message):
        """
        Append an incoming result to the result queue.

        :param message:  The incoming message
        :type message:  Message()
        """
        try:
            if isinstance(message, TaskMessage):
                payload = message.payload
                self.__result_q.put_nowait(payload)
        except Exception:
            self.handle_fail()

    async def __pull(self, message):
        """
        The pull message.

        :param message: The Pull message
        :type message: Pull()
        """
        try:
            await self.__signal_provider()
            if isinstance(message, Pull):
                if self.__task_q.empty():
                    self.run_on_empty()
                task = self.__task_q.get()
                if task:
                    if isinstance(task, Push):
                        task = task.payload
                    await self.tell(
                        self.router, RouteTell(TaskMessage(task, message.sender, self)))
        except Exception:
            self.handle_fail()

    async def __signal_provider(self):
        """
        Signal the provider.
        """
        try:
            prov = self.__providers[self.__current_provider]
            await self.tell(prov, Pull(None, self))
        except Exception as e:
            self.handle_fail()
        self.__current_provider += 1
        if self.__current_provider >= len(self.__providers):
            self.__current_provider  = 0

    def on_pull(self, message):
        """
        User implemented function to handle incoming methods.
        """
        logging.error("Should Override Push Function")
        return None

    async def __push(self, message):
        """
        The push function.
        """
        if isinstance(message, Publish):
            self.__task_q.put_nowait(message.payload)
Exemple #7
0
def test_round_robin_broadcast():
    print("Testing Broadcast")
    sys = ActorSystem("tests")
    rr = RoundRobinRouter("test_router")
    rr.start()
    rr.set_actor_system(sys, "tests")

    a = StringTestActor()
    a.start()
    rr.add_actor(a)

    b = StringTestActor()
    b.start()
    rr.add_actor(b)

    asyncio.get_event_loop().run_until_complete(
        rr.broadcast(StringMessage("Hello World!")))
    msg = "Actors Missing. Length {}".format(rr.get_num_actors())
    assert(rr.get_num_actors() is 2), msg

    asyncio.get_event_loop().run_until_complete(
        rr.route_tell(StringMessage("Hello World")))
    sys.close()
    assert(a.get_state() is ActorState.TERMINATED), "Actor a Not Terminated"
    assert(b.get_state() is ActorState.TERMINATED), " Actor b Not Terminated"
    assert(rr.get_state() is ActorState.TERMINATED), "Router Not Terminated"
    print("Finished Testing Broadcast")
Exemple #8
0
def test_round_robin_arithmetic():
    print("Testing multiplication")
    a_sys = ActorSystem("tests")
    rr = RoundRobinRouter("test_router")
    rr.start()
    rr.set_actor_system(a_sys, "tests")

    a = AddTestActor("testa")
    a.start()
    rr.add_actor(a)

    b = AddTestActor("testb")
    b.start()
    rr.add_actor(b)

    async def get_addition():
        res = await rr.route_ask(AddIntMessage(1))
        return res

    res = asyncio.get_event_loop().run_until_complete(get_addition())
    print(rr.get_current_index())
    assert(res is 2), "Addition Not Completed"
    msg = "Actors Missing. Length {}".format(rr.get_num_actors())
    assert(rr.get_num_actors() is 2), msg
    a_sys.close()

    assert(a.get_state() is ActorState.TERMINATED), "Actor a Not Terminated"
    assert(b.get_state(), ActorState.TERMINATED)," Actor b Not Terminated"
    assert(rr.get_state(), ActorState.TERMINATED), "Router Not Terminated"
    print("Done Testing Multiplication")
Exemple #9
0
def test_round_robin_actor_addition():
    print("Starting Actor Addition Test")
    sys = ActorSystem("tests")
    rr = RoundRobinRouter("test_router")
    rr.start()
    rr.set_actor_system(sys, "tests")

    a = BaseActor("testa")
    a.start()
    rr.add_actor(a)

    b = BaseActor("testb")
    b.start()
    rr.add_actor(b)
    msg = "Actors Missing. Length {}".format(rr.get_num_actors())
    assert(rr.get_num_actors() == 2), msg
    rr.remove_actor(a)
    assert(rr.get_num_actors() == 1), "Number of Actors Should be 1"
    asyncio.get_event_loop().run_until_complete(a.stop())
    sys.close()
    assert(a.get_state() == ActorState.TERMINATED), "Actor a Not Terminated"
    assert(b.get_state() == ActorState.TERMINATED), " Actor b Not Terminated"
    assert(rr.get_state() == ActorState.TERMINATED), "Router Not Terminated"
    print("Actor Addition Test Complete")
Exemple #10
0
class PubSub(BaseActor):
    """
    Special Publisher/Subscriber for streaming
    """
    def __init__(self,
                 name,
                 loop=asyncio.get_event_loop(),
                 address=None,
                 mailbox_size=1000,
                 inbox=None):
        """
        The constructor

        :Keyword Arguments:
            *publisher (BaseActor): The publishing actor
            *router (BaseActor): a router for subscribers
        """
        super().__init__(name, loop, address, mailbox_size, inbox)
        self._subscription_router = RoundRobinRouter()

        if self._subscription_router.get_state() is ActorState.LIMBO:
            self._subscription_router.start()

        if self._subscription_router.get_state() is not ActorState.RUNNING:
            raise ActorStateError("Router not Started in PubSub")

        self.register_handler(Subscribe, self.subscribe)
        self.register_handler(Publish, self.do_publish)
        self.register_handler(DeSubscribe, self.desubscribe)

    def subscribe(self, actor):
        """
        Subscribe to the pub/sub.  This will replace the subscriber queue to
        create a balancing router.
        """
        try:
            self._subscription_router.add_actor(actor)
        except Exception:
            self.handle_fail()

    async def desubscribe(self, message):
        try:
            await self.tell(self._subscription_router, message)
        except Exception:
            self.handle_fail()

    def broadcast(self, message):
        """
        Send a broadcast to all members of the publishers router
        """
        try:
            self.loop.run_until_complete(
                self.tell(self._subscription_router, RouteBroadcast(message)))
        except Exception:
            self.handle_fail()

    async def do_publish(self, message):
        """
        Submit a message to a chosen actor in the subscribers
        """
        try:
            await self.tell(self._subscription_router,
                            RouteTell(message.payload))
        except Exception:
            self.handle_fail()

    async def handle_broadcast(self, message):
        """
        Broadcast to all actors.  Do not connect the actors subscribing here
        to a broadcast router.
        """
        try:
            for actor in self._subscribers:
                await self.tell(actor, message)
        except Exception:
            self.handle_fail()