protocol buffers

时间:2024-06-01 11:55:08编辑:小早

Protobuf性能到底有没有比JSON快5倍

直接利用python提供的json包,在django model的定义中增加一个方法toJSON,利用django model 能访问 _meta.fields 得到相关属性而得到,例子如下:
class Category(models.Model):
autoid = models.AutoField(primary_key=True)
email=models.CharField(max_length=150,blank=False)
comtype=models.CharField(max_length=20,blank=False)
catname=models.CharField(max_length=150,blank=False)

def __unicode__(self):
return '%s' % (self.catname)

def toJSON(self):
import json
return json.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]]))
然后用django查出数据,并转换成json,代码如下:
row=models.Category.objects.get(autoid=23)


了解一下ProtoBuf

我们在进行网络通信调用的时候,总是需要将内存的数据块经过序列化,转换成为一种可以通过网络流进行传输的格式。而这种格式在经过了传输之后再经过序列化,能还原成我们预想中的数据结构。 那么我们对于这种用于中间网络传输的数据格式就有一定的要求。首先它可以准确地描述数据内容,在此基础上我们则希望它尽量的小。 最开始流行起来的是XML,可扩展标记语言。由于它可以用来标记数据、定义数据类型,所以用户可以自己定义数据自己的语言,从而让对不同的数据结构化成统一的格式称为了可能。 而另外一个我们熟知的则是JSON(JavaScript Object Notation, JS 对象简谱)。尽管JSON中缺少了XML中的标签属性等描述方式,但是足够简介和清晰的层次结构使得其成为了必XML更受欢迎的数据交换格式。 同一份数据显然JSON的数据量比XML所使用的空间更少。那么空间省略在哪里呢?一方面是json使用更简单的字符来定义数据间的关联关系;另一方面是JSON减少了对数据类型的描述。但是丢少的数据类型再哪里呢? 以Java中的 OpenFeign 举例,JSON中缺少的类型定义被定义道程序中的接口中了。当进行序列化与反序列化时,JSON格式并不记录数据的类型,具体的数据类型在序列化方与反序列化方通过事先约定的接口来进行定义。这样就减少了信息传输过程中的信息量,从而让数据得以压缩。 但是JSON由于没有定义数据类型,所以在传输的过程中实际上就都是文本流,那么这种方法还可以进一步压缩吗? 结合上文的讨论,我们先说结论:方法是有的,并写当前的实现方式是ProtoBuf。但在此之前我们先来了解一下ProtoBuf。 我们可以先看看官方给出的定义与描述: 同样的,ProtoBuf也是一种支持序列化反序列化的方法,并且他具有很多优点: 实际上,ProtoBuf提供了一种通用的数据描述方式,这种定义数据的方式是通用的,就如同JSON或者XML一样。 接下来我们来来回答本节一开始的问题,针对JSON来说,ProtoBuf是如何将体积变得更小的呢?答案很简单,就是为数据序列化反序列化提供更多的先验知识。 本文暂不过度深入ProtoBuf原理,但是可以通过一张图来进行简要说明(): ProtoBuf中的数据是按顺序进行排列,而整体的结构为若干个field,每一个field中由 Tag-[Length]-Value 组成。Length是可选的,而是否存在Length是通过Tag的类型来决定的。也就是说如果是指定的类型,比如int64,那我们就可以知道Value的长度,也就不用在依靠Length来对其空间进行描述(redis中的压缩列表也是这个思想)。 那么field应该对应的是什么字段呢?这个则是在序列化与反序列化时在ProtoBuf的服务端与客户端之间进行预先定义的。而因为提前定义了field的类型、排序,所以field本身可以不用对字段名、字段位置进行描述,只需要根据字段类型选用合适的二进制序列化方法,将字段本身的value值进行序列化传输即可。 稍微总结一下: ProtoBuf通过对传输字段的名称、顺序进行预定义,从而在传输结构中只需要顺序的记录每个字段的类型标签和二进制值。 尽管上文和官方中都是以XML或者JSON来对ProtoBuf进行对比。但是因为ProtoBuf本身就是二进制序列化方式,所以从压缩比上比较感觉有点欺负人。 对应的在Java中二进制常用的序列化器有Kryo和Hessian。但事实上,由于Kryo和Hessian中都需要对Java类名和字段信息进行存储。而ProtoBuf则只有Tag-Length-Value的数据对,且Value更是有针对性的特殊编码,所以空间占用小的很多。 Kryo是专门针对Java进行优化了的。所以在使用的便捷性上来说Kryo则更加方便。但ProtoBuf是跨平台的,且由于进行了字段的顺序定义,所以似的ProtoBuf定义后的接口是可以向前兼容的(只向后追加字段),而这种优势是Kryo所没有的。 ProtoBuf是跨语言的,使用ProtoBuf的第一步是先定一个 proto 文件 ,而由于ProtoBuf 2和3语言版本的不同,其定义格式会有所不同,具体的细节还是得参考官方文档:https://developers.google.cn/protocol-buffers/docs/proto3 对于ProtoBuf 3 的定义文档我们可以按如下方法定义: 其中message关键字是定义的文件名,而 string、int32则是预定的字段类型,repeated则是描述字段为可重复任意多次的字段。 ProtoBuf通过这种形式的文件定义了传输信息的文件结构。 但是之前小节中我们知道了ProtoBuf是通过 Tag-[Length]-Value 组成的数据组来进行信息传输的,那么proto文件中定义的内容如何转换为实际传输的对象呢? ProtoBuf的做法是,为每一种语言提供一个生成器protoc。通过使用protoc则可以根据.proto文件生成为一组java文件。对应的官方语法演示样例为: 官方的生成参考为:https://developers.google.com/protocol-buffers/docs/reference/java-generated 生成后的java文件将提供对应的实体以及数据的构造方法等文件,从而支持后续的使用。 需要注意的是,ProtoBuf是本质上是序列化方法,具体是通过Spring Cloud 的OpenFeign进行接口调用,还是通过grpc进行接口调用,都是可以的。 本文对ProtoBuff进行了概念的整理,并没有对每个细节都进行深入的梳理,可以当作概念科普来进行阅读。

怎样把json转为protocol buffer

例子:

package org.protob;

import org.protob.W.helloworld;

import com.google.protobuf.InvalidProtocolBufferException;
import com.googlecode.protobuf.format.JsonFormat;
import com.googlecode.protobuf.format.JsonFormat.ParseException;
/**
* 下载 protoc-2.5.0-win32.zip
* cmd: protoc.exe --java_out=./ w.proto
* @author liangrui
*
*/
public class Main {

public static void main(String[] args) throws Exception {
main2(null);
main3(null);
main4(null);
}

//序列化 /返序列化
public static void main2(String[] args) throws InvalidProtocolBufferException {
//序列化
helloworld.Builder builder=helloworld.newBuilder();
builder.setId(10);
builder.setStr("fx");
builder.setOpt(20);
helloworld info=builder.build();
byte[] result=info.toByteArray() ;
//返序列化
helloworld msg = helloworld.parseFrom(result);
System.out.println(msg);

}

//protobuf转json
public static void main3(String[] args) throws InvalidProtocolBufferException {

//序列化
helloworld.Builder builder=helloworld.newBuilder();
builder.setId(10);
builder.setStr("fx");
builder.setOpt(20);
helloworld info=builder.build();
byte[] result=info.toByteArray() ;


//返序列化
helloworld hello = helloworld.parseFrom(result);
System.out.println(hello);

String jsonFormat =JsonFormat.printToString(hello);
System.out.println(jsonFormat);

}

//josn转protobuf
public static void main4(String[] args) throws ParseException {
helloworld.Builder builder =helloworld.newBuilder();
String jsonFormat = "{id:11,str:'xxx',opt:50}";
JsonFormat.merge(jsonFormat, builder);

System.out.println(builder.build());

}

}
/**
output:
id: 10
str: "fx"
opt: 20

id: 10
str: "fx"
opt: 20

{"id": 10,"str": "fx","opt": 20}
id: 11
str: "xxx"
opt: 50
*/



proto文件 w.proto


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



生成后的java类

// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: w.proto

package org.protob;

public final class W {
private W() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
}
public interface helloworldOrBuilder
extends com.google.protobuf.MessageOrBuilder {

// required int32 id = 1;
/**
* required int32 id = 1;
*
*
* ID
*
*/
boolean hasId();
/**
* required int32 id = 1;
*
*
* ID
*
*/
int getId();

// required string str = 2;
/**
* required string str = 2;
*
*
* str
*
*/
boolean hasStr();
/**
* required string str = 2;
*
*
* str
*
*/
java.lang.String getStr();
/**
* required string str = 2;
*
*
* str
*
*/
com.google.protobuf.ByteString
getStrBytes();

// optional int32 opt = 3;
/**
* optional int32 opt = 3;
*
*
*optional field
*
*/
boolean hasOpt();
/**
* optional int32 opt = 3;
*
*
*optional field
*
*/
int getOpt();
}
/**
* Protobuf type {@code org.protob.helloworld}
*/
public static final class helloworld extends
com.google.protobuf.GeneratedMessage
implements helloworldOrBuilder {
// Use helloworld.newBuilder() to construct.
private helloworld(com.google.protobuf.GeneratedMessage.Builder builder) {
super(builder);
this.unknownFields = builder.getUnknownFields();
}
private helloworld(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }

private static final helloworld defaultInstance;
public static helloworld getDefaultInstance() {
return defaultInstance;
}

public helloworld getDefaultInstanceForType() {
return defaultInstance;
}

private final com.google.protobuf.UnknownFieldSet unknownFields;
@java.lang.Override
public final com.google.protobuf.UnknownFieldSet
getUnknownFields() {
return this.unknownFields;
}
private helloworld(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
initFields();
int mutable_bitField0_ = 0;
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
com.google.protobuf.UnknownFieldSet.newBuilder();
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
default: {
if (!parseUnknownField(input, unknownFields,
extensionRegistry, tag)) {
done = true;
}
break;
}
case 8: {
bitField0_ |= 0x00000001;
id_ = input.readInt32();
break;
}
case 18: {
bitField0_ |= 0x00000002;
str_ = input.readBytes();
break;
}
case 24: {
bitField0_ |= 0x00000004;
opt_ = input.readInt32();
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(
e.getMessage()).setUnfinishedMessage(this);
} finally {
this.unknownFields = unknownFields.build();
makeExtensionsImmutable();
}
}
public static final com.google.protobuf.Descriptors.Descriptor
getDescriptor() {
return org.protob.W.internal_static_org_protob_helloworld_descriptor;
}

protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
internalGetFieldAccessorTable() {
return org.protob.W.internal_static_org_protob_helloworld_fieldAccessorTable
.ensureFieldAccessorsInitialized(
org.protob.W.helloworld.class, org.protob.W.helloworld.Builder.class);
}

public static com.google.protobuf.Parser PARSER =
new com.google.protobuf.AbstractParser() {
public helloworld parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return new helloworld(input, extensionRegistry);
}
};

@java.lang.Override
public com.google.protobuf.Parser getParserForType() {
return PARSER;
}


如何在PHP中处理Protocol Buffers数据

第一步,安装Google的protoc编译器,这个工具可以把proto文件中定义的Message转换为各种编程语言中的类。下载release版本直接编译安装。
第二步,安装protoc的PHP plugin,需要使用composer安装,这个插件可以将proto文件转换到PHP文件,在PHP应用中引用后,可以将二进制格式的Protocol Buffers数据转换为PHP的对象。
第三步,安装php-protocolbuffers,这是一个PHP扩展,在第二步中已经完成了proto文件到PHP文件的转换,但是对PHP对象的各种操作还需要这个扩展中的API方法。
第四步,借助上面提到的扩展的各种API方法,可以简单地get出自己需要的字段完成后续工作
require "....../kingso.proto.php"
$ks_result = KSResult::parseFromString($kingso_res);
$ks_result->get('xxx_name');


如何在PHP中处理Protocol Buffers数据

第一步安装Googleprotoc编译器工具proto文件定义Message转换各种编程语言类载release版本直接编译安装
第二步安装protocPHP plugin需要使用composer安装插件proto文件转换PHP文件PHP应用引用二进制格式Protocol Buffers数据转换PHP象
第三步安装php-protocolbuffersPHP扩展第二步已经完proto文件PHP文件转换PHP象各种操作需要扩展API
第四步借助面提扩展各种API简单get自需要字段完续工作
require "....../kingso.proto.php"
$ks_result = KSResult::parseFromString($kingso_res);
$ks_result->get('xxx_name');


如何将.proto通过命令转换成.java文件

1.http://code.google.com/p/protobuf/downloads/list 下载 protoc-2.4.1-win32.zip 至本地并解压2.通过命令行将.proto的文件生成为.java的文件官网上是这样写的protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto下面用图形来说明如果操作。下载的部分就不说了命令:文件夹结构:

如何将.proto通过命令转换成.java文件

Protocol
Buffers的是一个高效且可扩展的格式结构化数据编码的一种方式。

谷歌使用了Protocol
Buffers的几乎所有它的内部RPC协议和文件格式


使用protobuf传输的好处有:二进制传输,安全、数据量小

.proto文件中我们服务器定义需要传输的数据,及数据类型等。而传输的时候则是使用了通过官方工具生成的具体为.java的消息体

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

https://developers.google.com/protocol-buffers/docs/overview

可参考官方网上的具体使用


下面介绍如何用将.proto的文件转换成.java的文件

1.http://code.google.com/p/protobuf/downloads/list
下载 protoc-2.4.1-win32.zip 至本地并解压

2.通过命令行将.proto的文件生成为.java的文件官网上是这样写的

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

下面用图形来说明如果操作。下载的部分就不说了


究竟如何使用 Google Protocol Buffer 2.5.0

  在网上查了一下,虽然有很多文章介绍Protocol Buffer,但是实际使用起来,还是会遇到很多问题,所以我想应该有一个指南一样的东西,让新手很快就能使用它。  Protocol Buffer简写为Protobuf,是Google开发的一种储存数据的方式,功能与XML一样,但更方便,数据量更小,速度更快,在序列化和反序列化的时候使用,有很大的优势。比如,网络游戏的通讯协议编写。更重要的是,它是一个开源项目。      由于Protobuf不能生成C#代码,所以就要用到另外一个开源项目protobuf-net,    用法是,在命令行用protoc.exe依据你自己定义的.proto文件,来生成.cpp文件  用protobuf-net的protogen.exe来生成.cs文件,可以写一个批处理,如下:  protoc -I=. --cpp_out=. Net.proto //在当前目录下,以cpp方式编译Net.proto  protogen -i:Net.proto -o:Net.cs //在当前目录下,用Net.proto生成Net.cs    其实两句功能是一样,只是分别生成了Net.proto的cpp文件和cs文件,这样就可以在两种语言编写的应用程序间通信了。


上一篇:哆啦大冒险

下一篇:炊事班的故事第3部