高性能RPC over MINA&google protobuf 代码&实例 (一)

作者的其他文章还不错 !

原文: 

http://maoyidao.iteye.com/blog/1636923


最近团队在开发基于移动互联网的项目,又一次涉及到post service,即在服务器集群之间投递消息。是的,又是一个RPC服务。RPC实现方式从笨重的CORBA,SOAP over HTTP,XMPP over TCP,到轻量级的protobuf,scribe和Avro。这里不想比较各自的应用场景(另外后面三种RPC方式极为接近,都是通过提供Object <-> 二进制映射来提高高效的传输),本文的目的是给大家一点可以实际操作的代码:java如何用protobuf 实现rpc

 

和protobuf-socket-rpc的区别

protobuf-socket-rpc(code.google.com/p/protobuf-socket-rpc/)是googlecode为rpc写的简单实现。本文介绍的代码和googlecode不同之处在于:

1,基于NIO

2,增加了校验码

高性能RPC over google protobuf

 

Google's protocol buffer library makes writing rpc services easy, but it does not contain a rpc implementation. The transport details are left up to the user to implement.

google把这问题留给了我们,那么看看应该怎么实现。hellow world伪代码应该是这样的:

 

Java代码   收藏代码
  1. MessageLite message = getMessage(); // get a proto message object by proto file  
  2.   
  3. OutputStream out = getOutputStream();  
  4. InputStream in = getInputStream();  
  5. message.writeDelimitedTo(out); //  Like writeTo(OutputStream), but writes the size of the message as a varint before writing the data  
  6. messageBuilder.mergeDelimitedFrom(in);  
 

好了,这样就实现了序列化和反序列化。在真正的内容之前加入内容长度,这是一种最简单的实现。为了能可靠的进行传输,我在消息长度前加入了2个byte的验证码,下面就开始逐步构建我的rpc代码。

 

定义你的proto文件,为传输多种消息,需要有“命令”字段:比如:Maoyidao.proto

List 1:

 

Java代码   收藏代码
  1. package com.maoyidao.rpc;  
  2.   
  3. message MaoyidaoPacket {  
  4.   required int32 cmd = 1;           
  5.   required int32 subcmd = 2;     
  6.   optional bytes content = 3;        
  7. }  

 

 

OK,compile it to Java class: protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto

你会得到一个MaoyidaoPacket 类,然后你需要这样获得实例:

List 2:

 

Java代码   收藏代码
  1. Maoyidao.MaoyidaoPacket packet = Maoyidao.MaoyidaoPacket.newBuilder()  
  2.    .setCmd(mycmd)  
  3.    .setSubcmd(mysubcmd)  
  4.    .setContent(ByteString.copyFromUtf8("some message")).build();   

 

 

我们先不讨论怎么基于MINA创建一个NIO,先假设我们获得了一个OutputStream,看看怎么把消息写出去(其中的关键是用一些特殊的字符来区分你的消息,这是RPC over TCP的基本要求):

List 3:

 

Java代码   收藏代码
  1. private final void writeObject(OutputStream os, Maoyidao.MaoyidaoPacket packet)  {        
  2.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  3.         com.google.protobuf.CodedOutputStream cos = com.google.protobuf.CodedOutputStream.newInstance(baos);      
  4.         cos.writeRawVarint32(3);  
  5.         cos.writeRawVarint32(7);  
  6.         cos.writeRawVarint32(packet.getSerializedSize());  
  7.         vpacket.writeTo(cos);  
  8.         cos.flush();          
  9.         os.write(baos.toByteArray());                 
  10.             baos.close();  
  11.     }  
  12. }  

 

注意我不仅写了分隔符,还写了content长度。

 

读进来的时候要用相同的方式解析,假设我们得到了一个Bytebuffer,熟悉NIO的同学知道,你总是会从ByteBuffer中读取数据。同时我需要用到com.google.protobuf.CodedInputStream:Reads and decodes protocol message fields. This class contains two kinds of methods: methods that read specific protocol message constructs and field types (e.g. readTag() and readInt32()) and methods that read low-level values (e.g. readRawVarint32() and readRawBytes(int)).)这样我就可以从inputstream中读到校验码:

 

 

Java代码   收藏代码
  1. ByteBuffer in = getByteBuffer();  
  2. CodedInputStream cis = CodedInputStream.newInstance(in);  
  3.   
  4. int flag1 = cis.readRawVarint32();  
  5. int flag2 = cis.readRawVarint32();  
  6. if(flag1 != 3 || flag2 != 7){  
  7.     // find some error  
  8. }  
  9.   
  10. int contentLength = cis.readRawVarint32();  
  11. int contentLength0 = contentLength + CodedOutputStream.computeRawVarint32Size(contentLength);  
  12.   
  13. if(in.remaining() >= contentLength0){              
  14.     try {  
  15.         Maoyidao.MaoyidaoPacket.Builder builder = Maoyidao.MaoyidaoPacket.newBuilder();  
  16.         CodedInputStream.newInstance(getBytesFromIn(in,contentLength0)).readMessage(  
  17.             builder, ExtensionRegistry.getEmptyRegistry());               
  18.         out.write(builder.build());  
  19.         in.position(in.position() + protocolLength);  
  20.         return true;  
  21.     } catch (Exception e) {  
  22.         //   
  23.     }             
  24. }  
  25.   
  26. // ByteBuffer没有足够的数据,等待下一次  
  27. // do something  
 

截止目前,我们完成了带校验码的基于protobuf的消息序列化和反序列。在这个实现中,我更偏向把protobuf当做一个序列化工具来使用,整体还是依赖MINA本身提供的架构,这部分将在本系列的下一篇中详细阐述。

 

 

本文系maoyidao原创,转载请引用原链接:

http://maoyidao.iteye.com/blog/1636923

转载于:https://my.oschina.net/u/257088/blog/195953

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值