1. Protobuf语法介绍
我们先看看官方文档给出的定义和描述
简单来讲, ProtoBuf 是结构数据序列化方法,可简单类比于,其具有以下特点:
在protobuf中,协议是由一系列的消息组成的。因此最重要的就是定义通信时使用到的消息格式。一个Protobuf 消息(对应java类),由至少一个字段(对应Java类属性)组合而成。
消息的定义很简单,就是message关键字加上消息的名字
字段定义格式:
限定修饰符 | 数据类型 | 字段名称 | = | 字段编码
required:
表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。
optional:
表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。---因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。
repeated:
表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。类比于Java这边的List
protobuf定义了一套基本的数据类型,几乎都映射了Java语言的基础数据类型(主要以Java为例)
详见下面表格
字段的命名方式与Java的命名方式大致一致
字段编码是一个序列化和反序列化的标记值,有了该值,通信双方才能互相识别对方的字段。当然相同的编码值,其限定修饰符和数据类型必须相同。编码值的取值范围为 1~2^32(4294967296)
其中 1~15的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低(相对于1-15),当然一般情况下相邻的2个值编码效率的是相同的,除非2个值恰好实在4字节,12字节,20字节等的临界区。比如15和16
1900~2000编码值为Google protobuf 系统内部保留值,建议不要在自己的项目中使用。protobuf 还建议把经常要传递的值把其字段编码设置为1-15之间的值
完整的消息定义示例
枚举的定义和Java 相同,使用 enum 关键字,但是有一些限制。
枚举值必须大于等于0的整数。
使用分号 ; 分隔枚举变量而不是Java 语言中的逗号 ,
示例:
你可以将其他消息类型用作字段类型。已我们现在IM的为例,在每一个CSSendLiveRoomMsgReq消息中包含ImMsgBody消息,此时可以在相同的.proto文件中定义一个ImMsgBody消息类型,然后在CSSendLiveRoomMsgReq消息中指定一个ImMsgBody类型的字段
示例:
如果你的消息中有很多可选字段, 并且同时至多一个字段会被设置, 你可以加强这个行为,使用oneof特性节省内存,Oneof字段就像可选字段, 除了它们会共享内存, 至多一个字段会被设置。 设置其中一个字段会清除其它字段。
为了在.proto定义Oneof字段, 你需要在名字前面加上oneof关键字, 比如下面例子的ImMsgBody:
oneof的特性
2. java 怎么使用protobuf库
1.到http://code.google.com/p/protobuf/downloads/list ,选择其中的win版本下载,我选择的是protoc-2.4.1-win32.zip
2.下载一个protobuf-java-2.4.1.jar文件(注意,要与你刚才下的proto.exe版本相同)
然后就开始开发了。
步骤:
1.用记事本编写一个.proto文件:
}如:编写的是test.proto
package protobuf;
option java_package = "com.sq.protobuf";
option java_outer_classname = "FirstProtobuf";
message testBuf {
required int32 ID = 1;
required string Url = 2;
}
将其放在与刚解压的protoc.exe同级目录中。
2.在cmd中,到protoc-2.4.1-win32文件夹下,
执行
E:\protoc-2.4.1-win32> protoc.exe --java_out=./ test.proto
则可以找到的一个生成的FirstProtobuf.java文件。
3.在MyEclipse中新建一个java project,建立包com.sq.protobuf,然后将刚才生成的FirstProtobuf.java文件放在其下面。
此时会报错,因为没有引入jar包,在package视图下,将protobuf-java-2.4.1.jar引入,即可解决问题。
4.建立测试文件:
package com.sq.protobuf.test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import com.google.protobuf.;
import com.sq.protobuf.FirstProtobuf;
public class Test {
public static void main(String[] args) {
//序列化过程
//FirstProtobuf是生成类的名字,即proto文件中的java_outer_classname
//testBuf是里面某个序列的名字,即proto文件中的message testBuf
FirstProtobuf.testBuf.Builder builder=FirstProtobuf.testBuf.newBuilder();
builder.setID(777);
builder.setUrl("shiqi");
//testBuf
FirstProtobuf.testBuf info=builder.build();
byte[] result = info.toByteArray() ;
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@10.64.59.12:1521/orcl";
String user = "parkingsystem";
String password = "parkingsystem";
try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
if(!conn.isClosed()){
System.out.println("Succeeded connecting to the Database!");
//此处只能使用prepareStatement
PreparedStatement ps = conn.prepareStatement("insert into test(id,test) values (1,?)");
//写入数据库,要把它改写为流的形式
ByteArrayInputStream stream = new ByteArrayInputStream(result);
ps.setBinaryStream(1,stream,stream.available());
Statement statement = conn.createStatement();
Blob blob = null;
ps.execute();
////////////////上述完成将写入数据库的操作,数据库中对应的字段的属性要设置为Blob
String sql = "select test from test";
ResultSet rs = statement.executeQuery(sql);
if(rs.next()){
blob = rs.getBlob("test");
}
byte[] s = blob.getBytes(1,(int)blob.length());
FirstProtobuf.testBuf testBuf = FirstProtobuf.testBuf.parseFrom(s);
System.out.println(testBuf);
conn.close();
}
}catch(Exception e) {
e.printStackTrace();
}
//反序列化过程
try {
FirstProtobuf.testBuf testBuf = FirstProtobuf.testBuf.parseFrom(result);
System.out.println(testBuf);
} catch ( e) {
e.printStackTrace();
}
}
}
发现可以将其序列化,插入到数据库,并可以从数据库出取出后,反序列化,内容可以正常显示出来。
注意的就是2点:
1.不能用statement,否则无法插入blob类型的数据
2.为参数赋值时,要用
ByteArrayInputStream stream = new ByteArrayInputStream(result);
ps.setBinaryStream(1,stream,stream.available());
3. 作为java后端,在用protobuf的情况下我该给前端传什么形式的数据
protobuf是你们落地时存储的数据格式,跟给前端的格式是没有关系的。
可以通过工具类直接转json后传给前端。
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod("parseFrom", byte[].class);
JsonFormat().printToString((GeneratedMessage) method.invoke(clazz, bytes));
其中className是你根据protobuf文件生成的java类,bytes是读取的protobuf对象的字节数组。
4. 大数据技术是学什么的
大数据技术是学这些:
编程语言
想要学习大数据技术,首先要掌握一门基础编程语言。Java编程语言的使用率最广泛,因此就业机会会更多一些,而Python编程语言正在高速推广应用中,同时学习Python的就业方向会更多一些。
Linux
学习大数据一定要掌握一定的Linux技术知识,不要求技术水平达到就业的层次,但是一定要掌握Linux系统的基本操作。能够处理在实际工作中遇到的相关问题。
SQL
大数据的特点就是数据量非常大,因此大数据的核心之一就是数据仓储相关工作。因此大数据工作对于数据库要求是非常的高。甚至很多公司单独设置数据库开发工程师。
Hadoop
Spark是专门为大规模数据处理而设计的快速通用的计算引擎。可以用它来完成各种各样的运算,包括SQL查询、文本处理、机器学习等等。
机器学习
机器学习是目前人工智能领域的核心技术,在大数据专业中也有非常广泛的引用。在算法和自动化的发展过程中,机器学习扮演着非常重要的角色。可以大大拓展自己的就业方向。
互联网行业里大数据和云智能是当下最重要板块,企业借助大数据技术不仅能避免企业发展时会面临的各种风险,更能解决发展过程中所遇到的种种难题。
对于想要学习大数据的更多信息,可以选择到CDA 认证中心,是一套科学化,专业化,国际化的人才考核标准,共分为 CDA 、LEVELⅠ ,LEVEL Ⅱ,LEVEL Ⅲ三个等级,涉及行业包括互联网、金融、咨询、电信、零售、医疗、旅游等,涉及岗位包括大数据、数据分析、市场、产品、运营、咨询、投资、研发等。CDACDA(Certified Data Analyst),即“CDA 数据分析师”,
5. 如何提供java编译protobuf协议文件的速度
ProtoBuf java 包编译
ProtoBuf的官方下载包并不包含jar文件,需要用户自己configure/make….来自行编译。由于Windows上没有编译环境,就用了一个笨一点方法处理了。
分别下载:
protobuf-2.4.1.zip ProtoBuf的源文件(包含了C++/Java/Python)的源文件
protoc-2.4.1-win32.zip 已经编译过的用于Windows平台的protoc命令(该命令用于将.proto文件转化为Java或C++源文件)。
分别解析这两个文件,你可以在protoc-2.4.1-win32.zip解压后的文件中找到一个protoc.exe文件,将其到protobuf-2.4.1/src目录下,然后进入protobuf-2.4.1/java,执行:
mvn install
如果没有安装maven的话,可以在网上找一下maven的安装手册。
编译完成后可以在protobuf-2.4.1/java/target目录中找到protobuf-2.4.1.jar文件.
后记
protobuf-2.4.1.zip里面同时包含了protoc和java等的源文件,如果按照官方教程来安装的话,就是先产生出protoc编译器,然后再生成jar包。由于mvn install同时包含了编译和测试过程,而测试代码又依赖于protoc编译器,所以就需要单独下载protoc编译器,并置于src目录下了。
如果不需要验证编译结果(不执行单元测试),则可以如下:
下载protobuf-2.4.1.zip并解压,进入到protobuf-2.4.1/java
mvn install –Dmaven.test.skip=true
这样就可以了。
6. 了解一下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进行了概念的整理,并没有对每个细节都进行深入的梳理,可以当作概念科普来进行阅读。
7. 如何查看php是否支持protobuf
首先要知道,protobuf是google提供的一个开源序列化框架,类似于XML,JSON这样的数据表示语言,详情访问protobuf的google官方网站。
为什么要使用呢!
使用protobuf主要是, 一条消息数据,用protobuf序列化后的大小是json的10分之一,xml格式的20分之一,是二进制序列化的10分之一。
其次要知道,protobuf原生支持程序语言c++,java,python;其它的语言需要第三方或者自己写,序列化和反序列化的效率不保证。若是真要在php中使用,网络也很多,层次不齐,可斟酌!
参考:
https://github.com/protobuf-php/protobuf
8. protobuffer java中文乱码怎么解决
protobuf支持非UTF8字符串
protobuf规范string类型是必须是UTF8字符,但在C/C++中可以直接调用set方法设置任意编码方式的字符串,也可以直接取得对应字符串,但在控制台中会打印出编码不是UTF8字符的错误信息.
查看protobuf源代码发现是在wire_format.h中有一函数VerifyUTF8String()里进行编码判断的,而且有一宏定义GOOGLE_PROTOBUF_UTF8_VALIDATION_ENABLED可以取消此错误信息.估计google当初开发时用的是std::string类型,并没有编码方面的强制要求,只是在跨平台时没有统一编码容易引起问题,才统一使用UTF8方式传送字符串.
但像Java,Python缺省就是支持UNICODE,在protobuf库中就已经做了转换或检测,可以修改相关代码不做此转换或检测.
如python中修改lib中的protobufxxx.egg中的decoder.py的StringDecoder()方法,将value.append(local_unicode(buffer[pos:new_pos], 'utf-8'))
改为value.append(buffer[pos:new_pos])
,将field_dict[key] = local_unicode(buffer[pos:new_pos], 'utf-8')
改为field_dict[key] = buffer[pos:new_pos]
即可,Python即不会报异常错误,也能正确取得任意编码的字符串,但需要注意取出后需要进行编码(decode("gbk"))才能正确显示.
另外type_checkers.py中CheckValue()中对str的判断也需要去掉,encoder.py中带'utf-8'的全改了,才能正常编码.
至于如此改会不会有其它潜在的问题,还有待测试.
9. php protobuf不能为null吗
不能。
phpprotobuf必须满是程序数字不可以添加空的值。
phpProtocol是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通讯协议和数据存储等领域。
null是在计算中具有保留的值,用于指示指针不引用有效对象。
10. web后端开发需要什么技术
1.脚本语言基础 主流的有php java .net 非主流的有python ruby 还有最近出现的node.js golang 任一即可
2.数据库基础 后端就是跟数据库打交道的 一般学习关系型数据库即可
3.服务器基础 后端代码是运行在服务器上的 不像前端运行在客户浏览器 所以你需要掌握少许的服务器基础 至少要会用
4.以上三点满足即可开始后端开发 但是要提高还需要学会 缓存 队列应用 跨平台请求 分布式 等等 可以说 后端入门容易 但很宽也很广 只能一点点学习