怎样查看MySql数据库物理文件存放位置

mikel阅读(1307)

想导出mySQL中的数据库文件,死活找不到,网上说在配置文件中有路径,可是我打开我的配置文件,里边的代码全都是注释掉的,没有一句有用的。后来在某一论坛上找到解决方法了,记录下来。

使用如下命令:

1
<code class=" hljs vbnet">mySQL> show global variables like "%datadir%";</code>

这里写图片描述

数据库文件存放在这个位置,每个人可能有所不同哦。

1
<code class=" hljs tex"> C:\ProgramData\MySQL\MySQL Server 5.6\Data\</code>

经此一役,我还是觉得以后使用mysql还是先自己给配置一个数据库存储路径比较好。

安装protocolbuffer详解 - pingyan158的专栏 - 博客频道 - CSDN.NET

mikel阅读(963)

来源: 安装protocolbuffer详解 – pingyan158的专栏 – 博客频道 – CSDN.NET

原文:http://cq520.iteye.com/blog/2024739

What’s the protocolbuffer?

protocolbuffer是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了三种语言的实现:Javac++ 和 Python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域

本篇博客主要教大家如何在windows7下安装Java的protocol buffer(具体使用及注意事项将会在下一篇博客当中进行详细介绍)

首先,要使用protocol buffer得保证maven安装成功,maven的下载地址:http://maven.apache.org/download.cgi 。

1.解压完之后请将maven的bin目录配置到你的环境变量当中。

2.请确保你的JAVA_HOME的变量是指向你的JDK的主目录,如果你的系统变量中没有JAVA_HOME这一项,请点击新建添加。

3.打开命令行,输入“mvn –version”如果输出正确则表示安装成功

安装完maven之后就要进行protocol buffer的安装了,下载地址: http://code.google.com/p/protobuf/downloads/list 。下载protobuf-2.4.1.zip 和 protoc-2.4.1-win32.zip 两个包。

1. 解压完成之后有两种选择,第一:将protoc-2.4.1-win32中的protoc.exe所在的目录配置到环境变量当中,第二:将protoc.exe拷贝到c:\windows\system32目录下,这里推荐第二种做法。

2. 将proto.exe文件拷贝到解压后的protobuf-2.4.1\src目录中.

3. 进入protobuf-2.4.1\java 目录  执行mvn package命令编辑该包,系统将会在target目录中生成protobuf-java-2.4.1.jar文件(注意运行时需要联网,首次安装可能需要一定的时间)。

4. 假设你的数据文件目录在XXX\data目录,把上一步生成的jar拷贝到该目录中即可。

5. 进入XXX\protobuf-2.4.1\examples目录,可以看到addressbook.proto文件,在命令行中执行 protoc –java_out=. addressbook.proto 命令(特别注意. Addressbook.proto中间的空格,我第一次安装就因为没注意而反复失败),如果生成com文件夹并且最终生成AddressBookProtos类则说明安装成功。

6. 打开eclipse,选择windows–>preferences–>java–>Installed JREs编辑你默认的java源码包,并将上面所提到的protobuf-java-2.4.1.jar文件添加进去。

OK,大功告成了。

Protobuf语言指南 - dkcndk - 博客园

mikel阅读(1539)

来源: Protobuf语言指南 – dkcndk – 博客园

Protobuf语言指南

l  定义一个消息(message)类型

l  标量值类型

l  Optional 的字段及默认值

l  枚举

l  使用其他消息类型

l  嵌套类型

l  更新一个消息类型

l  扩展

l  包(package)

l  定义服务(service)

l  选项(option)

l  生成访问类

本指南描述了怎样使用protocolbuffer语言来构造你的protocol buffer数据,包括.proto文件语法以及怎样生成.proto文件的数据访问类。

本文是一个参考指南——如果要查看如何使用本文中描述的多个特性的循序渐进的例子,请在http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/tutorials.html中查找需要的语言的教程。

l  定义一个消息类型

先来看一个非常简单的例子。假设你想定义一个“搜索请求”的消息格式,每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数,以及每一页多少条查询结果。可以采用如下的方式来定义消息类型的.proto文件了:

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;

  optional int32 result_per_page = 3;

}

SearchRequest消息格式有3个字段,在消息中承载的数据分别对应于每一个字段。其中每个字段都有一个名字和一种类型。

Ø  指定字段类型

在上面的例子中,所有字段都是标量类型:两个整型(page_number和result_per_page),一个string类型(query)。当然,你也可以为字段指定其他的合成类型,包括枚举(enumerations)或其他消息类型。

Ø   分配标识号

正如上述文件格式,在消息定义中,每个字段都有唯一的一个标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改 变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。

最小的标识号可以从1开始,最大到229 – 1, or 536,870,911。不可以使用其中的[19000-19999]的标识号, Protobuf协议实现中对这些进行了预留。如果非要在.proto文件中使用这些预留标识号,编译时就会报警。

Ø  指定字段规则

所指定的消息字段修饰符必须是如下之一:

²  required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的;

²  optional:消息格式中该字段可以有0个或1个值(不超过1个)。

²  repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于java中的List。

由于一些历史原因,基本数值类型的repeated的字段并没有被尽可能地高效编码。在新的代码中,用户应该使用特殊选项[packed=true]来保证更高效的编码。如:

repeated int32 samples = 4 [packed=true];

required是永久性的:在将一个字段标识为required的时候,应该特别小心。如果在某些情况下不想写入或者发送一个required的 字段,将原始该字段修饰符更改为optional可能会遇到问题——旧版本的使用者会认为不含该字段的消息是不完整的,从而可能会无目的的拒绝解析。在这 种情况下,你应该考虑编写特别针对于应用程序的、自定义的消息校验函数。Google的一些工程师得出了一个结论:使用required弊多于利;他们更 愿意使用optional和repeated而不是required。当然,这个观点并不具有普遍性。

Ø   添加更多消息类型

在一个.proto文件中可以定义多个消息类型。在定义多个相关的消息的时候,这一点特别有用——例如,如果想定义与SearchResponse消息类型对应的回复消息格式的话,你可以将它添加到相同的.proto文件中,如:

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;

  optional int32 result_per_page = 3;

}

message SearchResponse {

 …

}

Ø  添加注释

向.proto文件添加注释,可以使用C/C++/java风格的双斜杠(//) 语法格式,如:

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;// 最终返回的页数

  optional int32 result_per_page = 3;// 每页返回的结果数

}

Ø  从.proto文件生成了什么?

当用protocolbuffer编译器来运行.proto文件时,编译器将生成所选择语言的代码,这些代码可以操作在.proto文件中定义的消息类型,包括获取、设置字段值,将消息序列化到一个输出流中,以及从一个输入流中解析消息。

²  对C++来说,编译器会为每个.proto文件生成一个.h文件和一个.cc文件,.proto文件中的每一个消息有一个对应的类。

²  对Java来说,编译器为每一个消息类型生成了一个.java文件,以及一个特殊的Builder类(该类是用来创建消息类接口的)。

²  对Python来说,有点不太一样——Python编译器为.proto文件中的每个消息类型生成一个含有静态描述符的模块,,该模块与一个元类(metaclass)在运行时(runtime)被用来创建所需的Python数据访问类。

你可以从如下的文档链接中获取每种语言更多API。http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference/overview.html

l  标量数值类型

一个标量消息字段可以含有一个如下的类型——该表格展示了定义于.proto文件中的类型,以及与之对应的、在自动生成的访问类中定义的类型:

.proto类型 Java 类型 C++类型 备注
double double double  
float float float  
int32 int int32 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。
int64 long int64 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。
uint32 int[1] uint32 Uses variable-length encoding.
uint64 long[1] uint64 Uses variable-length encoding.
sint32 int int32 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。
sint64 long int64 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。
fixed32 int[1] uint32 总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效。
fixed64 long[1] uint64 总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。
sfixed32 int int32 总是4个字节。
sfixed64 long int64 总是8个字节。
bool boolean bool  
string String string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。
bytes ByteString string 可能包含任意顺序的字节数据。

你可以在文章http://code.google.com/apis/protocolbuffers/docs/encoding.html 中,找到更多“序列化消息时各种类型如何编码”的信息。

l  Optional的字段和默认值

如上所述,消息描述中的一个元素可以被标记为“可选的”(optional)。一个格式良好的消息可以包含0个或一个optional的元素。当解 析消息时,如果它不包含optional的元素值,那么解析出来的对象中的对应字段就被置为默认值。默认值可以在消息描述文件中指定。例如,要为 SearchRequest消息的result_per_page字段指定默认值10,在定义消息格式时如下所示:

optional int32 result_per_page = 3 [default = 10];

如果没有为optional的元素指定默认值,就会使用与特定类型相关的默认值:对string来说,默认值是空字符串。对bool来说,默认值是false。对数值类型来说,默认值是0。对枚举来说,默认值是枚举类型定义中的第一个值。

枚举

当需要定义一个消息类型的时候,可能想为一个字段指定某“预定义值序列”中的一个值。例如,假设要为每一个SearchRequest消息添加一个 corpus字段,而corpus的值可能是UNIVERSAL,WEB,IMAGES,LOCAL,NEWS,PRODUCTS或VIDEO中的一个。 其实可以很容易地实现这一点:通过向消息定义中添加一个枚举(enum)就可以了。一个enum类型的字段只能用指定的常量集中的一个值作为其值(如果尝 试指定不同的值,解析器就会把它当作一个未知的字段来对待)。在下面的例子中,在消息格式中添加了一个叫做Corpus的枚举类型——它含有所有可能的值 ——以及一个类型为Corpus的字段:

message SearchRequest {

  required string query = 1;

  optional int32 page_number = 2;

  optional int32 result_per_page = 3 [default = 10];

  enum Corpus {

    UNIVERSAL = 0;

    WEB = 1;

    IMAGES = 2;

    LOCAL = 3;

    NEWS = 4;

    PRODUCTS = 5;

    VIDEO = 6;

  }

  optional Corpus corpus = 4 [default = UNIVERSAL];

}

枚举常量必须在32位整型值的范围内。因为enum值是使用可变编码方式的,对负数不够高效,因此不推荐在enum中使用负数。如上例所示,可以在 一个消息定义的内部或外部定义枚举——这些枚举可以在.proto文件中的任何消息定义里重用。当然也可以在一个消息中声明一个枚举类型,而在另一个不同 的消息中使用它——采用MessageType.EnumType的语法格式。

当对一个使用了枚举的.proto文件运行protocol buffer编译器的时候,生成的代码中将有一个对应的enum(对Java或C++来说),或者一个特殊的EnumDescriptor类(对 Python来说),它被用来在运行时生成的类中创建一系列的整型值符号常量(symbolic constants)。

关于如何在你的应用程序的消息中使用枚举的更多信息,请查看所选择的语言http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference/overview.html。

l  使用其他消息类型

你可以将其他消息类型用作字段类型。例如,假设在每一个SearchResponse消息中包含Result消息,此时可以在相同的.proto文件中定义一个Result消息类型,然后在SearchResponse消息中指定一个Result类型的字段,如:

message SearchResponse {

  repeated Result result = 1;

}

message Result {

  required string url = 1;

  optional string title = 2;

  repeated string snippets = 3;

}

Ø  导入定义

在上面的例子中,Result消息类型与SearchResponse是定义在同一文件中的。如果想要使用的消息类型已经在其他.proto文件中已经定义过了呢?

你可以通过导入(importing)其他.proto文件中的定义来使用它们。要导入其他.proto文件的定义,你需要在你的文件中添加一个导入声明,如:

import “myproject/other_protos.proto”;

protocol编译器就会在一系列目录中查找需要被导入的文件,这些目录通过protocol编译器的命令行参数-I/–import_path指定。如果不提供参数,编译器就在其调用目录下查找。

l  嵌套类型

你可以在其他消息类型中定义、使用消息类型,在下面的例子中,Result消息就定义在SearchResponse消息内,如:

message SearchResponse {

  message Result {

    required string url = 1;

    optional string title = 2;

    repeated string snippets = 3;

  }

  repeated Result result = 1;

}

如果你想在它的父消息类型的外部重用这个消息类型,你需要以Parent.Type的形式使用它,如:

message SomeOtherMessage {

  optional SearchResponse.Result result = 1;

}

当然,你也可以将消息嵌套任意多层,如:

message Outer {                  // Level 0

  message MiddleAA {  // Level 1

    message Inner {   // Level 2

      required int64 ival = 1;

      optional bool  booly = 2;

    }

  }

  message MiddleBB {  // Level 1

    message Inner {   // Level 2

      required int32 ival = 1;

      optional bool  booly = 2;

    }

  }

}

Ø  

注:该特性已被弃用,在创建新的消息类型的时候,不应该再使用它——可以使用嵌套消息类型来代替它。

“组”是指在消息定义中嵌套信息的另一种方法。比如,在SearchResponse中包含若干Result的另一种方法是 :

message SearchResponse {

  repeated group Result = 1 {

    required string url = 2;

    optional string title = 3;

    repeated string snippets = 4;

  }

}

一个“组”只是简单地将一个嵌套消息类型和一个字段捆绑到一个单独的声明中。在代码中,可以把它看成是含有一个Result类型、名叫result的字段的消息(后面的名字被转换成了小写,所以它不会与前面的冲突)。

因此,除了数据传输格式不同之外,这个例子与上面的SearchResponse例子是完全等价的。

l  更新一个消息类型

如果一个已有的消息格式已无法满足新的需求——如,要在消息中添加一个额外的字段——但是同时旧版本写的代码仍然可用。不用担心!更新消息而不破坏已有代码是非常简单的。在更新时只要记住以下的规则即可。

²  不要更改任何已有的字段的数值标识。

²  所添加的任何字段都必须是optional或repeated的。这就意味着任何使用“旧”的消息格式的代码序列化的消息可以被新的代码所解析,因为它们 不会丢掉任何required的元素。应该为这些元素设置合理的默认值,这样新的代码就能够正确地与老代码生成的消息交互了。类似地,新的代码创建的消息 也能被老的代码解析:老的二进制程序在解析的时候只是简单地将新字段忽略。然而,未知的字段是没有被抛弃的。此后,如果消息被序列化,未知的字段会随之一 起被序列化——所以,如果消息传到了新代码那里,则新的字段仍然可用。注意:对Python来说,对未知字段的保留策略是无效的。

²  非required的字段可以移除——只要它们的标识号在新的消息类型中不再使用(更好的做法可能是重命名那个字段,例如在字段前添加“OBSOLETE_”前缀,那样的话,使用的.proto文件的用户将来就不会无意中重新使用了那些不该使用的标识号)。

²  一个非required的字段可以转换为一个扩展,反之亦然——只要它的类型和标识号保持不变。

²  int32, uint32, int64, uint64,和bool是全部兼容的,这意味着可以将这些类型中的一个转换为另外一个,而不会破坏向前、 向后的兼容性。如果解析出来的数字与对应的类型不相符,那么结果就像在C++中对它进行了强制类型转换一样(例如,如果把一个64位数字当作int32来 读取,那么它就会被截断为32位的数字)。

²  sint32和sint64是互相兼容的,但是它们与其他整数类型不兼容。

²  string和bytes是兼容的——只要bytes是有效的UTF-8编码。

²  嵌套消息与bytes是兼容的——只要bytes包含该消息的一个编码过的版本。

²  fixed32与sfixed32是兼容的,fixed64与sfixed64是兼容的。

l  扩展

通过扩展,可以将一个范围内的字段标识号声明为可被第三方扩展所用。然后,其他人就可以在他们自己的.proto文件中为该消息类型声明新的字段,而不必去编辑原始文件了。看个具体例子:

message Foo {

  // …

  extensions 100 to 199;

}

这个例子表明:在消息Foo中,范围[100,199]之内的字段标识号被保留为扩展用。现在,其他人就可以在他们自己的.proto文件中添加新字段到Foo里了,但是添加的字段标识号要在指定的范围内——例如:

extend Foo {

  optional int32 bar = 126;

}

这个例子表明:消息Foo现在有一个名为bar的optional int32字段。

当用户的Foo消息被编码的时候,数据的传输格式与用户在Foo里定义新字段的效果是完全一样的。

然而,要在程序代码中访问扩展字段的方法与访问普通的字段稍有不同——生成的数据访问代码为扩展准备了特殊的访问函数来访问它。例如,下面是如何在C++中设置bar的值:

Foo foo;
foo.SetExtension(bar, 15);

类似地,Foo类也定义了模板函数 HasExtension(),ClearExtension(),GetExtension(),MutableExtension(),以及 AddExtension()。这些函数的语义都与对应的普通字段的访问函数相符。要查看更多使用扩展的信息,请参考相应语言的代码生成指南。注:扩展可 以是任何字段类型,包括消息类型。

l  嵌套的扩展

可以在另一个类型的范围内声明扩展,如:

message Baz {

  extend Foo {

    optional int32 bar = 126;

  }

  …

}

在此例中,访问此扩展的C++代码如下:

Foo foo;

foo.SetExtension(Baz::bar, 15);

一个通常的设计模式就是:在扩展的字段类型的范围内定义该扩展——例如,下面是一个Foo的扩展(该扩展是Baz类型的),其中,扩展被定义为了Baz的一部分:

message Baz {

  extend Foo {

    optional Baz foo_ext = 127;

  }

  …

}

然而,并没有强制要求一个消息类型的扩展一定要定义在那个消息中。也可以这样做:

message Baz {

  …

}

extend Foo {

  optional Baz foo_baz_ext = 127;

}

 

事实上,这种语法格式更能防止引起混淆。正如上面所提到的,嵌套的语法通常被错误地认为有子类化的关系——尤其是对那些还不熟悉扩展的用户来说。

Ø  选择可扩展的标符号

在同一个消息类型中一定要确保两个用户不会扩展新增相同的标识号,否则可能会导致数据的不一致。可以通过为新项目定义一个可扩展标识号规则来防止该情况的发生。

如果标识号需要很大的数量时,可以将该可扩展标符号的范围扩大至max,其中max是229 – 1, 或536,870,911。如下所示:

message Foo {

  extensions 1000 to max;

}

通常情况下在选择标符号时,标识号产生的规则中应该避开[19000-19999]之间的数字,因为这些已经被Protocol Buffers实现中预留了。

l  包(Package)

当然可以为.proto文件新增一个可选的package声明符,用来防止不同的消息类型有命名冲突。如:

package foo.bar;

message Open { … }

在其他的消息格式定义中可以使用包名+消息名的方式来定义域的类型,如:

message Foo {

  …

  required foo.bar.Open open = 1;

  …

}

包的声明符会根据使用语言的不同影响生成的代码。对于C++,产生的类会被包装在C++的命名空间中,如上例中的Open会被封装在 foo::bar空间中;对于Java,包声明符会变为java的一个包,除非在.proto文件中提供了一个明确有java_package;对于 Python,这个包声明符是被忽略的,因为Python模块是按照其在文件系统中的位置进行组织的。

Ø  包及名称的解析

Protocol buffer语言中类型名称的解析与C++是一致的:首先从最内部开始查找,依次向外进行,每个包会被看作是其父类包的内部类。当然对于 (foo.bar.Baz)这样以“.”分隔的意味着是从最外围开始的。ProtocolBuffer编译器会解析.proto文件中定义的所有类型名。 对于不同语言的代码生成器会知道如何来指向每个具体的类型,即使它们使用了不同的规则。

l  定义服务(Service)

如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。如,想要定义一个RPC服务并具有一个方法,该方法能够接收 SearchRequest并返回一个SearchResponse,此时可以在.proto文件中进行如下定义:

service SearchService {

  rpc Search (SearchRequest) returns (SearchResponse);

}

protocol编译器将产生一个抽象接口SearchService以及一个相应的存根实现。存根将所有的调用指向RpcChannel,它是一 个抽象接口,必须在RPC系统中对该接口进行实现。如,可以实现RpcChannel以完成序列化消息并通过HTTP方式来发送到一个服务器。换句话说, 产生的存根提供了一个类型安全的接口用来完成基于protocolbuffer的RPC调用,而不是将你限定在一个特定的RPC的实现中。C++中的代码 如下所示:

using google::protobuf;

protobuf::RpcChannel* channel;
protobuf::RpcController* controller;
SearchService* service;
SearchRequest request;
SearchResponse response;

void DoSearch() {
// You provide classes MyRpcChannel and MyRpcController, which implement
// the abstract interfaces protobuf::RpcChannel and protobuf::RpcController.
channel = new MyRpcChannel(“somehost.example.com:1234”);
controller = new MyRpcController;

// The protocol compiler generates the SearchService class based on the
// definition given above.

service = new SearchService::Stub(channel);
// Set up the request.
request.set_query(“protocol buffers”);

  // Execute the RPC.
service->Search(controller, request, response, protobuf::NewCallback(&Done));
}

void Done() {
delete service;
delete channel;
delete controller;
}

所有service类都必须实现Service接口,它提供了一种用来调用具体方法的方式,即在编译期不需要知道方法名及它的输入、输出类型。在服务器端,通过服务注册它可以被用来实现一个RPC Server。

using google::protobuf;

class ExampleSearchService : public SearchService {
public:
void Search(protobuf::RpcController* controller,
const SearchRequest* request,
SearchResponse* response,
protobuf::Closure* done) {
if (request->query() == “google”) {
response->add_result()->set_url(“http://www.google.com”);
} else if (request->query() == “protocol buffers”) {
response->add_result()->set_url(“http://protobuf.googlecode.com”);
}
done->Run();
}
};

int main() {
// You provide class MyRpcServer.  It does not have to implement any
// particular interface; this is just an example.
MyRpcServer server;

protobuf::Service* service = new ExampleSearchService;
server.ExportOnPort(1234, service);
server.Run();

delete service;
return 0;
}

l  选项(Options)

在定义.proto文件时能够标注一系列的options。Options并不改变整个文件声明的含义,但却能够影响特定环境下处理方式。完整的可用选项可以在google/protobuf/descriptor.proto找到。

一些选项是文件级别的,意味着它可以作用于最外范围,不包含在任何消息内部、enum或服务定义中。一些选项是消息级别的,意味着它可以用在消息定 义的内部。当然有些选项可以作用在域、enum类型、enum值、服务类型及服务方法中。到目前为止,并没有一种有效的选项能作用于所有的类型。

如下就是一些常用的选择:

²  java_package (file option): 这个选项表明生成java类所在的包。如果在.proto文件中没有明确的声明java_package,就采用默认的包名。当然了,默认方式产生的 java包名并不是最好的方式,按照应用名称倒序方式进行排序的。如果不需要产生java代码,则该选项将不起任何作用。如:

option java_package = "com.example.foo";

²  java_outer_classname (file option): 该选项表明想要生成Java类的名称。如果在.proto文件中没有明确的java_outer_classname定义,生成的class名称将会根据.proto文件的名称采用驼峰式的命名方式进行生成。如(foo_bar.proto生成的java类名为FooBar.java),如果不生成java代码,则该选项不起任何作用。如:

option java_outer_classname = "Ponycopter";

²  optimize_for (fileoption): 可以被设置为 SPEED, CODE_SIZE,or LITE_RUNTIME。这些值将通过如下的方式影响C++及java代码的生成:

·        SPEED (default): protocol buffer编译器将通过在消息类型上执行序列化、语法分析及其他通用的操作。这种代码是最优的。

·        CODE_SIZE: protocol buffer编译器将会产生最少量的类,通过共享或基于反射的代码来实现序列化、语法分析及各种其它操作。采用该方式产生的代码将比SPEED要少得多, 但是操作要相对慢些。当然实现的类及其对外的API与SPEED模式都是一样的。这种方式经常用在一些包含大量的.proto文件而且并不盲目追求速度的 应用中。

·        LITE_RUNTIME: protocol buffer编译器依赖于运行时核心类库来生成代码(即采用libprotobuf-lite 替代libprotobuf)。这种核心类库由于忽略了一 些描述符及反射,要比全类库小得多。这种模式经常在移动手机平台应用多一些。编译器采用该模式产生的方法实现与SPEED模式不相上下,产生的类通过实现 MessageLite接口,但它仅仅是Messager接口的一个子集。

option optimize_for = CODE_SIZE;

²  cc_generic_servicesjava_generic_servicespy_generic_services (file options): 在C++、java、python中protocol buffer编译器是否应该基于服务定义产生抽象服务代码。由于历史遗留问题,该值默认是true。但是自2.3.0版本以来,它被认为通过提供代码生成 器插件来对RPC实现更可取,而不是依赖于“抽象”服务。

// This file relies on plugins to generate service code.

option cc_generic_services = false;

option java_generic_services = false;

option py_generic_services = false;

²  message_set_wire_format (message option):如果该值被设置为true,该消息将使用一种不同的二进制格式来与Google内部的MessageSet的老格式相兼容。对于Google外部的用户来说,该选项将不会被用到。如下所示:

message Foo {

  option message_set_wire_format = true;

  extensions 4 to max;

}

²  packed (field option): 如果该选项在一个整型基本类型上被设置为真,则采用更紧凑的编码方式。当然使用该值并不会对数值造成任何损失。在2.3.0版本之前,解析器将会忽略那些 非期望的包装值。因此,它不可能在不破坏现有框架的兼容性上而改变压缩格式。在2.3.0之后,这种改变将是安全的,解析器能够接受上述两种格式,但是在 处理protobuf老版本程序时,还是要多留意一下。

repeated int32 samples = 4 [packed=true];

²  deprecated (field option): 如果该选项被设置为true,表明该字段已经被弃用了,在新代码中不建议使用。在多数语言中,这并没有实际的含义。在java中,它将会变成一个 @Deprecated注释。也许在将来,其它基于语言声明的代码在生成时也会如此使用,当使用该字段时,编译器将自动报警。如:

optional int32 old_field = 6 [deprecated=true];

Ø 自定义选项

ProtocolBuffers允许自定义并使用选项。该功能应该属于一个高级特性,对于大部分人是用不到的。由于options是定在 google/protobuf/descriptor.proto中的,因此你可以在该文件中进行扩展,定义自己的选项。如:

import "google/protobuf/descriptor.proto";

 

extend google.protobuf.MessageOptions {

  optional string my_option = 51234;

}

 

message MyMessage {

  option (my_option) = "Hello world!";

}

在上述代码中,通过对MessageOptions进行扩展定义了一个新的消息级别的选项。当使用该选项时,选项的名称需要使用()包裹起来,以表明它是一个扩展。在C++代码中可以看出my_option是以如下方式被读取的。

string value = MyMessage::descriptor()->options().GetExtension(my_option);

在Java代码中的读取方式如下:

String value = MyProtoFile.MyMessage.getDescriptor().getOptions().getExtension(MyProtoFile.myOption);

正如上面的读取方式,定制选项对于Python并不支持。定制选项在protocol buffer语言中可用于任何结构。下面就是一些具体的例子:

import "google/protobuf/descriptor.proto";

 

extend google.protobuf.FileOptions {

  optional string my_file_option = 50000;

}

extend google.protobuf.MessageOptions {

  optional int32 my_message_option = 50001;

}

extend google.protobuf.FieldOptions {

  optional float my_field_option = 50002;

}

extend google.protobuf.EnumOptions {

  optional bool my_enum_option = 50003;

}

extend google.protobuf.EnumValueOptions {

  optional uint32 my_enum_value_option = 50004;

}

extend google.protobuf.ServiceOptions {

  optional MyEnum my_service_option = 50005;

}

extend google.protobuf.MethodOptions {

  optional MyMessage my_method_option = 50006;

}

 

option (my_file_option) = "Hello world!";

 

message MyMessage {

  option (my_message_option) = 1234;

 

  optional int32 foo = 1 [(my_field_option) = 4.5];

  optional string bar = 2;

}

 

enum MyEnum {

  option (my_enum_option) = true;

 

  FOO = 1 [(my_enum_value_option) = 321];

  BAR = 2;

}

 

message RequestType {}

message ResponseType {}

 

service MyService {

  option (my_service_option) = FOO;

 

  rpc MyMethod(RequestType) returns(ResponseType) {

    // Note:  my_method_option has type MyMessage.  We can set each field

    //   within it using a separate "option" line.

    option (my_method_option).foo = 567;

    option (my_method_option).bar = "Some string";

  }

}

注:如果要在该选项定义之外使用一个自定义的选项,必须要由包名 + 选项名来定义该选项。如:

// foo.proto

import "google/protobuf/descriptor.proto";

package foo;

extend google.protobuf.MessageOptions {

  optional string my_option = 51234;

}

// bar.proto

import "foo.proto";

package bar;

message MyMessage {

  option (foo.my_option) = "Hello world!";

}

最后一件事情需要注意:因为自定义选项是可扩展的,它必须象其它的域或扩展一样来定义标识号。正如上述示例,[50000-99999]已经被占 用,该范围内的值已经被内部所使用,当然了你可以在内部应用中随意使用。如果你想在一些公共应用中进行自定义选项,你必须确保它是全局唯一的。可以通过protobuf-global-extension-registry@google.com来获取全局唯一标识号。

l  生成访问类

可以通过定义好的.proto文件来生成Java、Python、C++代码,需要基于.proto文件运行protocol buffer编译器protoc。运行的命令如下所示:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto

·        IMPORT_PATH声明了一个.proto文件所在的具体目录。如果忽略该值,则使用当前目录。如果有多个目录则可以 对–proto_path 写多次,它们将会顺序的被访问并执行导入。-I=IMPORT_PATH是它的简化形式。

·        当然也可以提供一个或多个输出路径:

o   –cpp_out 在目标目录DST_DIR中产生C++代码,可以在 http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference /cpp-generated.html中查看更多。

o   –java_out 在目标目录DST_DIR中产生Java代码,可以在 http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference /java-generated.html中查看更多。

o   –python_out 在目标目录 DST_DIR 中产生Python代码,可以在http://code.google.com/intl/zh-CN/apis/protocolbuffers /docs/reference/python-generated.html中查看更多。

     作为一种额外的使得,如果DST_DIR 是以.zip或.jar结尾的,编译器将输出结果打包成一个zip格式的归档文件。.jar将会输出一个 Java JAR声明必须的manifest文件。注:如果该输出归档文件已经存在,它将会被重写,编译器并没有做到足够的智能来为已经存在的归档文件添加新的文 件。

·        你必须提供一个或多个.proto文件作为输入。多个.proto文件能够一次全部声明。虽然这些文件是相对于当前目录来命名的,每个文件必须在一个IMPORT_PATH中,只有如此编译器才可以决定它的标准名称。

from:http://www.open-open.com/home/space.php?uid=37924&do=blog&id=5873

=========================================================

ProtoBuf开发者指南:http://gashero.yeax.com/?p=108

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

语言指南
http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/proto.html

风格
http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/style.html

[转]Google Protocol Buffer 的使用和原理 - heiyeluren的blog(黑夜路人的开源世界) - 博客频道 - CSDN.NET

mikel阅读(886)

来源: [转]Google Protocol Buffer 的使用和原理 – heiyeluren的blog(黑夜路人的开源世界) – 博客频道 – CSDN.NET

[转]Google Protocol Buffer 的使用和原理

标签: buffergooglecompiler数据结构descriptor编译器
13607人阅读 评论(2) 收藏 举报
分类:

 

作者:刘明
来源:http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/?ca=drs-tp4608

 

 

简介

什么是 Google Protocol Buffer? 假如您在网上搜索,应该会得到类似这样的文字介绍:

Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、JavaPython 三种语言的 API。

或许您和我一样,在第一次看完这些介绍后还是不明白 Protobuf 究竟是什么,那么我想一个简单的例子应该比较有助于理解它。


一个简单的例子

安装 Google Protocol Buffer

网站 http://code.google.com/p/protobuf/downloads/list上可以下载 Protobuf 的源代码。然后解压编译安装便可以使用它了。

安装步骤如下所示:

 tar -xzf protobuf-2.1.0.tar.gz 
 cd protobuf-2.1.0 
 ./configure --prefix=$INSTALL_DIR 
 make 
 make check 
 make install 

关于简单例子的描述

我打算使用 Protobuf 和 C++ 开发一个十分简单的例子程序。

该程序由两部分组成。第一部分被称为 Writer,第二部分叫做 Reader。

Writer 负责将一些结构化的数据写入一个磁盘文件,Reader 则负责从该磁盘文件中读取结构化数据并打印到屏幕上。

准备用于演示的结构化数据是 HelloWorld,它包含两个基本数据:

  • ID,为一个整数类型的数据
  • Str,这是一个字符串

书写 .proto 文件

首先我们需要编写一个 proto 文件,定义我们程序中需要处理的结构化数据,在 protobuf 的术语中,结构化数据被称为 Message。proto 文件非常类似 java 或者 C 语言的数据定义。代码清单 1 显示了例子应用中的 proto 文件内容。
清单 1. proto 文件

				
 package lm; 
 message helloworld 
 { 
    required int32     id = 1;  // ID 
    required string    str = 2;  // str 
    optional int32     opt = 3;  //optional field 
 } 

一个比较好的习惯是认真对待 proto 文件的文件名。比如将命名规则定于如下:

 packageName.MessageName.proto 

在上例中,package 名字叫做 lm,定义了一个消息 helloworld,该消息有三个成员,类型为 int32 的 id,另一个为类型为 string 的成员 str。opt 是一个可选的成员,即消息中可以不包含该成员。

编译 .proto 文件

写好 proto 文件之后就可以用 Protobuf 编译器将该文件编译成目标语言了。本例中我们将使用 C++。

假设您的 proto 文件存放在 $SRC_DIR 下面,您也想把生成的文件放在同一个目录下,则可以使用如下命令:

 protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto 

命令将生成两个文件:

lm.helloworld.pb.h , 定义了 C++ 类的头文件

lm.helloworld.pb.cc , C++ 类的实现文件

在生成的头文件中,定义了一个 C++ 类 helloworld,后面的 Writer 和 Reader 将使用这个类来对消息进行操作。诸如对消息的成员进行赋值,将消息序列化等等都有相应的方法。

编写 writer 和 Reader

如前所述,Writer 将把一个结构化数据写入磁盘,以便其他人来读取。假如我们不使用 Protobuf,其实也有许多的选择。一个可能的方法是将数据转换为字符串,然后将字符串写入磁盘。转换为字符串的方法可以使用 sprintf(),这非常简单。数字 123 可以变成字符串”123”。

这样做似乎没有什么不妥,但是仔细考虑一下就会发现,这样的做法对写 Reader 的那个人的要求比较高,Reader 的作者必须了 Writer 的细节。比如”123”可以是单个数字 123,但也可以是三个数字 1,2 和 3,等等。这么说来,我们还必须让 Writer 定义一种分隔符一样的字符,以便 Reader 可以正确读取。但分隔符也许还会引起其他的什么问题。最后我们发现一个简单的 Helloworld 也需要写许多处理消息格式的代码。

如果使用 Protobuf,那么这些细节就可以不需要应用程序来考虑了。

使用 Protobuf,Writer 的工作很简单,需要处理的结构化数据由 .proto 文件描述,经过上一节中的编译过程后,该数据化结构对应了一个 C++ 的类,并定义在 lm.helloworld.pb.h 中。对于本例,类名为 lm::helloworld。

Writer 需要 include 该头文件,然后便可以使用这个类了。

现在,在 Writer 代码中,将要存入磁盘的结构化数据由一个 lm::helloworld 类的对象表示,它提供了一系列的 get/set 函数用来修改和读取结构化数据中的数据成员,或者叫 field。

当我们需要将该结构化数据保存到磁盘上时,类 lm::helloworld 已经提供相应的方法来把一个复杂的数据变成一个字节序列,我们可以将这个字节序列写入磁盘。

对于想要读取这个数据的程序来说,也只需要使用类 lm::helloworld 的相应反序列化方法来将这个字节序列重新转换会结构化数据。这同我们开始时那个“123”的想法类似,不过 Protobuf 想的远远比我们那个粗糙的字符串转换要全面,因此,我们不如放心将这类事情交给 Protobuf 吧。

程序清单 2 演示了 Writer 的主要代码,您一定会觉得很简单吧?
清单 2. Writer 的主要代码

				
 #include "lm.helloworld.pb.h"
…

 int main(void) 
 { 
  
  lm::helloworld msg1; 
  msg1.set_id(101); 
  msg1.set_str(“hello”); 
    
  // Write the new address book back to disk. 
  fstream output("./log", ios::out | ios::trunc | ios::binary); 
        
  if (!msg1.SerializeToOstream(&output)) { 
      cerr << "Failed to write msg." << endl; 
      return -1; 
  }         
  return 0; 
 } 

Msg1 是一个 helloworld 类的对象,set_id() 用来设置 id 的值。SerializeToOstream 将对象序列化后写入一个 fstream 流。

代码清单 3 列出了 reader 的主要代码。
清单 3. Reader

				
 #include "lm.helloworld.pb.h" 
…
 void ListMsg(const lm::helloworld & msg) { 
  cout << msg.id() << endl; 
  cout << msg.str() << endl; 
 } 
 
 int main(int argc, char* argv[]) { 

  lm::helloworld msg1; 
 
  { 
    fstream input("./log", ios::in | ios::binary); 
    if (!msg1.ParseFromIstream(&input)) { 
      cerr << "Failed to parse address book." << endl; 
      return -1; 
    } 
  } 
 
  ListMsg(msg1); 
  … 
 } 

同样,Reader 声明类 helloworld 的对象 msg1,然后利用 ParseFromIstream 从一个 fstream 流中读取信息并反序列化。此后,ListMsg 中采用 get 方法读取消息的内部信息,并进行打印输出操作。

运行结果

运行 Writer 和 Reader 的结果如下:

 >writer 
 >reader 
 101 
 Hello 

Reader 读取文件 log 中的序列化信息并打印到屏幕上。本文中所有的例子代码都可以在附件中下载。您可以亲身体验一下。

这个例子本身并无意义,但只要您稍加修改就可以将它变成更加有用的程序。比如将磁盘替换为网络 socket,那么就可以实现基于网络的数据交换任务。而存储和交换正是 Protobuf 最有效的应用领域。


和其他类似技术的比较

看完这个简单的例子之后,希望您已经能理解 Protobuf 能做什么了,那么您可能会说,世上还有很多其他的类似技术啊,比如 XML,JSON,Thrift 等等。和他们相比,Protobuf 有什么不同呢?

简单说来 Protobuf 的主要优点就是:简单,快。

这有测试为证,项目 thrift-protobuf-compare 比较了这些类似的技术,图 1 显示了该项目的一项测试结果,Total Time.
图 1. 性能测试结果
图 1. 性能测试结果

Total Time 指一个对象操作的整个时间,包括创建对象,将对象序列化为内存中的字节序列,然后再反序列化的整个过程。从测试结果可以看到 Protobuf 的成绩很好,感兴趣的读者可以自行到网站 http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking上了解更详细的测试结果。

Protobuf 的优点

Protobuf 有如 XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况 下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。

它有一个非常棒的特性,即“向后”兼容性好,人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构进行升级。这样您的程序就可以不必担 心因为消息结构的改变而造成的大规模的代码重构或者迁移的问题。因为添加新的消息中的 field 并不会引起已经发布的程序的任何改变。

Protobuf 语义更清晰,无需类似 XML 解析器的东西(因为 Protobuf 编译器会将 .proto 文件编译生成对应的数据访问类以对 Protobuf 数据进行序列化、反序列化操作)。

使用 Protobuf 无需学习复杂的文档对象模型,Protobuf 的编程模式比较友好,简单易学,同时它拥有良好的文档和示例,对于喜欢简单事物的人们而言,Protobuf 比其他的技术更加有吸引力。

Protobuf 的不足

Protbuf 与 XML 相比也有不足之处。它功能简单,无法用来表示复杂的概念。

XML 已经成为多种行业标准的编写工具,Protobuf 只是 Google 公司内部使用的工具,在通用性上还差很多。

由于文本并不适合用来描述数据结构,所以 Protobuf 也不适合用来对基于文本的标记文档(如 HTML)建模。另外,由于 XML 具有某种程度上的自解释性,它可以被人直接读取编辑,在这一点上 Protobuf 不行,它以二进制的方式存储,除非你有 .proto 定义,否则你没法直接读出 Protobuf 的任何内容【 2 】。


高级应用话题

更复杂的 Message

到这里为止,我们只给出了一个简单的没有任何用处的例子。在实际应用中,人们往往需要定义更加复杂的 Message。我们用“复杂”这个词,不仅仅是指从个数上说有更多的 fields 或者更多类型的 fields,而是指更加复杂的数据结构:

嵌套 Message

嵌套是一个神奇的概念,一旦拥有嵌套能力,消息的表达能力就会非常强大。

代码清单 4 给出一个嵌套 Message 的例子。
清单 4. 嵌套 Message 的例子

				
 message Person { 
  required string name = 1; 
  required int32 id = 2;        // Unique ID number for this person. 
  optional string email = 3; 

  enum PhoneType { 
    MOBILE = 0; 
    HOME = 1; 
    WORK = 2; 
  } 

  message PhoneNumber { 
    required string number = 1; 
    optional PhoneType type = 2 [default = HOME]; 
  } 
  repeated PhoneNumber phone = 4; 
 } 

在 Message Person 中,定义了嵌套消息 PhoneNumber,并用来定义 Person 消息中的 phone 域。这使得人们可以定义更加复杂的数据结构。

4.1.2 Import Message

在一个 .proto 文件中,还可以用 Import 关键字引入在其他 .proto 文件中定义的消息,这可以称做 Import Message,或者 Dependency Message。

比如下例:
清单 5. 代码

				
 import common.header; 

 message youMsg{ 
  required common.info_header header = 1; 
  required string youPrivateData = 2; 
 } 

其中 ,common.info_header定义在common.header包内。

Import Message 的用处主要在于提供了方便的代码管理机制,类似 C 语言中的头文件。您可以将一些公用的 Message 定义在一个 package 中,然后在别的 .proto 文件中引入该 package,进而使用其中的消息定义。

Google Protocol Buffer 可以很好地支持嵌套 Message 和引入 Message,从而让定义复杂的数据结构的工作变得非常轻松愉快。

动态编译

一般情况下,使用 Protobuf 的人们都会先写好 .proto 文件,再用 Protobuf 编译器生成目标语言所需要的源代码文件。将这些生成的代码和应用程序一起编译。

可是在某且情况下,人们无法预先知道 .proto 文件,他们需要动态处理一些未知的 .proto 文件。比如一个通用的消息转发中间件,它不可能预知需要处理怎样的消息。这需要动态编译 .proto 文件,并使用其中的 Message。

Protobuf 提供了 google::protobuf::compiler 包来完成动态编译的功能。主要的类叫做 importer,定义在 importer.h 中。使用 Importer 非常简单,下图展示了与 Import 和其它几个重要的类的关系。图 2. Importer 类
图 2. Importer 类

 

Import 类对象中包含三个主要的对象,分别为处理错误的 MultiFileErrorCollector 类,定义 .proto 文件源目录的 SourceTree 类。

下面还是通过实例说明这些类的关系和使用吧。

对于给定的 proto 文件,比如 lm.helloworld.proto,在程序中动态编译它只需要很少的一些代码。如代码清单 6 所示。
清单 6. 代码

				
 google::protobuf::compiler::MultiFileErrorCollector errorCollector;
 google::protobuf::compiler::DiskSourceTree sourceTree; 

 google::protobuf::compiler::Importer importer(&sourceTree, &errorCollector); 
 sourceTree.MapPath("", protosrc); 

 importer.import(“lm.helloworld.proto”); 

首先构造一个 importer 对象。构造函数需要两个入口参数,一个是 source Tree 对象,该对象指定了存放 .proto 文件的源目录。第二个参数是一个 error collector 对象,该对象有一个 AddError 方法,用来处理解析 .proto 文件时遇到的语法错误。

之后,需要动态编译一个 .proto 文件时,只需调用 importer 对象的 import 方法。非常简单。

那么我们如何使用动态编译后的 Message 呢?我们需要首先了解几个其他的类

Package google::protobuf::compiler 中提供了以下几个类,用来表示一个 .proto 文件中定义的 message,以及 Message 中的 field,如图所示。
图 3. 各个 Compiler 类之间的关系
图 3. 各个 Compiler 类之间的关系

类 FileDescriptor 表示一个编译后的 .proto 文件;类 Descriptor 对应该文件中的一个 Message;类 FieldDescriptor 描述一个 Message 中的一个具体 Field。

比如编译完 lm.helloworld.proto 之后,可以通过如下代码得到 lm.helloworld.id 的定义:
清单 7. 得到 lm.helloworld.id 的定义的代码

				
 const protobuf::Descriptor *desc = 
    importer_.pool()->FindMessageTypeByName(“lm.helloworld”); 
 const protobuf::FieldDescriptor* field = 
    desc->pool()->FindFileByName (“id”); 

通过 Descriptor,FieldDescriptor 的各种方法和属性,应用程序可以获得各种关于 Message 定义的信息。比如通过 field->name() 得到 field 的名字。这样,您就可以使用一个动态定义的消息了。

编写新的 proto 编译器

随 Google Protocol Buffer 源代码一起发布的编译器 protoc 支持 3 种编程语言:C++,java 和 Python。但使用 Google Protocol Buffer 的 Compiler 包,您可以开发出支持其他语言的新的编译器。

类 CommandLineInterface 封装了 protoc 编译器的前端,包括命令行参数的解析,proto 文件的编译等功能。您所需要做的是实现类 CodeGenerator 的派生类,实现诸如代码生成等后端工作:

程序的大体框架如图所示:
图 4. XML 编译器框图
图 4. XML 编译器框图

在 main() 函数内,生成 CommandLineInterface 的对象 cli,调用其 RegisterGenerator() 方法将新语言的后端代码生成器 yourG 对象注册给 cli 对象。然后调用 cli 的 Run() 方法即可。

这样生成的编译器和 protoc 的使用方法相同,接受同样的命令行参数,cli 将对用户输入的 .proto 进行词法语法等分析工作,最终生成一个语法树。该树的结构如图所示。
图 5. 语法树
图 5. 语法树

其根节点为一个 FileDescriptor 对象(请参考“动态编译”一节),并作为输入参数被传入 yourG 的 Generator() 方法。在这个方法内,您可以遍历语法树,然后生成对应的您所需要的代码。简单说来,要想实现一个新的 compiler,您只需要写一个 main 函数,和一个实现了方法 Generator() 的派生类即可。

在本文的下载附件中,有一个参考例子,将 .proto 文件编译生成 XML 的 compiler,可以作为参考。


Protobuf 的更多细节

人们一直在强调,同 XML 相比, Protobuf 的主要优点在于性能高。它以高效的二进制方式存储,比 XML 小 3 到 10 倍,快 20 到 100 倍。

对于这些 “小 3 到 10 倍”,“快 20 到 100 倍”的说法,严肃的程序员需要一个解释。因此在本文的最后,让我们稍微深入 Protobuf 的内部实现吧。

有两项技术保证了采用 Protobuf 的程序能获得相对于 XML 极大的性能提高。

第一点,我们可以考察 Protobuf 序列化后的信息内容。您可以看到 Protocol Buffer 信息的表示非常紧凑,这意味着消息的体积减少,自然需要更少的资源。比如网络上传输的字节数更少,需要的 IO 更少等,从而提高性能。

第二点我们需要理解 Protobuf 封解包的大致过程,从而理解为什么会比 XML 快很多。

Google Protocol Buffer 的 Encoding

Protobuf 序列化后所生成的二进制消息非常紧凑,这得益于 Protobuf 采用的非常巧妙的 Encoding 方法。

考察消息结构之前,让我首先要介绍一个叫做 Varint 的术语。

Varint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。

比如对于 int32 类型的数字,一般需要 4 个 byte 来表示。但是采用 Varint,对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示。当然凡事都有好的也有不好的一面,采用 Varint 表示法,大的数字则需要 5 个 byte 来表示。从统计的角度来说,一般不会所有的消息中的数字都是大数,因此大多数情况下,采用 Varint 后,可以用更少的字节数来表示数字信息。下面就详细介绍一下 Varint。

Varint 中的每个 byte 的最高位 bit 有特殊的含义,如果该位为 1,表示后续的 byte 也是该数字的一部分,如果该位为 0,则结束。其他的 7 个 bit 都用来表示数字。因此小于 128 的数字都可以用一个 byte 表示。大于 128 的数字,比如 300,会用两个字节来表示:1010 1100 0000 0010

下图演示了 Google Protocol Buffer 如何解析两个 bytes。注意到最终计算前将两个 byte 的位置相互交换过一次,这是因为 Google Protocol Buffer 字节序采用 little-endian 的方式。
图 6. Varint 编码
图 6. Varint 编码

消息经过序列化后会成为一个二进制数据流,该流中的数据为一系列的 Key-Value 对。如下图所示:
图 7. Message Buffer
图 7. Message Buffer

采用这种 Key-Pair 结构无需使用分隔符来分割不同的 Field。对于可选的 Field,如果消息中不存在该 field,那么在最终的 Message Buffer 中就没有该 field,这些特性都有助于节约消息本身的大小。

以代码清单 1 中的消息为例。假设我们生成如下的一个消息 Test1:

 Test1.id = 10; 
 Test1.str = “hello”;

则最终的 Message Buffer 中有两个 Key-Value 对,一个对应消息中的 id;另一个对应 str。

Key 用来标识具体的 field,在解包的时候,Protocol Buffer 根据 Key 就可以知道相应的 Value 应该对应于消息中的哪一个 field。

Key 的定义如下:

 (field_number << 3) | wire_type 

可以看到 Key 由两部分组成。第一部分是 field_number,比如消息 lm.helloworld 中 field id 的 field_number 为 1。第二部分为 wire_type。表示 Value 的传输类型。

Wire Type 可能的类型如下表所示:
表 1. Wire Type

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

在我们的例子当中,field id 所采用的数据类型为 int32,因此对应的 wire type 为 0。细心的读者或许会看到在 Type 0 所能表示的数据类型中有 int32 和 sint32 这两个非常类似的数据类型。Google Protocol Buffer 区别它们的主要意图也是为了减少 encoding 后的字节数。

在计算机内,一个负数一般会被表示为一个很大的整数,因为计算机定义负数的符号位为数字的最高位。如果采用 Varint 表示一个负数,那么一定需要 5 个 byte。为此 Google Protocol Buffer 定义了 sint32 这种类型,采用 zigzag 编码。

Zigzag 编码用无符号数来表示有符号数字,正数和负数交错,这就是 zigzag 这个词的含义了。

如图所示:
图 8. ZigZag 编码
图 8. ZigZag 编码

使用 zigzag 编码,绝对值小的数字,无论正负都可以采用较少的 byte 来表示,充分利用了 Varint 这种技术。

其他的数据类型,比如字符串等则采用类似数据库中的 varchar 的表示方法,即用一个 varint 表示长度,然后将其余部分紧跟在这个长度部分之后即可。

通过以上对 protobuf Encoding 方法的介绍,想必您也已经发现 protobuf 消息的内容小,适于网络传输。假如您对那些有关技术细节的描述缺乏耐心和兴趣,那么下面这个简单而直观的比较应该能给您更加深刻的印象。

对于代码清单 1 中的消息,用 Protobuf 序列化后的字节序列为:

 08 65 12 06 48 65 6C 6C 6F 77 

而如果用 XML,则类似这样:

 31 30 31 3C 2F 69 64 3E 3C 6E 61 6D 65 3E 68 65 
 6C 6C 6F 3C 2F 6E 61 6D 65 3E 3C 2F 68 65 6C 6C 
 6F 77 6F 72 6C 64 3E 

一共 55 个字节,这些奇怪的数字需要稍微解释一下,其含义用 ASCII 表示如下:
 <helloworld> 
    <id>101</id> 
    <name>hello</name> 
 </helloworld> 

封解包的速度

首先我们来了解一下 XML 的封解包过程。XML 需要从文件中读取出字符串,再转换为 XML 文档对象结构模型。之后,再从 XML 文档对象结构模型中读取指定节点的字符串,最后再将这个字符串转换成指定类型的变量。这个过程非常复杂,其中将 XML 文件转换为文档对象结构模型的过程通常需要完成词法文法分析等大量消耗 CPU 的复杂计算。

反观 Protobuf,它只需要简单地将一个二进制序列,按照指定的格式读取到 C++ 对应的结构类型中就可以了。从上一节的描述可以看到消息的 decoding 过程也可以通过几个位移操作组成的表达式计算即可完成。速度非常快。

为了说明这并不是我拍脑袋随意想出来的说法,下面让我们简单分析一下 Protobuf 解包的代码流程吧。

以代码清单 3 中的 Reader 为例,该程序首先调用 msg1 的 ParseFromIstream 方法,这个方法解析从文件读入的二进制数据流,并将解析出来的数据赋予 helloworld 类的相应数据成员。

该过程可以用下图表示:
图 9. 解包流程图
图 9. 解包流程图

整个解析过程需要 Protobuf 本身的框架代码和由 Protobuf 编译器生成的代码共同完成。Protobuf 提供了基类 Message 以及 Message_lite 作为通用的 Framework,,CodedInputStream 类,WireFormatLite 类等提供了对二进制数据的 decode 功能,从 5.1 节的分析来看,Protobuf 的解码可以通过几个简单的数学运算完成,无需复杂的词法语法分析,因此 ReadTag() 等方法都非常快。在这个调用路径上的其他类和方法都非常简单,感兴趣的读者可以自行阅读。相对于 XML 的解析过程,以上的流程图实在是非常简单吧?这也就是 Protobuf 效率高的第二个原因了。


结束语

往往了解越多,人们就会越觉得自己无知。我惶恐地发现自己竟然写了一篇关于序列化的文章,文中必然有许多想当然而自以为是的东西,还希望各位能够去伪存真,更希望真的高手能不吝赐教,给我来信。谢谢。

参考资料

学习

讨论

Google Protocol Buffer技术详解(语言规范) - Stephen_Liu - 博客园

mikel阅读(961)

来源: Protocol Buffer技术详解(语言规范) – Stephen_Liu – 博客园

      该系列Blog的内容主体主要源自于Protocol Buffer的官方文档,而代码示例则抽取于当前正在开发的一个公司内部项目的Demo。这样做的目的主要在于不仅可以保持Google文档的良好风格和 系统性,同时再结合一些比较实用和通用的用例,这样就更加便于公司内部的培训,以及和广大网友的技术交流。需要说明的是,Blog的内容并非line by line的翻译,其中包含一些经验性总结,与此同时,对于一些不是非常常用的功能并未予以说明,有兴趣的开发者可以直接查阅Google的官方文档。

      一、为什么使用Protocol Buffer?
在回答这个问题之前,我们还是先给出一个在实际开发中经常会遇到的系统场景。比如:我们的客户端程序是使用Java开发的,可能运行自不同的平台, 如:Linux、Windows或者是Android,而我们的服务器程序通常是基于Linux平台并使用C++开发完成的。在这两种程序之间进行数据通讯时存在多种方式用于设计消息格式,如:
1. 直接传递C/C++语言中一字节对齐的结构体数据,只要结构体的声明为定长格式,那么该方式对于C/C++程序而言就非常方便了,仅需将接收到的数据按照 结构体类型强行转换即可。事实上对于变长结构体也不会非常麻烦。在发送数据时,也只需定义一个结构体变量并设置各个成员变量的值之后,再以char*的方 式将该二进制数据发送到远端。反之,该方式对于Java开发者而言就会非常繁琐,首先需要将接收到的数据存于ByteBuffer之中,再根据约定的字节 序逐个读取每个字段,并将读取后的值再赋值给另外一个值对象中的域变量,以便于程序中其他代码逻辑的编写。对于该类型程序而言,联调的基准是必须客户端和 服务器双方均完成了消息报文构建程序的编写后才能展开,而该设计方式将会直接导致Java程序开发的进度过慢。即便是Debug阶段,也会经常遇到 Java程序中出现各种域字段拼接的小错误。
2. 使用SOAP协议(WebService)作为消息报文的格式载体,由该方式生成的报文是基于文本格式的,同时还存在大量的XML描述信息,因此将会大大 增加网络IO的负担。又由于XML解析的复杂性,这也会大幅降低报文解析的性能。总之,使用该设计方式将会使系统的整体运行性能明显下降。
对于以上两种方式所产生的问题,Protocol Buffer均可以很好的解决,不仅如此,Protocol Buffer还有一个非常重要的优点就是可以保证同一消息报文新旧版本之间的兼容性。至于具体的方式我们将会在后续的博客中给出。

      二、定义第一个Protocol Buffer消息。
创建扩展名为.proto的文件,如:MyMessage.proto,并将以下内容存入该文件中。
      message LogonReqMessage {
          required int64 acctID = 1;
          required string passwd = 2;
      }
这里将给出以上消息定义的关键性说明。
1. message是消息定义的关键字,等同于C++中的struct/class,或是Java中的class。
2. LogonReqMessage为消息的名字,等同于结构体名或类名。
3. required前缀表示该字段为必要字段,既在序列化和反序列化之前该字段必须已经被赋值。与此同时,在Protocol Buffer中还存在另外两个类似的关键字,optional和repeated,带有这两种限定符的消息字段则没有required字段这样的限制。相 比于optional,repeated主要用于表示数组字段。具体的使用方式在后面的用例中均会一一列出。
4. int64和string分别表示长整型和字符串型的消息字段,在Protocol Buffer中存在一张类型对照表,既Protocol Buffer中的数据类型与其他编程语言(C++/Java)中所用类型的对照。该对照表中还将给出在不同的数据场景下,哪种类型更为高效。该对照表将在 后面给出。
5. acctID和passwd分别表示消息字段名,等同于Java中的域变量名,或是C++中的成员变量名。
6. 标签数字12则 表示不同的字段在序列化后的二进制数据中的布局位置。在该例中,passwd字段编码后的数据一定位于acctID之后。需要注意的是该值在同一 message中不能重复。另外,对于Protocol Buffer而言,标签值为1到15的字段在编码时可以得到优化,既标签值和类型信息仅占有一个byte,标签范围是16到2047的将占有两个 bytes,而Protocol Buffer可以支持的字段数量则为2的29次方减一。有鉴于此,我们在设计消息结构时,可以尽可能考虑让repeated类型的字段标签位于1到15之 间,这样便可以有效的节省编码后的字节数量。

      三、定义第二个(含有枚举字段)Protocol Buffer消息。
      //在定义Protocol Buffer的消息时,可以使用和C++/Java代码同样的方式添加注释。
      enum UserStatus {
          OFFLINE = 0;  //表示处于离线状态的用户
          ONLINE = 1;   //表示处于在线状态的用户
      }
      message UserInfo {
          required int64 acctID = 1;
          required string name = 2;
          required UserStatus status = 3;
      }
这里将给出以上消息定义的关键性说明(仅包括上一小节中没有描述的)。
1. enum是枚举类型定义的关键字,等同于C++/Java中的enum。
2. UserStatus为枚举的名字。
3. 和C++/Java中的枚举不同的是,枚举值之间的分隔符是分号,而不是逗号。
4. OFFLINE/ONLINE为枚举值。
5. 0和1表示枚举值所对应的实际整型值,和C/C++一样,可以为枚举值指定任意整型值,而无需总是从0开始定义。如:
      enum OperationCode {
          LOGON_REQ_CODE = 101;
          LOGOUT_REQ_CODE = 102;
          RETRIEVE_BUDDIES_REQ_CODE = 103;
    
          LOGON_RESP_CODE = 1001;
          LOGOUT_RESP_CODE = 1002;
          RETRIEVE_BUDDIES_RESP_CODE = 1003;
      }

      四、定义第三个(含有嵌套消息字段)Protocol Buffer消息。
我们可以在同一个.proto文件中定义多个message,这样便可以很容易的实现嵌套消息的定义。如:
      enum UserStatus {
          OFFLINE = 0;
          ONLINE = 1;
      }
      message UserInfo {
          required int64 acctID = 1;
          required string name = 2;
          required UserStatus status = 3;
      }
      message LogonRespMessage {
          required LoginResult logonResult = 1;
          required UserInfo userInfo = 2;
      }
这里将给出以上消息定义的关键性说明(仅包括上两小节中没有描述的)。
1. LogonRespMessage消息的定义中包含另外一个消息类型作为其字段,如UserInfo userInfo。
2. 上例中的UserInfo和LogonRespMessage被定义在同一个.proto文件中,那么我们是否可以包含在其他.proto文件中定义的 message呢?Protocol Buffer提供了另外一个关键字import,这样我们便可以将很多通用的message定义在同一个.proto文件中,而其他消息定义文件可以通过 import的方式将该文件中定义的消息包含进来,如:
import “myproject/CommonMessages.proto”

     五、限定符(required/optional/repeated)的基本规则。
1. 在每个消息中必须至少留有一个required类型的字段。
2. 每个消息中可以包含0个或多个optional类型的字段。
3. repeated表示的字段可以包含0个或多个数据。需要说明的是,这一点有别于C++/Java中的数组,因为后两者中的数组必须包含至少一个元素。
4. 如果打算在原有消息协议中添加新的字段,同时还要保证老版本的程序能够正常读取或写入,那么对于新添加的字段必须是optional或repeated。道理非常简单,老版本程序无法读取或写入新增的required限定符的字段。

      六、类型对照表。

.proto Type Notes C++ Type Java Type
double  double  double
float  float  float
int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.  int32  int
int64 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.  int64  long
uint32 Uses variable-length encoding.  uint32  int
uint64 Uses variable-length encoding.  uint64  long
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.  int32  int
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.  int64  long
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 228.  uint32  int
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 256.  uint64  long
sfixed32 Always four bytes.  int32  int
sfixed64 Always eight bytes.  int64  long
bool  bool  boolean
string A string must always contain UTF-8 encoded or 7-bit ASCII text.  string  String
bytes May contain any arbitrary sequence of bytes. string ByteString

      七、Protocol Buffer消息升级原则。
在实际的开发中会存在这样一种应用场景,既消息格式因为某些需求的变化而不得不进行必要的升级,但是有些使用原有消息格式的应用程序暂时又不能被立刻升 级,这便要求我们在升级消息格式时要遵守一定的规则,从而可以保证基于新老消息格式的新老程序同时运行。规则如下:
1. 不要修改已经存在字段的标签号。
2. 任何新添加的字段必须是optional和repeated限定符,否则无法保证新老程序在互相传递消息时的消息兼容性。
3. 在原有的消息中,不能移除已经存在的required字段,optional和repeated类型的字段可以被移除,但是他们之前使用的标签号必须被保留,不能被新的字段重用。
4. int32、uint32、int64、uint64和bool等类型之间是兼容的,sint32和sint64是兼容的,string和bytes是兼 容的,fixed32和sfixed32,以及fixed64和sfixed64之间是兼容的,这意味着如果想修改原有字段的类型时,为了保证兼容性,只 能将其修改为与其原有类型兼容的类型,否则就将打破新老消息格式的兼容性。
5. optional和repeated限定符也是相互兼容的。

      八、Packages。
我们可以在.proto文件中定义包名,如:
package ourproject.lyphone;
该包名在生成对应的C++文件时,将被替换为名字空间名称,既namespace ourproject { namespace lyphone。而在生成的Java代码文件中将成为包名。

      九、Options。
Protocol Buffer允许我们在.proto文件中定义一些常用的选项,这样可以指示Protocol Buffer编译器帮助我们生成更为匹配的目标语言代码。Protocol Buffer内置的选项被分为以下三个级别:
1. 文件级别,这样的选项将影响当前文件中定义的所有消息和枚举。
2. 消息级别,这样的选项仅影响某个消息及其包含的所有字段。
3. 字段级别,这样的选项仅仅响应与其相关的字段。
下面将给出一些常用的Protocol Buffer选项。
1. option java_package = “com.companyname.projectname”;
java_package是 文件级别的选项,通过指定该选项可以让生成Java代码的包名为该选项值,如上例中的Java代码包名为 com.companyname.projectname。与此同时,生成的Java文件也将会自动存放到指定输出目录下的 com/companyname/projectname子目录中。如果没有指定该选项,Java的包名则为package关键字指定的名称。该选项对于 生成C++代码毫无影响。
2. option java_outer_classname = “LYPhoneMessage”;
java_outer_classname是 文件级别的选项,主要功能是显示的指定生成Java代码的外部类名称。如果没有指定该选项,Java代码的外部类名称为当前文件的文件名部分,同时还要将 文件名转换为驼峰格式,如:my_project.proto,那么该文件的默认外部类名称将为MyProject。该选项对于生成C++代码毫无影响。
注:主要是因为Java中要求同一个.java文件中只能包含一个Java外部类或外部接口,而C++则不存在此限制。因此在.proto文件中定义的消 息均为指定外部类的内部类,这样才能将这些消息生成到同一个Java文件中。在实际的使用中,为了避免总是输入该外部类限定符,可以将该外部类静态引入到 当前Java文件中,如:import static com.company.project.LYPhoneMessage.*
3. option optimize_for = LITE_RUNTIME;
optimize_for是文件级别的选项,Protocol Buffer定义三种优化级别SPEED/CODE_SIZE/LITE_RUNTIME。缺省情况下是SPEED。
SPEED: 表示生成的代码运行效率高,但是由此生成的代码编译后会占用更多的空间。
CODE_SIZE: 和SPEED恰恰相反,代码运行效率较低,但是由此生成的代码编译后会占用更少的空间,通常用于资源有限的平台,如Mobile。
LITE_RUNTIME: 生成的代码执行效率高,同时生成代码编译后的所占用的空间也是非常少。这是以牺牲Protocol Buffer提供的反射功能为代价的。因此我们在C++中链接Protocol Buffer库时仅需链接libprotobuf-lite,而非libprotobuf。在Java中仅需包含protobuf-java- 2.4.1-lite.jar,而非protobuf-java-2.4.1.jar。
注:对于LITE_MESSAGE选项而言,其生成的代码均将继承自MessageLite,而非Message。
4. [pack = true]: 因为历史原因,对于数值型的repeated字段,如int32、int64等,在编码时并没有得到很好的优化,然而在新近版本的Protocol Buffer中,可通过添加[pack=true]的字段选项,以通知Protocol Buffer在为该类型的消息对象编码时更加高效。如:
repeated int32 samples = 4 [packed=true]。
注:该选项仅适用于2.3.0以上的Protocol Buffer。
5. [default = default_value]: optional类型的字段,如果在序列化时没有被设置,或者是老版本的消息中根本不存在该字段,那么在反序列化该类型的消息是,optional的字段 将被赋予类型相关的缺省值,如bool被设置为false,int32被设置为0。Protocol Buffer也支持自定义的缺省值,如:
optional int32 result_per_page = 3 [default = 10]。

     十、命令行编译工具。
protoc –proto_path=IMPORT_PATH –cpp_out=DST_DIR –java_out=DST_DIR –python_out=DST_DIR path/to/file.proto
这里将给出上述命令的参数解释。
1. protoc为Protocol Buffer提供的命令行编译工具。
2. –proto_path等同于-I选项,主要用于指定待编译的.proto消息定义文件所在的目录,该选项可以被同时指定多个。
3. –cpp_out选项表示生成C++代码,–java_out表示生成Java代码,–python_out则表示生成Python代码,其后的目录为生成后的代码所存放的目录。
4. path/to/file.proto表示待编译的消息定义文件。
注:对于C++而言,通过Protocol Buffer编译工具,可以将每个.proto文件生成出一对.h和.cc的C++代码文件。生成后的文件可以直接加载到应用程序所在的工程项目中。 如:MyMessage.proto生成的文件为MyMessage.pb.h和MyMessage.pb.cc。

Tomcat报错:Document base ……does not exist or is not a readable directory - 青葱岁月 - ITeye技术网站

mikel阅读(1998)

来源: Tomcat报错:Document base ……does not exist or is not a readable directory – 青葱岁月 – ITeye技术网站

2013年3月12:

用Eclipse A打开Eclipse B的workspace,然后在tomcat下启动某一个web工程时报错:

Shell代码  收藏代码
  1. Document base E:\Workspaces\JavaEE\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\cy-manage does not exist or is not a readable directory

其中E:\Workspaces\JavaEE是我的Eclipse工作空间目录。对于这个问题一直纳闷不得其解,在网上找到了一篇相关的文章:http://blog.csdn.net/asdf544265772/article/details/7928121  文中给出原因是 tmp0\conf\server.xml 中<Context>节点配置了很多已经移除的项目;需要执行下面两步操作:

1.删除无用的 tmp0\conf\server.xml 中的  <Context>  节点;

2.删除 tmp0\work\Catalina\localhost 下的所有文件夹;

 

但是我按照他的方法去对应的目录下查找, tmp0\conf\server.xml里<Context>节点中并没有出现其余的项目。 tmp0\work\Catalina\localhost 下也没有多余文件夹。不得已,我尝试了最后的一种方法,就是把Eclipse中把Tomcat删除然后再重新配置。再次部署工程启动后发现竟然不再报错了。

 

记录一下,便于再次遇到这种问题时可以参考。

 

2013年11月20

今天又碰到了这个问题,是另外一个工程,该工程是我同事用IntelliJ IDEA建立的工程,我导入到我自己的Eclipse,配置了Project Facets之后,部署到tomcat下启动报错。

排查过程:

1.查看workspace工程目录下.settings文件夹下是否所有文件都存在,文件名称为:

Txt代码  收藏代码
  1. .jsdtscope
  2. org.eclipse.core.resources.prefs
  3. org.eclipse.jdt.core.prefs
  4. org.eclipse.m2e.core.prefs
  5. org.eclipse.wst.common.component
  6. org.eclipse.wst.common.project.facet.core.xml
  7. org.eclipse.wst.jsdt.ui.superType.container
  8. org.eclipse.wst.jsdt.ui.superType.name

如果有文件不存在,则可以从其它正常的maven web工程里拷贝一份过来。

2.检查org.eclipse.wst.common.project.facet.core.xml里的内容

Xml代码  收藏代码
  1. <?xml version=“1.0” encoding=“UTF-8”?>
  2. <faceted-project>
  3.   <fixed facet=“wst.jsdt.web”/>
  4.   <installed facet=“java” version=“1.6”/>
  5.   <installed facet=“jst.web” version=“2.5”/>
  6.   <installed facet=“wst.jsdt.web” version=“1.0”/>
  7. </faceted-project>

上面就是我们配置的Project Facet信息,版本号是跟你选择的Dynamic Web Module和Java版本相关。

 

部署后还是报错,定位到了org.eclipse.wst.common.component这个文件

我看了一下我的工程该文件的内容:

Xml代码  收藏代码
  1. <?xml version=“1.0” encoding=“UTF-8”?>
  2. <project-modules id=“moduleCoreId” project-version=“1.5.0”>
  3.     <wb-module deploy-name=“test-web”>
  4.         <wb-resource deploy-path=“/” source-path=“/WebContent” tag=“defaultRootSource”/>
  5.         <property name=“context-root” value=“hanting”/>
  6.         <property name=“java-output-path” value=“/hanting/target/classes”/>
  7.     </wb-module>
  8. </project-modules>

原来问题出在wb-module配置上面,source-path是默认的WebContent,但是一般的maven web工程的web资源目录是src/main/webapp,所以启动会报错,正确的配置如下:

Xml代码  收藏代码
  1. <?xml version=“1.0” encoding=“UTF-8”?>
  2. <project-modules id=“moduleCoreId” project-version=“1.5.0”>
  3.     <wb-module deploy-name=“test-web”>
  4.         <wb-resource deploy-path=“/” source-path=“/src/main/webapp” tag=“defaultRootSource”/>
  5.         <wb-resource deploy-path=“/WEB-INF/classes” source-path=“/src/main/java”/>
  6.         <wb-resource deploy-path=“/WEB-INF/classes” source-path=“/src/main/resources”/>
  7.         <property name=“java-output-path” value=“/ht-manage/target/classes”/>
  8.         <property name=“context-root” value=“test-web”/>
  9.     </wb-module>
  10. </project-modules>

修改后启动工程就正常了。

附:如果启动后报java.lang.ClassNotFoundException: ContextLoaderListener错误的话可以查看我的另外一篇博客:http://chenzhou123520.iteye.com/blog/1836987

Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds... - 阳光总在风雨后001 - 博客园

mikel阅读(961)

来源: Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds… – 阳光总在风雨后001 – 博客园

当启动tomcat时候出现 Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor. 的错误,那表示你的tomcat启动超时了,有时候你重新启动下就好了,但有时需要重新启动很多次,如果你不想这样的话,你只需修改下tomcat的启动 时间就行了,步骤如下:

修改 workspace\.metadata\.plugins\org.eclipse.wst.server.core\servers.xml文件。

<servers>

<server hostname=”localhost” id=”JBoss v5.0 at localhost” name=”JBoss v5.0 at localhost” runtime-id=”JBoss v5.0″ server-type=”org.eclipse.jst.server.generic.jboss5″ server-type-id=”org.eclipse.jst.server.generic.jboss5″ start-timeout=”1000″ stop- timeout=”15″ timestamp=”0″>

<map jndiPort=”1099″ key=”generic_server_instance_properties” port=”8090″ serverAddress=”127.0.0.1″ serverConfig=”default”/>

</server>

</servers>

把 start-timeout=”45″ 改为 start-timeout=”1000″ 或者更长 重启eclipse就可以了。

 

或者在界面修改

分类: tomcat

Unknown character set: 'utf8mb4' - xmqywx的专栏 - 博客频道 - CSDN.NET

mikel阅读(1511)

来源: Unknown character set: ‘utf8mb4’ – xmqywx的专栏 – 博客频道 – CSDN.NET

从昨天晚上开始,困扰了我几个小时的问题,无论用c3p0还是用Spring的DriverManagerDataSource都无法连接我服务器上的远程数据库,一直报的错误就是:

 

org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.MySQL.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown character set: ‘utf8mb4’

at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.Java:80)

at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:382)

at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:456)

at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:464)

at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:472)

at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:477)

at org.springframework.jdbc.core.JdbcTemplate.queryForInt(JdbcTemplate.java:486)

……………………..

我的配置里写得很明白:jdbc:mySQL://xx.xx.xx.xx:3306/flash_fee?useUnicode=true&characterEncoding=utf8

就是使用utf8字符集进行连接,不知道为什么在连接的时候非要在后面加上mb4,也不知道这东西是啥。

 

这个问题今天上午终于解决,是由于MySQL驱动版本引起的:

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.18</version>

</dependency>

改为:

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.6</version>

</dependency>

不清楚这个Mysql的驱动为什么版本太新了还不可以。去查看了一下服务器的Mysql版本:

Server version: 6.0.11-alpha-log Source distribution

不知道我们的服务器维护人员怎么想的,竟然装了这么奇葩的一个Mysql版本,还是个alpha的。。。真是蛋疼了!

看来度娘还是不行啊,要找技术东西还是得谷谷哥啊!

参考文章:http://kenai.com/jira/browse/ACTIVERECORD_JDBC-160

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException问题解决 - - ITeye技术网站

mikel阅读(1378)

来源: Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException问题解决 – – ITeye技术网站

今天在使用mySQL的时候报了个错
Caused by: com.mySQL.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown character set: ‘utf8mb4’…….

自己的配置文件中以前是
db.url = jdbc:mysql://localhost:3306/test
修改为
db.url = jdbc:mysql://localhost:3306/test?characterEncoding=utf8

Java连接Mysql数据库警告:Establishing SSL connection without server's identity verification is not recommend - 渴望,就奋力追寻... - 博客频道 - CSDN.NET

mikel阅读(2654)

来源: Java连接Mysql数据库警告:Establishing SSL connection without server’s identity verification is not recommend – 渴望,就奋力追寻… – 博客频道 – CSDN.NET

 

Java使用MySQL-jdbc连接MySQL出现如下警告:

Establishing SSL connection without server’s identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn’t set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to ‘false’. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.

原因是MySQL在高版本需要指明是否进行SSL连接。解决方案如下:

在mysql连接字符串url中加入ssl=true或者false即可,如下所示。

url=jdbc:mysql://127.0.0.1:3306/framework?characterEncoding=utf8&useSSL=true