分享

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

 9loong 2012-08-07

赖勇浩(http://)

快刀斩乱麻,祭上最后两个 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 ,引用全文如下:

C-sharp代码
  1. package protobuf.socketrpc;  
  2. option java_package = "com.googlecode.protobuf.socketrpc";  
  3. option java_outer_classname = "SocketRpcProtos";  
  4. message Request {  
  5.   // RPC service full name  
  6.   required string service_name = 1;  
  7.    
  8.   // RPC method name  
  9.   required string method_name = 2;  
  10.    
  11.   // RPC request proto  
  12.   required bytes request_proto = 3;  
  13. }  
  14. message Response {  
  15.   // RPC response proto  
  16.   optional bytes response_proto = 1;  
  17.    
  18.   // Error, if any  
  19.   optional string error = 2;  
  20.    
  21.   // Was callback invoked  
  22.   optional bool callback = 3 [default = false];  
  23.    
  24.   // Error Reason  
  25.   optional ErrorReason error_reason = 4;  
  26. }  
  27. // Possible error reasons  
  28. // The server-side errors are returned in the response from the server.  
  29. // The client-side errors are returned by the client-side code when it doesn't  
  30. // have a response from the server.  
  31. enum ErrorReason {  
  32.   // Server-side errors  
  33.   BAD_REQUEST_DATA = 0; // Server received bad request data  
  34.   BAD_REQUEST_PROTO = 1; // Server received bad request proto  
  35.   SERVICE_NOT_FOUND = 2; // Service not found on server  
  36.   METHOD_NOT_FOUND = 3; // Method not found on server  
  37.   RPC_ERROR = 4; // Rpc threw exception on server  
  38.   RPC_FAILED = 5; // Rpc failed on server  
  39.    
  40.   // Client-side errors (these are returned by the client-side code)  
  41.   INVALID_REQUEST_PROTO = 6; // Rpc was called with invalid request proto  
  42.   BAD_RESPONSE_PROTO = 7; // Server returned a bad response proto  
  43.   UNKNOWN_HOST = 8; // Could not find supplied host  
  44.   IO_ERROR = 9; // I/O error while communicating with server  
  45. }  

很眼熟啊,是的,跟之前看过的 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./~proppy/txprotobuf/master/view/head:/protobuf/txprotobuf.proto ,照例全文引过来:

C-sharp代码
  1. // Copyright (c) 2008 Johan Euphrosine  
  2. // See LICENSE for details.  
  3. message Call {  
  4.   required uint32 token = 1;  
  5.   required string method = 2;  
  6.   required bytes request = 3;  
  7. }  
  8. message Result {  
  9.   required uint32 token = 1;  
  10.   required bytes response = 2;  
  11. }  
  12. message Box {  
  13.   optional bytes call = 1;  
  14.   optional bytes result = 2;  
  15. }  

简洁,跟 casocklib 相似的设计。唯一能够说一下的就是只有 method 字段而没有 service 字段所以没有办法做到 multi-service。另外,Box 这个命名我很喜欢。

我自己的设计

本来想单独作为一篇的,后来发现前两个实在没啥好说啊,干脆就整合在一起得了。好,上代码:

C-sharp代码
  1. enum PackageType {  
  2.     HEARTBEAT = 0;  
  3.     ERROR = 1;  
  4.     REQUEST = 2;  
  5.     RESPONSE = 3;  
  6. };  
  7. message Heartbeat {  
  8. };  
  9. message Error {  
  10.     required string info = 1;  
  11. };  
  12. message Request {  
  13.     required string service = 1;  
  14.     required string method = 2;  
  15.     optional bytes request = 3;  
  16. };  
  17. message Response {  
  18.     optional bytes response = 1;  
  19. };  
  20. message Package {  
  21.     required PackageType type = 1;  
  22.     required uint32 identify = 2;  
  23.     required bytes serialized = 3;  
  24. };  
  25. message Placeholder {  
  26. };  

首先是学习 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。
===== 完 ======

(###)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多