最近在项目中用到一个python RPC框架 Pyro4, 中文文档比较少,在这里记录一下关于学习这个库的理解。
首先什么是RPC,RPC是远程过程调用。在分布式系统中,有两台服务器A,B 。 一个应用部署在A服务器上,想要调用B服务器上应用提供的函数和方法。由于不在一个内存空间里,不能直接调用。需要网络来传递调用的语义和传达调用的数据。
基于上述问题,我们抛出了一个RPC框架需要解决以下五点问题:
关于上述的五点,在Pyro4中已经进行了完善的封装。用户只需要使用Pyro4提供的API就可以轻松的使用Python完成RPC调用。
下面主要记录一下Pyro4中的主要模块(client, server,nameserver,以及Pyro4提供的命令行工具+
)的作用以及以及给出一个实际的分布式环境的实例(相对于官方文档中给出的都在localHost的单机伪分布式示例)。
实际上Pyro4提供了很强大的功能,如果需要详细了解其功能,可以访问
官方文档
我们先以一个本地的普通调用为例,然后将其改写为RPC调用
from __future__ import print_function
class Warehouse(object):
def __init__(self):
self.contents = ["chair", "bike", "flashlight", "laptop", "couch"]
def list_contents(self):
return self.contents
def take(self, name, item):
self.contents.remove(item)
print("{0} took the {1}.".format(name, item))
def store(self, name, item):
self.contents.append(item)
print("{0} stored the {1}.".format(name, item))
from __future__ import print_function
import sys
if sys.version_info < (3, 0):
input = raw_input
class Person(object):
def __init__(self, name):
self.name = name
def visit(self, warehouse):
print("This is {0}.".format(self.name))
self.deposit(warehouse)
self.retrieve(warehouse)
print("Thank you, come again!")
def deposit(self, warehouse):
print("The warehouse contains:", warehouse.list_contents())
item = input("Type a thing you want to store (or empty): ").strip()
if item:
warehouse.store(self.name, item)
def retrieve(self, warehouse):
print("The warehouse contains:", warehouse.list_contents())
item = input("Type something you want to take (or empty): ").strip()
if item:
warehouse.take(self.name, item)
# This is the code that runs this example.
from warehouse import Warehouse
from person import Person
warehouse = Warehouse()
janet = Person("Janet")
henry = Person("Henry")
janet.visit(warehouse)
henry.visit(warehouse)
执行效果如下
$ python visit.py
This is Janet.
The warehouse contains: ['chair', 'bike', 'flashlight', 'laptop', 'couch']
Type a thing you want to store (or empty): television # typed in
Janet stored the television.
The warehouse contains: ['chair', 'bike', 'flashlight', 'laptop', 'couch', 'television']
Type something you want to take (or empty): couch # <-- typed in
Janet took the couch.
Thank you, come again!
This is Henry.
The warehouse contains: ['chair', 'bike', 'flashlight', 'laptop', 'television']
Type a thing you want to store (or empty): bricks # <-- typed in
Henry stored the bricks.
The warehouse contains: ['chair', 'bike', 'flashlight', 'laptop', 'television', 'bricks']
Type something you want to take (or empty): bike # <-- typed in
Henry took the bike.
Thank you, come again!
接下来我们将其改写为分布式的RPC调用,我这里有三台host, ip分别是
其中host1 执行visit.py(同时person.py也在这台机器上),担任RPC 中client的角色。
host2 执行warehouse.py, 担任RPC中server的角色。
host3 执行nameserver.py,担任nameserver的角色。这里要特地说一下nameserver的作用:提供远程调用对象的逻辑名与URI的映射关系,使得client 在进行远程过程调用时只需要指定调用的远程对象的逻辑名称(而不用复杂的URI )和对应的nameserver就可以轻松的完成远程过程调用。
改为RPC调用代码如下:
# This is the code that visits the warehouse.
import sys
import Pyro4
from person import Person
sys.excepthook = Pyro4.util.excepthook
warehouse=Pyro4.Proxy("PYRONAME:example.warehouse")
janet = Person("Janet")
henry = Person("Henry")
janet.visit(warehouse)
henry.visit(warehouse)
person.py 无改动,同上
from __future__ import print_function
import Pyro4
#import person
@Pyro4.expose
class Warehouse(object):
def __init__(self):
self.contents = ["chair", "bike", "flashlight", "laptop", "couch"]
def list_contents(self):
return self.contents
def take(self, name, item):
self.contents.remove(item)
print("{0} took the {1}.".format(name, item))
def store(self, name, item):
self.contents.append(item)
print("{0} stored the {1}.".format(name, item))
def main():
Pyro4.naming.locateNS(host='10.133.1.221')
Pyro4.reDaemon.serveSimple(
{
Warehouse: "example.warehouse"
},
ns = True,
host = '10.133.1.220'
)
if __name__=="__main__":
main()
~
import Pyro4
Pyro4.naming.startNSloop(host='10.133.1.221')
首先执行nameserver.py,
使用命令行工具在执行nameserver.py的这台机器上查看
wxl@dev:~$ pyro4-nsc ping
Name server ping ok.
发现此时nameserver已经成功运行。
再执行
wxl@dev:~$ pyro4-nsc list
--------START LIST
Pyro.NameServer --> PYRO:Pyro.NameServer@10.133.1.221:9090
metadata: [u'class:Pyro4.naming.NameServer']
--------END LIST
发现此时nameserver上没有远程过程调用对象。
在host2上启动warehouse.py
再次在nameserver上查看发现:
wxl@dev:~$ pyro4-nsc list
--------START LIST
Pyro.NameServer --> PYRO:Pyro.NameServer@10.133.1.221:9090
metadata: [u'class:Pyro4.naming.NameServer']
example.warehouse --> PYRO:obj_cc4b9c4bf9a148ca9cf2f1445e578a75@10.133.1.220:36178
--------END LIST
已经注册到这台nameserver上了。
此时host1执行visit.py,发现可以调用远程warehouse对象