Pyro4 分布式计算简单示例

本文的例子来自 Pyro4 官网的 tutorial。这年头,Python的包各种强大,通过Pyro可以轻松进行分布式计算。具体说来,即使被调用方法的对象在远程服务器,Pyro 都可以让我们轻松调用。

+----------+                         +----------+
| server A |                         | server B |
|          |       < network >       |          |
| Python   |                         |   Python |
| OBJECT ----------foo.invoke()--------> OBJECT |
|          |                         |     foo  |
+----------+                         +----------+

概念

  • 代理(Proxy)
    “代理”是真实对象的替代品。通过操作“代理”,好像被调用方法的对象就在本地。尽管实际操作是在远程服务器上完成的,之后结果会返回给本机调用“代理”的程序。
  • URI
  • Pyro对象(Pyro object)
    是经过Pyro登记的Python对象,经过登记就可以在其他服务器上调用它。类也可以是Pyro对象,之后你就可以通过远程调用告诉Pyro如何创建该类的对象了。本文的示例中,是把类作为Pyro对象。
  • Pyro守护进程(Pyro daemon)
    这是 Pyro 负责监听调用的部分,并将调用分配给正确的对象,并将得到的结果返回给调用者。所有的Pyro对象,都应该登记到一个或者多个守护进程上。
  • Pyro命名服务器(Pyro name server)
    命名服务器为Pyro应用提供地址查询簿。远程对象在Pyro中的名字是逻辑名称,通过命名服务器可以得到精确地地址,从而跟远程对象进行交互。
  • 序列化(Serialization)
    就是将对象转换为比特流的过程,便于网络传输。接收方收到后会进行反序列化(deserialize),得到原来的对象。远程调用方法的参数和返回的数据都要进行序列化。注意,并不是所有对象都可以进行序列化,所有可能存在一些情况,本地可以正常调用,而无法通过 Pyro 进行远程调用。序列化的操作,由Pyro实现,我们无需操作。对Python中序列化有兴趣的,请见此文

示例

1. 本地实现

首先在本地实现代码:有一个Warehouse类,它的实例表示某个具体的仓库,负责保管物品;还有一个Person类,它的实例表示某个人,他可以去仓库存取物品。
在这个例子中,Janet 和 Henry要去本地的仓库存取物品。既然仓库在本地,当然就很方便了。

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))
import sys

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!

2. 分布式实现

但是顾客总比商家多,总有些小地方没有被商家覆盖。这该如何是好?
到分布式这个例子上,Janet和Henry两人在A地,他们要去仓库存取物品,但仓库主体都在B地。有Pyro在,就不用他们大老远跑了。首先仓库要拿到异地办理的资质(成为Pyro对象),然后在A地开一个门面(代理)。Jenet和Henry两人通过门面,就可以存取物品了。门面会把相应物品打包装箱(序列化),然后运到B地,再卸车拆包(反序列化)。仓库运营方(Pyro守护进程)会按要存入物品,再将两人要取的物品找出,装箱打包运到A地,再装卸取件。

import Pyro4

@Pyro4.expose
@Pyro4.behavior(instance_mode="single")
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.Daemon.serveSimple(
            {
                Warehouse: "example.warehouse"
            },
            ns = False)

if __name__=="__main__":
    main()

首先在命令行中运行这部分代码,如下:

$ python warehouse.py
Object <__main__.Warehouse object at 0x025F4FF0>:
    uri = PYRO:example.warehouse@localhost:51279
Pyro daemon running.

这部分代码中的 @Pyro4.expose 表示这个类可以进行远程操作,就是仓库要拿到异地办理的执照。因为这里是将一个类注册为Pyro对象,所以要添加@Pyro4.behaviorinstance_mode="single"表示实例被创建后需要负责之后所有的方法调用,参考官方文档
最后使用main方法,将Warehouse类这个Pyro对象登记到了一个守护进程。仓库有公司进行运营,正式开业啦。运行得到的uri,就是仓库的地址啦,后面需要通过这个地址来找它。
函数Person类的代码无需修改。最后来看调用的脚本。

# This is the code that visits the warehouse.
import sys
import Pyro4
from person import Person

if sys.version_info<(3,0):
    input = raw_input

uri = input("Enter the uri of the warehouse: ").strip()
warehouse = Pyro4.Proxy(uri)
janet = Person("Janet")
henry = Person("Henry")
janet.visit(warehouse)
henry.visit(warehouse)

通过warehouse = Pyro4.Proxy(uri),我们得到了Warehouse实例的代理。Pyro4自动创建了对应的实例,并负责后面的调用。就是说,B地仓库在A地有门面了,并由该门面承接A地的业务。
大致做个示意图,见图1。可以看到,在Pyro的帮助下,在计算机A上可以轻松调用计算机B上的对象,只要实现将B上的对象进行一些操作(注册为Pyro对象,登记到守护进程),并把B上对象的uri交给A,并在A上直接把代理当做对象进行调用即可。

图1. Pyro4 图示。除Pyro外,其他红色字体就是前文提到的一些概念。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容