RPC框架系列——Protocol Buffers

http://blog.jeoygin.org/2011/09/rpc-framework-protocol-buffers.html

1.下载与安装

  官方网站:http://code.google.com/p/protobuf/

  下载地址:http://protobuf.googlecode.com/files/protobuf-2.4.1.tar.bz2

  protocol buffers并没有实现RPC通信,可以使用第三方的RPC实现protobuf-socket-rpc,下载地址是:http://protobuf-socket-rpc.googlecode.com/files/protobuf-socket-rpc-2.0.jar

  1. cd /usr/local/src
  2. wget http://protobuf.googlecode.com/files/protobuf-2.4.1.tar.bz2
  3. tar jxvf protobuf-2.4.1.tar.bz2
  4. cd protobuf-2.4.1
  5. ./configure
  6. make
  7. make check
  8. make install

  下面将编译生成jar包,以便在java中使用Protocol Buffers,需确保已安装了maven。

  1. cd java
  2. mvn test
  3. mvn install
  4. mvn package

  安装、编译后在target/目录下会生成protobuf-java-2.4.1.jar。

2.消息结构与服务接口

  首先需要编写一个.proto文件,结构化数据被称为Message。

  1. package protobuf;
  2.  
  3. option java_package = "protobuf";
  4. option java_outer_classname = "PersonProtos";
  5. option java_generic_services = true;
  6.  
  7. message Person {
  8.     required string name = 1;
  9.     required int32 id = 2;
  10.     optional string email = 3;
  11.  
  12.     enum PhoneType {
  13.         MOBILE = 0;
  14.         HOME = 1;
  15.         WORK = 2;
  16.     }
  17.  
  18.     message PhoneNumber {
  19.         required string number = 1;
  20.         optional PhoneType type = 2 [default = HOME];
  21.     }
  22.  
  23.     repeated PhoneNumber phone = 4;
  24.  
  25.     service PhoneService {
  26.         rpc GetPhone (Phone) returns (Phone);
  27.     }
  28. }

  消息的成员需要指定其规则:

    (1) required:这个域在消息中必须刚好有1个;

    (2) optional:这个域在消息中可以有0或1个;

    (3) repeated:这个域在消息中可以有从多个,包括0个。

  Protobuf的类型与Java类型的映射关系:

  1. double   ->  double
  2. float    ->  float
  3. int32    ->  int
  4. int64    ->  long
  5. uint32   ->  int[1]
  6. uint64   ->  long[1]
  7. sint32   ->  int
  8. sint64   ->  long
  9. fixed32  ->  int[1]
  10. fixed64  ->  long[1]
  11. sfixed32 ->  int
  12. sfixed64 ->  long
  13. bool     ->  boolean
  14. string   ->  String
  15. bytes    ->  ByteString

  编写完.proto文件后,就可以使用下面的命令将会在protobuf目录中生成源文件PersonProtos.java

  1. protoc –java_out=. person.proto

3.序列化

  先看下面一个例子:

  1. message Test1 {
  2.     required int32 a = 1;
  3. }

  创建一个Test1消息,并且把a设置为150,那么序列化后有如下3个字节:

  1. 08 96 01

3.1.varint编码

  varint编码的序列化使用一个或多个字节,数字越大使用的字节数越多。对于序列化后的字节,除了最后一个字节,都有一个most significant bit(msb):表示后边是否有更多的字节。整数序列化时按7位一组,每个字节的低7位保存一组,第一个字节存储最低位一组,即使用little endian。

  比如300序列化后的字节序列是:

  1. 10101100 00000010

  先去掉每个字节的msb:

  1. 0101100 0000010

  交换字节的顺序:

  1. 0000010 0101100 -> 100101100 -> 256 + 32 + 8 + 4 = 300

3.2.消息结构

  一个protocol buffer message是一个key/value对序列。每一key/value对的key实际是两个值:.proto文件中的field number以及wire type。可用的wire type如下所示:


Type Meaning Used For
0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit fixed64, sfixed64, double
2 Length-delimited string, bytes, embedded messages, packed repeated fields
3 Start group groups (deprecated)
4 End group groups (deprecated)
5 32-bit fixed32, sfixed32, float

  每一个key是一个varint,值是(field_number << 3) | wire_type,即低三位存储wire type。

3.3.有符号整数

  有符号整数使用ZigZag编码来将有符号整数映射到无符号整数。


Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2147483647 4294967294
-2147483648 4294967294

3.4.非varint编码

  1. message Test2 {
  2.     required string b = 2;
  3. }

  将b的值设置为“testing”,编码结果为:

  1. 12 07 74 65 73 74 69 6e 67

  这里的key是0×12:field_number = 2, type = 2。字符串的长度是7。

3.5.嵌套消息

  1. message Tes3 {
  2.     required Test1 c = 3;
  3. }

  c的成员a的值设置为150,编码结果为:

  1. 1a 03 08 96 01

  后三个字节和Test1一样,之前的数字3表示长度。

3.5.Repeated域

  1. message Test4 {
  2.     repeated int32 d = 4;
  3. }

  {3, 270, 86942}编码结果为:

  1. 22        // tag (field number 4, wire type 2)
  2. 06        // payload size (6 bytes)
  3. 03        // first element (varint 3)
  4. 8E 02     // second element (varint 270)
  5. 9E A7 05  // third element (varint 86942)

4.rpc通信实现

  使用protocol buffers的第三方rpc实现protobuf-socket-rpc。

  假设protocol buffers生成的类是protobuf. MessageProtos,其中定义了一个消息类Message和一个服务类MessageService,MessageService中定义了一个接口getMessage(RpcController, Message request)。

  服务接口实现MessageServiceImpl.java:

  1. package protobuf;
  2.  
  3. import com.google.protobuf.RpcController;
  4. import com.google.protobuf.ServiceException;
  5. import protobuf.MessageProtos.Message;
  6. import protobuf.MessageProtos.MessageService.BlockingInterface;
  7.  
  8. public class MessageServiceImpl implements BlockingInterface {
  9.     @Override
  10.     public Message getMessage(RpcController controllerMessage request)
  11.             throws ServiceException {
  12.         // process request       
  13.     ……
  14.         return request;
  15.     }
  16. }

  服务端实现Server.java:

  1. package protobuf;
  2.  
  3. import java.util.concurrent.Executors;
  4.  
  5. import com.googlecode.protobuf.socketrpc.RpcServer;
  6. import com.googlecode.protobuf.socketrpc.ServerRpcConnectionFactory;
  7. import com.googlecode.protobuf.socketrpc.SocketRpcConnectionFactories;
  8. import protobuf.MessageProtos.MessageService;
  9.  
  10. public class Server {
  11.     private int port;
  12.     private int threadPoolSize;
  13.  
  14.     public Server(int portint threadPoolSize) {
  15.         this.port = port;
  16.         this.threadPoolSize = threadPoolSize;
  17.     }
  18.  
  19.     public void run() {
  20.         // Start server
  21.         ServerRpcConnectionFactory rpcConnectionFactory =SocketRpcConnectionFactories
  22.                 .createServerRpcConnectionFactory(port);
  23.         RpcServer server = new RpcServer(rpcConnectionFactory,
  24.                 Executors.newFixedThreadPool(threadPoolSize)true);
  25.         server.registerBlockingService(MessageService
  26.                 .newReflectiveBlockingService(new MessageServiceImpl()));
  27.         server.run();
  28.     }
  29.  
  30.     public static void main(String[] args) {
  31.         if (args.length != 2) {
  32.             System.out.println("Usage: Server port thread_pool_size");
  33.             return;
  34.         }
  35.  
  36.         int port = Integer.parseInt(args[0]);
  37.         int size = Integer.parseInt(args[1]);
  38.  
  39.         new Server(portsize).run();
  40.     }
  41. }

  客户端实现Client.java:

  1. package protobuf;
  2.  
  3. import protobuf.MessageProtos.Message;
  4. import protobuf.MessageProtos.MessageService;
  5. import protobuf.MessageProtos.MessageService.BlockingInterface;
  6.  
  7. import com.google.protobuf.BlockingRpcChannel;
  8. import com.google.protobuf.ByteString;
  9. import com.google.protobuf.RpcController;
  10. import com.google.protobuf.ServiceException;
  11. import com.googlecode.protobuf.socketrpc.RpcChannels;
  12. import com.googlecode.protobuf.socketrpc.RpcConnectionFactory;
  13. import com.googlecode.protobuf.socketrpc.SocketRpcConnectionFactories;
  14. import com.googlecode.protobuf.socketrpc.SocketRpcController;
  15.  
  16. public class Client {
  17.     private int port;
  18.     private String host;
  19.     private int size;
  20.     private int count;
  21.  
  22.     public Client(int portString hostint sizeint count) {
  23.         super();
  24.         this.port = port;
  25.         this.host = host;
  26.         this.size = size;
  27.         this.count = count;
  28.     }
  29.  
  30.     public long run() {
  31.         // Create channel
  32.         RpcConnectionFactory connectionFactory = SocketRpcConnectionFactories
  33.                 .createRpcConnectionFactory(hostport);
  34.         BlockingRpcChannel channel = RpcChannels
  35.                 .newBlockingRpcChannel(connectionFactory);
  36.  
  37.         // Call service
  38.         BlockingInterface service = MessageService.newBlockingStub(channel);
  39.         RpcController controller = new SocketRpcController();
  40.         Message.Builder message = Message.newBuilder();
  41.         // initiate the message
  42.         …
  43.  
  44.         long start = 0;
  45.         long end = 0;
  46.         try {
  47.             start = System.currentTimeMillis();
  48.             for (int i = 0i < counti++) {
  49.                 service.getMessage(controllermessage.build());
  50.             }
  51.             end = System.currentTimeMillis();
  52.             System.out.println(end - start);
  53.         } catch (ServiceException e) {
  54.             e.printStackTrace();
  55.         }
  56.  
  57.         // Check success
  58.         if (controller.failed()) {
  59.             System.err.println(String.format("Rpc failed %s : %s",
  60.                     ((SocketRpcController) controller).errorReason(),
  61.                     controller.errorText()));
  62.         }
  63.  
  64.         return end - start;
  65.     }
  66.  
  67.     public static void main(String[] args) {
  68.         if (args.length != 4) {
  69.             System.out.println("Usage: Client host port dataSize count");
  70.             return;
  71.         }
  72.         String host = args[0];
  73.         int port = Integer.parseInt(args[1]);
  74.         int size = Integer.parseInt(args[2]);
  75.         int count = Integer.parseInt(args[3]);
  76.  
  77.         new Client(porthostsizecount).run();
  78.     }
  79. }

5.参考资料

  (1) Protocol Buffers Documentation: http://code.google.com/apis/protocolbuffers/docs/overview.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值