Remote Tutorial¶
This page explains hot to use PyActor for remote communications between machines.
Sample 1 - Basic communication¶
This example shows the basis on setting a remote communication and sending tell
requests. This is the full code of this sample, which you can find and test in
pyactor\examples\Remote\s1_server.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | """
Basic remote example sending tell messages. SERVER
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, serve_forever
class Echo(object):
_tell = {'echo'}
def echo(self, msg):
print(msg)
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:1277/")
e1 = host.spawn('echo1', Echo)
serve_forever()
|
And pyactor\examples\Remote\s1_client.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | """
Basic remote example sending tell messages. CLIENT
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, shutdown
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:1679")
e1 = host.lookup_url("http://127.0.0.1:1277/echo1", 'Echo', 's1_server')
e1.echo("Hi there!") # TELL message
e1.echo("See ya!")
shutdown()
|
To create a host able to communicate with other machines, simply use as its URL one with an http scheme, as in the example. Using the http scheme will create a dispatcher on that host that will manage the queries through xml.
So, the server spawns an actor at 127.0.0.1:1277
and the client is able to
look for that actor just giving that IP:port and path. If the client does not
have the Class it is looking for, it must provide the module and the name of
that class when calling the lookup method as shown.
Then, the calls are used as usual.
In s1_clientb.py
we have the same code but the calls are repeated 1000
times.
Sample 2 - Basic communication 2¶
This example extends the first by adding ask requests. This is the full code of
this sample, which you can find and test in
pyactor\examples\Remote\s2_server.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | """
Basic remote example sending ask messages. SERVER
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, serve_forever
class Echo(object):
_tell = {'echo'}
_ask = {'get_msgs'}
def __init__(self):
self.msgs = []
def echo(self, msg):
print(msg)
self.msgs.append(msg)
def get_msgs(self):
return self.msgs
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:1277/")
e1 = host.spawn('echo1', Echo)
serve_forever()
|
And pyactor\examples\Remote\s2_client.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | """
Basic remote example sending ask messages. CLIENT
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, shutdown
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:1679")
e1 = host.lookup_url("http://127.0.0.1:1277/echo1", 'Echo', 's2_server')
e1.echo('Hi there!') # TELL message
e1.echo('See ya!')
print(e1.get_msgs())
shutdown()
|
This sample is like the previous one, but it includes examples of ask methods. As the tell methods, they are used as normally, like in the local examples.
Sample 3 - Remote spawning¶
This example shows how to spawn an actor in another host. This is the full
code of this sample, which you can find and test in
pyactor\examples\Remote\s3_host.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | """
Remote example spawning on a remote server. SERVER
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, serve_forever
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:1277/")
print("host listening at port 1277")
serve_forever()
|
And pyactor\examples\Remote\s3_client.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | """
Remote example spawning on a remote server. CLIENT
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, Host, sleep, shutdown
from pyactor.exceptions import PyActorTimeoutError
class Server(object):
_ask = {'add', 'wait_a_lot'}
_tell = {'substract'}
def add(self, x, y):
return x + y
def substract(self, x, y):
print("subtract", x - y)
def wait_a_lot(self):
sleep(2)
return "ok"
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:1679")
remote_host = host.lookup_url("http://127.0.0.1:1277/", Host)
print(remote_host)
server = remote_host.spawn('server', 's3_client/Server')
z = server.add(6, 7)
print(z)
server.subtract(6, 5)
t = server.add(8, 7)
print(t)
try:
print(server.wait_a_lot(timeout=1))
except PyActorTimeoutError as e:
print(e)
sleep(3)
shutdown()
|
In this case the server part only creates its host and makes it serve forever
(serve_forever()
). The client is the one that uses lookup_url()
to get the server reference and spawn an actor in it. Then, sends the work to
the actor. To spawn the actor, as the class of it is defined in the client
module, the method uses a string to define where is the Class so the server
can import it. This string uses the form module/class_name
:
server = remote_host.spawn('server', 's3_client/Server')
Sample 4 - Registry example¶
Here we have a basic example of a registry where some servers can bind to so the
clients are able to see all the servers available and connect to one. This is
the full code of this sample, which you can find and test in
pyactor\examples\Remote\s4_registry.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | """
Remote example with a registry. SERVER
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, serve_forever
class NotFound(Exception):
pass
class Registry(object):
_ask = {'get_all', 'bind', 'lookup', 'unbind'}
_ref = {'get_all', 'bind', 'lookup'}
def __init__(self):
self.actors = {}
def bind(self, name, actor):
print("server registred", name)
self.actors[name] = actor
def unbind(self, name):
if name in self.actors.keys():
del self.actors[name]
else:
raise NotFound()
def lookup(self, name):
if name in self.actors:
return self.actors[name]
else:
return None
def get_all(self):
return self.actors.values()
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:6000/")
registry = host.spawn('regis', Registry)
print("host listening at port 6000")
serve_forever()
|
And pyactor\examples\Remote\s4_client.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | """
Remote example with registry. CLIENT
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, serve_forever
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:6001")
registry = host.lookup_url("http://127.0.0.1:6000/regis", 'Registry',
's4_registry')
registry.bind('host1', host)
serve_forever()
|
And pyactor\examples\Remote\s4_clientb.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | """
Remote example with registry. CLIENT 2
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, sleep, shutdown
from s4_registry import NotFound
class Server(object):
_ask = {'add', 'wait_a_lot'}
_tell = {'subtract'}
def add(self, x, y):
return x + y
def subtract(self, x, y):
print("subtract", x - y)
def wait_a_lot(self):
sleep(2)
return "ok"
if __name__ == '__main__':
set_context()
host = create_host("http://127.0.0.1:6002")
registry = host.lookup_url("http://127.0.0.1:6000/regis", 'Registry',
's4_registry')
remote_host = registry.lookup('host1')
if remote_host is not None:
if not remote_host.has_actor('server'):
server = remote_host.spawn('server', 's4_clientb/Server')
else:
server = remote_host.lookup('server')
z = server.add(6, 7)
print(z)
server.subtract(6, 5)
t = server.add(8, 7)
print(t)
try:
registry.unbind('None')
except NotFound:
print("Cannot unbind this object: is not in the registry.")
shutdown()
|
In this example we have a registry where Servers can be bound. The registry module starts an actor which is the registry itself to which servers can be bound and clients look for servers. The first client binds its host to the registry and waits. The second one uses the registry to find the first’s host and spawn a server on it. Then, send work to that server.
In order to execute the second client repeatedly without having to restart
all the processes, before spawning the server remotely, it checks if the first
client has already the server by using the method has_actor
on the
remote_host.
Sample 5 - Multiple Hosts¶
This example tests the creation of multiple host at the same time on one unique
execution. This is the full code of this sample, which you can find and test in
pyactor\examples\Remote\sample5.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | """
Multiple hosts. Remote required since v0.9.
@author: Daniel Barcelona Pons
"""
from pyactor.context import set_context, create_host, sleep, shutdown
class Echo(object):
_tell = {'echo'}
_ref = {'echo'}
def echo(self, msg, pref=None):
print(msg, pref)
if __name__ == '__main__':
set_context()
h = create_host("http://127.0.0.1:6666/host")
e1 = h.spawn('echo1', Echo)
e1.echo("hello there !!", e1)
h2 = create_host("http://127.0.0.1:7777/host")
e2 = h2.spawn('echo1', Echo)
e2.echo("hello 2", e1)
sleep(1)
e1.echo("hello 3", e2)
sleep(1)
shutdown()
# or, to only stop one of them:
# shutdown("http://127.0.0.1:7777/host")
|
The first thing to make clear is that you should never need to create more than one host locally, since they are meant for remote communication. This is for testing purposes.
To create more hosts, you only need to call again the function
create_host()
. But you will need to specify different locations for
each host, since those are their identifiers. In the example we create two
hosts in the same location, but attending different ports:
h = create_host("http://127.0.0.1:6666/host")
h2 = create_host("http://127.0.0.1:7777/host")
Note
Remember that the default address for a host is
local://local:6666/host
Note
To communicate two hosts, both of them must have a remote dispatcher, so they must have one of the schemes required.
Now, each host will manage its own actors and threads, so they will need to communicate through TCP connections.
One thing important to know about this is that only one host can be used to manage the main execution of your program, so there always will be a main host and the other ones will be created as secondary hosts.
This main host will be automatically assigned to the first one created. If that one is closed and there still are other hosts operative, the oldest of them will assume the role of main host.
Using RabbitMQ¶
Unmaintained Only works on a single machine with multiple hosts and needs the rabbit server running locally.
This library also supports the usage of communication through RabbitMQ queues. To use this approach, simply define the hosts with an URL with the scheme amqp instead of http. This will create a dispatcher for that host that works with RabbitMQ, and all its actors will work at that scheme.
You can see an example with pyactor\examples\Remote\s1_clientrbb.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | """
Basic remote example sending tell messages. CLIENT
@author: Daniel Barcelona Pons
"""
from pyactor.context import \
set_context, create_host, set_rabbit_credentials, shutdown
if __name__ == '__main__':
set_rabbit_credentials('daniel', 'passs')
set_context()
host = create_host("amqp://127.0.0.1:1679")
e1 = host.lookup_url("amqp://127.0.0.1:1277/echo1", 'Echo', 's1_server')
e1.echo("Hi there!") # TELL message
e1.echo("See ya!")
shutdown()
|
and pyactor\examples\Remote\s1_serverrbb.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | """
Basic remote example sending tell messages. SERVER
@author: Daniel Barcelona Pons
"""
from pyactor.context import \
set_context, create_host, set_rabbit_credentials, serve_forever
class Echo(object):
_tell = {'echo'}
def echo(self, msg):
print(msg)
if __name__ == '__main__':
# set_rabbit_credentials('daniel', 'passs')
set_context()
host = create_host("amqp://127.0.0.1:1277/")
e1 = host.spawn('echo1', Echo)
serve_forever()
|
You can configure your rabbit credentials with:
setRabbitCredentials('user', 'password')
If you don’t, it will use the default Rabbit guest user, which only can connect locally.