赖勇浩(http://laiyonghoa.com )
注:前几天 GAE SDK 1.5.1 发布,其中一个新特性是 Python SDK 增加了 ProtoRPC API,我对 GAE 兴趣不大,但最近正好自己也在写基于 google protobuf 的 RPC(不同的是我的 RPC 基于 TCP 的),所以很有兴趣地看了一下 ProtoRPC 的 overview,后来心血来潮就把它简单译了一下,不过不是逐句对译,所以如有困惑,敬请参详原文(原文也在变化之中,我译的是 2011 年 6 月 25 日的版本,如果后来更新了,对不上号,概不负责):http://code.google.com/appengine/docs/python/tools/protorpc/overview.html
ProtoRPC 发送和接收基于 HTTP 的远程过程调用(RPC)服务的简单方案。所谓 RPC 服务就是提供结构化地扩展应用程序与 Web 应用交互的一堆消息类型和远程方法。因为只能够用 Python 编程语言来定义消息和服务,所以更容易开发服务、测试服务和在 App Engine 上获得更好的伸缩性。
当把 ProtoRPC 用以任何基于 HTTP 的 RPC 时,一些常见的应用场景包括:
可以在单一 Python 类中定义任意数量的 ProtoRPC 远程方法,每个远程方法接受一个请求(包含一些特定的参数集合),并返回一个特定的响应。所有的请求和响应都是用户定义的类,这些类又称为消息。
实验性的!
ProtoRPC 是一个实验性的、全新的、快速变化的 App Engine 的新特性,请勿用于商业关键的应用程序,当它完全可用时,我们会告诉大家的。
这个小节演示一下从远端接受一个消息(包含了用户名 HelloRequest.my_name),并返回一个响应(HelloResponse.hello)。
在这里我们使用 App Engine Getting Started Guide (Python) 的留言板示例(http://code.google.com/appengine/docs/python/gettingstarted/)。用户可以在线访问这个留言板(已经包含在 Python SDK 中),编写留言,查看所有用户的留言。
接下来将把 ProtoRPC 应用到这个基本的留言板中,使得 Web 应用能够存取其数据。但这个教程只涵盖了使用 ProtoRPC 来扩展留言板功能,其实你可以做到更多,比如编写一个工具读取用户提交的所有消息或创建一个每天有多少条留言的图表。根据特定的应用,可以随意使用 ProtoRPC,重要的是 ProtoRPC 可以让你随便折腾你的数据。
万事之始,是创建一个名为 postservice.py 的文件,所有存取留言板应用数据的远程方法都在其中实现。
第一步,在应用程序目录下创建一个名为 postservice.py 的文件,它将实现两个方法:一个用以远程提交数据,另一个用以远程获取数据。
消息是 ProtoRPC 的基本数据类型,通过声明一个 Message 继承下来的子类来定义消息,然后定义与消息字段相应的类属性。
留言板服务能够让用户提交一个留言,那么就定义一个消息来表示一个留言吧:
Note 消息定义了两个字段,text 和 when。每一个字段都有自己的类型,比如 text 字段的类型提 unicode 字符串,用以表示用户通过留言板页面提交的内容,而 when 字段就是一个整数,用以表示用户提交的时间戳。除此之外:
可以通过 Note 类的构建函数来给字段赋值:
也可以操作普通的 Python 属性一样读、写字段,比如下面的代码可以改变消息:
一个服务就是指一个从 Service 基类继承的类,服务的远程方法由 remote 装饰器标识。服务的每个方法都接受单个消息作为参数,并返回一个消息作为响应。
现在来定义 PostService 的第一个方法。如果你还没有准备好,记得先在你应用目录下创建 postservice.py 文件,或如果你觉得有必要的话就读一下留言板教程(http://code.google.com/appengine/docs/python/gettingstarted/)。PostService 与留言板教程一样,使用 guestbook.Greeting 来存储提交的数据。
remote 装饰器有两个参数:
因为 Note.when 是一个可选字段,所以调用方可能并没有对它赋值,这时 when 的值就是 None,当 Note.when 的值为 None,post_note() 以接收到消息的时间作为时间戳。
响应消息由远程方法实例化,一般是远程方法需要返回的时候会这样做。
可以使用 App Engine 的 webapp 框架(http://code.google.com/appengine/docs/python/tools/webapp/)发布新的服务。ProtoRPC 有一个很小的库(protorpc.service_handlers)可以简化这个事儿。在应用目录新建一个 services.py 的文件,把下面的代码拷进去:
然后把下面的代码加到 app.yaml 文件中:
可以创建你的基本 webapp 的服务啦~
创建服务后,可以使用 curl 或相似的命令行工具进行测试:
当返回一个空的 JSON 表示留言提交成功,可以通过浏览器(http://localhost:8080/)查看这个留言。
现在可以向 PostService 提交留言了,接下来再增加一个新的方法。先在 postservice.py 中定义一个请求消息,它有一些默认值,还有之前没有接触过的枚举字段(用来告诉服务器如何对留言排序)。让我们把下面的代码加到 PostService 类之前:
消息中 limit 字段表示最大的请求的留言数量,默认为 10 条(通过 default=10 关键字参数指定)。
order 字段引入了一个 EnumField 类,它能够让 enum 字段类型的取值严格地限定在已定义的符号值范围内。在这里,服务器如何排序显示中的留言是由 order 字段指定的。要定义枚举值,需要创建 Enum 类的子类,它的每一个类属性都应为唯一的数字,然后被转换为一个可以通过类来存取的枚举类型的实例。
除了访问它的 name 和 number 属性,enum 值还有一个“特殊技能”能够更方便地转换它的 name 和 number,比如转换某个值到字符串或整数:
枚举字段的声明与其它字段是类似的,除了需要在第一个参数标明它的枚举类型,而且枚举字段也可以有默认值。
现在定义一下 get_notes() 的响应消息。这个响应显然应该包含一组 Note 消息,消息能够包含其它的消:
Notes.notes 字段是一个重复字段(通过 repeated=True 关键字参数说明),重复字段的值是一个列表,在这个例子里,Notes.notes 就是包含多个 Note 实例的列表,列表是自动创建的,并且不能赋值为 None。
来个如何创建 Notes 对象的例子:
现在把 get_notes() 方法加到 PostService 类中: