现在的位置: 首页 > 综合 > 正文

ProtocolBuffer 结合 LZO在 Hadoop中的使用(三)

2012年10月04日 ⁄ 综合 ⁄ 共 4898字 ⁄ 字号 评论关闭

ProtocolBuffer 结合 LZO在 Hadoop中的使用(三)

1.自动生成Protocol buffer对象

1、把protoc.exe文件复制到 c:\windows 目录中(呵呵,这样最省事),window7放在windows/system32下

下载地址

2、使用 protocol buffer 编译器编译 logFormat.proto 文件。打开命令行窗口,并定位到logFormat.proto文件目录中,运行命令 protoc --java_out=.
logFormat.proto(注意.后面有空格,否则会有错误),执行这个命令后会在当前目录下生成一个java类com.searchlog.LogFormat.java。

下载
protobuf-java-2.4.1.jar

//http://code.google.com/apis/protocolbuffers/docs/proto.html

option java_package = "com.searchlog";

message URI {
optional string protocol=1;
optional string host=2;
optional int32 port=3;
optional string path=4;
optional string query=5;
}


message SearchLog {
optional int64 id=39;
optional string remote_addr=1;
optional int64 time=2; //日志的时间,从1970年以来的毫秒值
optional int32 status=3;
optional int32 body_bytes_sent=4;
optional string ua=5;
optional string CookieId=6; //si. 标识唯一用户.
optional string lastVisitTime=7; //lt. 上次页面加载时间
optional string screenResolution=8; //sr. 屏幕分辨率
optional string language=33; //ln.浏览器语言
optional bool cookieEnabled=9; //ca. 是否启用cookie
optional bool javaEnable=10; //ja. 是否启用java
optional URI refer=11; //re. referer
optional URI location=12; //lo. 当前页面的location
optional string flashVersion=13; //fv.flashversion
optional string clickPoint=14;//cp. 页面上点击坐标
optional int32 rand=15; //rand. 随机字符串
optional string keyword=16; //k. 搜索keyword
optional int32 type=17; 
optional int32 pos=18;
optional string pageStay=42; //ps.页面停留时间
optional string jsVersion=20; //v. 统计代码版本
optional int32 sendCount=21;//c. 本页发送统计数据的次数
optional string uuid=22; //uuid. 页面唯一id表示
optional string timestamp=23; //ts
optional int32 ds=24; //ds. 页面直达区个数
optional int32 vs=25;//vs. 页面普通结果数
optional string pid=26; //pid. 点击链接的节目id
optional string vid=27; //vid. 点击链接的视频id
optional int32 pageno=28; //pn. 当前页码
optional int32 mc=29; //mc. 点击直达区更多,1点击展开,2点击隐藏
optional int32 ct=30; //ct. 直达区分类:
optional int32 directpos=31; //directpos
optional int32 origin=32; //0,tudou. 1,youku
optional string ok=34; //ok,用户在输入框中输入的词
optional string os=35; //os,用户通过kubox选择的词
optional int32 ki=37; //ki.用户选择的第几个提示词
optional string lastKeyword=38; //lk.上一次的搜索词
optional string kt=36; //kt. 
optional int32 sh=40; //sh. 是否屏蔽词。1,是。0或者不填,不是。
optional int32 site=41; 
};

这边介绍一下该文件定义的语法:

package com.searchlog.proto; // 声明包,表明此声明下方的数据结构都属于此包, package起到命名空间的作用避免变量命名冲突,使用protoc.exe编译.proto文件后,package名即转换为相应的命名空间;

 

定义在message A里边的message B在protoc.exe编译后,会成为package 下的两个平行的类:

 public static final class URI extends
      com.google.protobuf.GeneratedMessage
      implements URIOrBuilder {
...
}

 public static final class SearchLog extends
      com.google.protobuf.GeneratedMessage
      implements SearchLogOrBuilder{
...
}

message中的每一个成员都必须使用域规则(field rules)required、optional、repeated 三者之一作为前置声明。

required 修饰的成员必须被赋值,如果libprotobuf 在debug模式下编译,那么对包含未初始化的repeated成员的对象进行序列化的时候将会失败,如果libprotobuf经过优化编译,那么序列化会继续进行而不产生错误,但对序列化后的数据进行解析会失败。

对于optional 成员,其值可以不被初始化,但在可行的时候应为optional 成员设定默认值。如果没有默认值,那么系统会自动为optional成员添加默认值,对于数值类型,默认值是0;对于字符串类型,默认为空串;bool类型默认值为false。对于嵌套的message,其默认值为默认的对象或原型,即未初始化的对象。访问默认对象的成员得到的将是系统默认值。

repeated成员意味着可以包含多个此类型的成员(也可以是0个)。

 

对于message中的required 和optional 变量var,使用相应的set_var函数对成员变量赋值;对于repeated 变量 revar, 使用 add_revar 函数进行赋值。

 拿其中的一个字段来说:

生成的代码中含有:

// optional string lastVisitTime = 7;
      private java.lang.Object lastVisitTime_ = "";
      public boolean hasLastVisitTime() {
        return ((bitField0_ & 0x00000080) == 0x00000080);
      }
      public String getLastVisitTime() {
        java.lang.Object ref = lastVisitTime_;
        if (!(ref instanceof String)) {
          String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
          lastVisitTime_ = s;
          return s;
        } else {
          return (String) ref;
        }
      }
      public Builder setLastVisitTime(String value) {
        if (value == null) {
    throw new NullPointerException();
  }
  bitField0_ |= 0x00000080;
        lastVisitTime_ = value;
        onChanged();
        return this;
      }
      public Builder clearLastVisitTime() {
        bitField0_ = (bitField0_ & ~0x00000080);
        lastVisitTime_ = getDefaultInstance().getLastVisitTime();
        onChanged();
        return this;
      }
      void setLastVisitTime(com.google.protobuf.ByteString value) {
        bitField0_ |= 0x00000080;
        lastVisitTime_ = value;
        onChanged();
      }

使用的时候:

SearchLog.Builder builder = SearchLog.newBuilder()
builder.setLastVisitTime(value.get(0));
SearchLog msg = builder.build();

对于任意类型的成员var, 都可以通过 have_var() 来判断成员变量是否已经赋值;通过clear_var() 来清除变量内容;对于对象,可以通过 IsInitialized()函数判断required类型成员是否全部初始化;使用clear()清理对象的所有成员。对于repeated成员b,b_size()函数可以用来获取b成员的个数。

 

对于string成员S,使用mutable_S()函数将使得你得到直接指向该成员的指针(direct pointer),而无论该成员是否已经初始化。

 

message 成员的类型可以是基础类型boolint32floatdoublestring也可以是复杂的自定义类型,自定义类型可以嵌套。更详细的类型支持列表:http://code.google.com/apis/protocolbuffers/docs/proto.html

3.将生成的ProtoBufferPractice.java文件引入eclipse

4.把下载的protobuf-java-2.4.1.jar也引入工程

option java_package = “com.example.foo”;
我认为比较重要的文件级选项有
java_package ——指定生成的java代码的packge。
java_outer_classname ——protoc会为每个.proto文件一个java类,其中每个message都做作为该java类的内部类,这个选项就用于指定外围类的类名。
optimize_for ——用于优化protoc产生的代码,有三种模式:
SPEED –产生的代码将具有最好的序列化/反序列化执行速度。
CODE_SIZE –产生的代码将具有更少的代码行数。
LITE_RUNTIME –protocol buffer有一个简化版的runtime库,比如要生成用于手机平台的代码,可以使用这个选项。
另外,packed是一个字段级选项,在上面字段修改符repeated中提到了。

分析:
   个人认为pb之所以快和省流量除了本省字节少以外,序列化过程减少的字节数是主要因素
具体可参考
http://kangsg219.iteye.com/blog/904762

其他参考:http://www.searchtb.com/2010/11/protocol-buffers%E7%9A%84%E5%BA%94%E7%94%A8%E4%B8%8E%E5%88%86%E6%9E%90.html

在实践中具体选择什么还需要依情况而定:

http://www.oschina.net/question/12_10307

抱歉!评论已关闭.