以小见大——那些基于 protobuf 的五花八门的 RPC(5 完)

燕志学
2023-12-01
               

赖勇浩(http://laiyonghao.com)

快刀斩乱麻,祭上最后两个 rpc 分析,再整上我自己的设计,这个系列就完结了。

protobuf-socket-rpc

好,废话不多说,看看这个 protobuf-socket-rpc,简介是 Java and Python protobuf rpc implementation using tcp/ip sockets,听说支持 java 的服务器和 python 的客户端,有点怪怪地哈?基本格式见:http://code.google.com/p/protobuf-socket-rpc/source/browse/trunk/proto/rpc.proto ,引用全文如下:
package protobuf.socketrpc;option java_package = "com.googlecode.protobuf.socketrpc";option java_outer_classname = "SocketRpcProtos";message Request {  // RPC service full name  required string service_name = 1;   // RPC method name  required string method_name = 2;   // RPC request proto  required bytes request_proto = 3;}message Response {  // RPC response proto  optional bytes response_proto = 1;   // Error, if any  optional string error = 2;   // Was callback invoked  optional bool callback = 3 [default = false];   // Error Reason  optional ErrorReason error_reason = 4;}// Possible error reasons// The server-side errors are returned in the response from the server.// The client-side errors are returned by the client-side code when it doesn't// have a response from the server.enum ErrorReason {  // Server-side errors  BAD_REQUEST_DATA = 0; // Server received bad request data  BAD_REQUEST_PROTO = 1; // Server received bad request proto  SERVICE_NOT_FOUND = 2; // Service not found on server  METHOD_NOT_FOUND = 3; // Method not found on server  RPC_ERROR = 4; // Rpc threw exception on server  RPC_FAILED = 5; // Rpc failed on server   // Client-side errors (these are returned by the client-side code)  INVALID_REQUEST_PROTO = 6; // Rpc was called with invalid request proto  BAD_RESPONSE_PROTO = 7; // Server returned a bad response proto  UNKNOWN_HOST = 8; // Could not find supplied host  IO_ERROR = 9; // I/O error while communicating with server}
很眼熟啊,是的,跟之前看过的 fepss-rpc 几乎一样,除了这里定义了更多的错误类型,连注释都几乎一模一样啊,不知道谁抄谁的。所以没啥好说的了,唯一可以唠叨一下的是它跟 fepss-rpc 都采用无 ID 的设计,所以不能 parallel pipelining,细节见(http://blog.csdn.net/lanphaday/archive/2011/04/11/6316099.aspx)。

txprotobuf

以 Python 始,再以 Python 终,最后一个是 protocol buffers (http://code.google.com/p/protobuf/) RPC implemented using python twisted framework.,基于 twisted 的 rpc,虽然我不喜欢 twisted,但不影响我看 txprotobuf 哈。它的基本格式见:http://bazaar.launchpad.net/~proppy/txprotobuf/master/view/head:/protobuf/txprotobuf.proto ,照例全文引过来:
// Copyright (c) 2008 Johan Euphrosine// See LICENSE for details.message Call {  required uint32 token = 1;  required string method = 2;  required bytes request = 3;}message Result {  required uint32 token = 1;  required bytes response = 2;}message Box {  optional bytes call = 1;  optional bytes result = 2;}
简洁,跟 casocklib 相似的设计。唯一能够说一下的就是只有 method 字段而没有 service 字段所以没有办法做到 multi-service。另外,Box 这个命名我很喜欢。

我自己的设计

本来想单独作为一篇的,后来发现前两个实在没啥好说啊,干脆就整合在一起得了。好,上代码:
enum PackageType {    HEARTBEAT = 0;    ERROR = 1;    REQUEST = 2;    RESPONSE = 3;};message Heartbeat {};message Error {    required string info = 1;};message Request {    required string service = 1;    required string method = 2;    optional bytes request = 3;};message Response {    optional bytes response = 1;};message Package {    required PackageType type = 1;    required uint32 identify = 2;    required bytes serialized = 3;};message Placeholder {};
首先是学习 server1 定义了几种数据包的类型,不过我扩展了一下,加了心跳和错误这两种包。心跳在这里就是简单的一个空包,只是激活一下 TCP 连接,以免超时断开。如果要复杂一点的,可以参考陈硕《分布式系统的工程化开发方法》 p.51 心跳协议的设计(http://blog.csdn.net/Solstice/archive/2010/10/19/5950190.aspx)。
message Error 很简单,只有一个 info 字段。之前也在想要不要加个数字枚举来让调用端了解一下基本出了啥错,后来我想还是不要了。主要是出错了对于调用端来说没有什么挽救手段啊,只能是往日志里写一句,然后该干啥干啥去,既然如此,又何必多此一举呢,直接把错误的文本从服务器端传过来就好了。
message Request 带了 service 和 method 字段,为的是明晰地表达咱支持 multi-service。而的 request 字段是 optional 的,为的是支持“空”参数调用 RPC。Message Response 的 optional bytes response 也是类似,为了支持“空”返回值。因为 protobuf 的 rpc 声明不支持 rpc XXX()returns() 这样的无参数无返回值的声明,所以我又在后面声明了一个 message Placeholder,这样 rpc 声明就可以写成 rpc XXX(Placeholder)returns(Placeholder)。调用代码时,可以直接写成 proxy.service.XXX() 这样子,由我们自己的 Proxy/Channel 实现去处理好这个占位参数和返回值就行了。
最后,整个连接上传输的数据包就只有一种:message Package,不论是 Heartbeat、Error、Rquest 或 Response,最后都会序列化到 Package.serialized 里去。
对比这么多实现,我的这个版本既没有像 protobuf-remote 般自由开放的调用参数机制,也没有 protobuf-rpc-pro 可以返回超大块数据的内置方案。不过我还是比较满意,太自由和太重量的方案不符合我的技术观。
我打算基于 gevent 实现这个 rpc。
===== 完 ======

           
 类似资料: