Как использовать прокси-объект Pyro в качестве фабрики?
Я хочу использовать Pyro с существующим набором классов, которые включают шаблон фабрики, то есть объект класса A (как правило, будет только один из них) используется для создания экземпляров объектов класса B (их может быть произвольное число через заводской метод. Итак, я выставляю объект класса A как объект прокси Pyro.
Я расширил вводный пример кода Pyro, чтобы примерно отразить то, что я пытаюсь сделать. Код на стороне сервера выглядит следующим образом:
# saved as greeting.py
import Pyro4
import socket
class NewObj:
func_count = None
def __init__(self):
print "{0} ctor".format(self)
func_count = 0
def __del__(self):
print "{0} dtor".format(self)
def func(self):
print "{0} func call {1}".format(self, self.func_count)
self.func_count += 1
class GreetingMaker(object):
def __init__(self):
print "{0} ctor".format(self)
def __del__(self):
print "{0} dtor".format(self)
def get_fortune(self, name):
print "getting fortune"
return "Hello, {0}. Here is your fortune message:\n" \
"Behold the warranty -- the bold print giveth and the fine print taketh away.".format(name)
def make_obj(self):
return NewObj()
greeting_maker=GreetingMaker()
daemon=Pyro4.Daemon(host=socket.gethostbyname(socket.gethostname()), port=8080) # make a Pyro daemon
uri=daemon.register(greeting_maker, "foo") # register the greeting object as a Pyro object
print "Ready. Object uri =", uri # print the uri so we can use it in the client later
daemon.requestLoop() # start the event loop of the server to wait for calls
Код на стороне клиента также был немного изменен:
# saved as client.py
import Pyro4
uri="PYRO:foo@10.2.129.6:8080"
name="foo"
greeting_maker=Pyro4.Proxy(uri) # get a Pyro proxy to the greeting object
print greeting_maker.get_fortune(name) # call method normally
print greeting_maker.make_obj()
Мое намерение состоит в том, чтобы иметь возможность создавать экземпляры NewObj
и манипулировать ими так же, как я могу манипулировать экземплярами GreetingMaker
на стороне клиента, но похоже, что происходит, когда make_obj
метод вызывается, NewObj
создается на стороне сервера, немедленно выпадает из области видимости и, следовательно, подвергается сборке мусора.
Вот как выглядит вывод на стороне сервера:
<__main__.GreetingMaker object at 0x2aed47e01110> ctor
/usr/lib/python2.6/site-packages/Pyro4-4.12-py2.6.egg/Pyro4/core.py:152: UserWarning: HMAC_KEY not set, protocol data may not be secure
warnings.warn("HMAC_KEY not set, protocol data may not be secure")
Ready. Object uri = PYRO:foo@10.2.129.6:8080
getting fortune
<__main__.NewObj instance at 0x175c8098> ctor
<__main__.NewObj instance at 0x175c8098> dtor
... и на стороне клиента:
/usr/local/lib/python2.6/dist-packages/Pyro4-4.12-py2.6.egg/Pyro4/core.py:152: UserWarning: HMAC_KEY not set, protocol data may not be secure
warnings.warn("HMAC_KEY not set, protocol data may not be secure")
Hello, foo. Here is your fortune message:
Behold the warranty -- the bold print giveth and the fine print taketh away.
Traceback (most recent call last):
File "client.py", line 9, in <module>
print greeting_maker.make_obj()
File "/usr/local/lib/python2.6/dist-packages/Pyro4-4.12-py2.6.egg/Pyro4/core.py", line 146, in __call__
return self.__send(self.__name, args, kwargs)
File "/usr/local/lib/python2.6/dist-packages/Pyro4-4.12-py2.6.egg/Pyro4/core.py", line 269, in _pyroInvoke
data=self._pyroSerializer.deserialize(data, compressed=flags & MessageFactory.FLAGS_COMPRESSED)
File "/usr/local/lib/python2.6/dist-packages/Pyro4-4.12-py2.6.egg/Pyro4/util.py", line 146, in deserialize
return self.pickle.loads(data)
AttributeError: 'module' object has no attribute 'NewObj'
Я подозреваю, что я мог бы решить эту проблему, имея фабричный класс (т.е. GreetingMaker
) сохранить ссылку на каждый NewObj
что он создает и добавляет какой-то метод очистки... но действительно ли это необходимо? Я что-то упускаю в Pyro, который может помочь мне реализовать это?
(отредактировано для ясности)
2 ответа
Я недавно сталкивался с этой функцией и использую ее. Это важно для моего кода, который использует аналогичный шаблон фабрики.
Пиро сервер
class Foo(object):
def __init__(self, x=5):
self.x = x
class Server(object):
def build_foo(self, x=5):
foo = Foo(x)
# This line will register your foo instance as its own proxy
self._pyroDaemon.register(foo)
# Returning foo here returns the proxy, not the actual foo
return foo
#...
uri = daemon.register(Server()) # In the later versions, just use Server, not Server()
#...
Проблема здесь в том, что pyro
соленья NewObj
объект на стороне сервера, но он не может распаковать его на стороне клиента, потому что NewObj
реализация неизвестна клиенту.
Один из способов решения проблемы - создать третий модуль, например, с именем new_obj.py
и после этого импортируйте его как на сервер, так и на клиент следующим образом:
from new_obj import NewObj
Это позволит клиенту раскрыть NewObj
экземпляр и работа с ним. В любом случае, обратите внимание, что это будет настоящий NewObj
объект, живущий в клиенте, а не прокси для объекта, живущего на сервере.