Springboot集成ProtoBuf
ProtoBuf是一种序列化和解析速度远高于JSON和XML的数据格式,项目中使用了CouchBase作为缓存服务器,从数据库中拿到数据后通过protobuf序列化后放入CouchBase作为缓存,查询数据的时候解压并反序列化成数据对象,下面是ProtoBuf的具体使用方法
1、pom.xml引入相关依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
< dependency > < groupId >com.google.protobuf</ groupId > < artifactId >protobuf-java</ artifactId > < version >3.5.0</ version > </ dependency > < dependency > < groupId >io.protostuff</ groupId > < artifactId >protostuff-core</ artifactId > < version >1.4.0</ version > </ dependency > < dependency > < groupId >io.protostuff</ groupId > < artifactId >protostuff-runtime</ artifactId > < version >1.4.0</ version > </ dependency > |
2、新建序列化工具类ProtoBufUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package com.xrq.demo.utils; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.objenesis.Objenesis; import org.springframework.objenesis.ObjenesisStd; import io.protostuff.LinkedBuffer; import io.protostuff.ProtostuffIOUtil; import io.protostuff.Schema; import io.protostuff.runtime.RuntimeSchema; /** * ProtoBufUtil 转换工具类 * * @author XRQ * */ public class ProtoBufUtil { private static Logger log = LoggerFactory.getLogger(ProtoBufUtil. class ); private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>(); private static Objenesis objenesis = new ObjenesisStd( true ); @SuppressWarnings ( "unchecked" ) private static <T> Schema<T> getSchema(Class<T> cls) { Schema<T> schema = (Schema<T>) cachedSchema.get(cls); if (schema == null ) { schema = RuntimeSchema.createFrom(cls); if (schema != null ) { cachedSchema.put(cls, schema); } } return schema; } public ProtoBufUtil() { } @SuppressWarnings ({ "unchecked" }) public static <T> byte [] serializer(T obj) { Class<T> cls = (Class<T>) obj.getClass(); LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); try { Schema<T> schema = getSchema(cls); return ProtostuffIOUtil.toByteArray(obj, schema, buffer); } catch (Exception e) { log.error( "protobuf序列化失败" ); throw new IllegalStateException(e.getMessage(), e); } finally { buffer.clear(); } } public static <T> T deserializer( byte [] bytes, Class<T> clazz) { try { T message = (T) objenesis.newInstance(clazz); Schema<T> schema = getSchema(clazz); ProtostuffIOUtil.mergeFrom(bytes, message, schema); return message; } catch (Exception e) { log.error( "protobuf反序列化失败" ); throw new IllegalStateException(e.getMessage(), e); } } } |
3、新建实体类User.java
注:重点是@Tag标签需要按照顺序往下排,如果需要新增字段,只能接着往下排,不能改变已存在的标签序号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
package com.xrq.demo.bo; import java.io.Serializable; import java.util.Date; import io.protostuff.Tag; /** * 用户信息类 * * @ClassName: User * @author XRQ * @date 2019年4月30日 */ public class User implements Serializable { private static final long serialVersionUID = 1L; // 用户ID @Tag ( 1 ) private int userId; // 用户类型 @Tag ( 2 ) private int userTypeId; // 用户名 @Tag ( 3 ) private String userName; // 创建时间 @Tag ( 4 ) private Date createDateTime; public int getUserId() { return userId; } public void setUserId( int userId) { this .userId = userId; } public int getUserTypeId() { return userTypeId; } public void setUserTypeId( int userTypeId) { this .userTypeId = userTypeId; } public String getUserName() { return userName; } public void setUserName(String userName) { this .userName = userName; } public Date getCreateDateTime() { return createDateTime; } public void setCreateDateTime(Date createDateTime) { this .createDateTime = createDateTime; } } |
4、使用方式
1
2
3
4
5
6
7
8
9
10
|
User user = new User(); user.setUserId( 1 ); user.setUserTypeId( 1 ); user.setUserName( "XRQ" ); user.setCreateDateTime( new Date()); //序列化成ProtoBuf数据结构 byte [] userProtoObj= ProtoBufUtil.serializer(userInfo) //ProtoBuf数据结构反序列化成User对象 User newUserObj = ProtoBufUtil.deserializer(userProtoObj, User. class )) |
ProtoBuf+Java+Springboot+IDEA应用
什么是Protobuf
1.Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准;
2.Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式;
3.形式为.proto结尾的文件;
应用环境
近期接触公司的网约车接口对接项目,第三方公司限定了接口的数据用protobuf格式序列化后,通过AES128加密后传输。
开发环境
Java+Spring boot+IDEA+Windows
新建Spring boot项目 在IDEA开发工具中安装protobuf插件
添加配置maven依赖(可能一开始自己的项目中存在固有的配置,不要删除,在对应的地方添加下面的配置即可,不需要修改)
1
2
3
4
5
|
< dependency > < groupId >io.protostuff</ groupId > < artifactId >protostuff-runtime</ artifactId > < version >1.4.0</ version > </ dependency > |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
< plugin > < groupId >org.xolstice.maven.plugins</ groupId > < artifactId >protobuf-maven-plugin</ artifactId > < version >0.5.0</ version > < configuration > < protocArtifact > com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier} </ protocArtifact > < pluginId >grpc-java</ pluginId > </ configuration > < executions > < execution > < goals > < goal >compile</ goal > < goal >compile-custom</ goal > </ goals > </ execution > </ executions > </ plugin > |
写项目对应的.proto文件(这里贴一部分代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 版本号 syntax = "proto2"; // 打包路径 option java_package="XXX1"; // Java文件名 option java_outer_classname="XXX2"; // 属性信息 message BaseInfo { // 公司标识 required string CompanyId = 1; // 公司名称 required string CompanyName = 2; // 操作标识 required uint32 Flag = 3; // 更新时间 required uint64 UpdateTime = 4; } |
.proto文件存在项目路径
生成.java文件方式(点击项目中的.proto文件,找到对应的maven依赖,双击标识2对应的protobuf:compile文件)
运行成功之后在对应路径下查看生成的.java文件
生成的Java文件即可使用(如果在业务中.proto文件很大,生成的Java大小超出IDEA默认的2.5M,生成的Java文件将被IDEA误认为不是Java文件,导致生成的Java文件不可使用,这时需要修改IDEA的配置文件,将默认的大小修改,重启IDEA即可) 进行数据序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
List<BaseInfo> baseInfoList = baseInfoMapper.selectAll(); XXX2.OTIpcList.Builder listBuilder = XXX2.OTIpcList.newBuilder(); XXX2.OTIpc.Builder otipcBuilder = XXX2.OTIpc.newBuilder(); otipcBuilder.setCompanyId(ProjectConstant.COMPANY_ID); otipcBuilder.setSource(ProjectConstant.COMPANY_SOURCE); otipcBuilder.setIPCType(OTIpcDef.IpcType.baseInfoCompany); for ( int i= 0 ;i<baseInfoList .size();i++){ try { XXX2.BaseInfo.Builder baseInfoBuilder = XXX2.BaseInfo.newBuilder(); baseInfoBuilder .setCompanyId(baseInfoList .get(i).getCompanyid()); baseInfoBuilder .setCompanyName(baseInfoList .get(i).getCompanyname()); baseInfoBuilder .setFlag(baseInfoList .get(i).getFlag()); baseInfoBuilder .setUpdateTime(baseInfoList .get(i).getUpdatetime()); for ( int j= 0 ;j< 10 ;j++) { otipcBuilder.addBaseInfo(baseInfoBuilder .build()); } } catch (Exception e){ LoggerUtils.info(getClass(),e.getMessage()); } } listBuilder.addOtpic(otipcBuilder); |
进行数据AES128位加密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public static String sendScreate(OTIpcDef.OTIpcList.Builder listBuilder) { String screateKeyString = getSecretKey(); String screateKey = screateKeyString.split( ";" )[ 1 ]; String screateValue = screateKeyString.split( ";" )[ 0 ]; OTIpcDef.OTIpcList list = listBuilder.build(); ByteArrayOutputStream output = new ByteArrayOutputStream(); try { list.writeTo(output); } catch (IOException e) { e.printStackTrace(); } byte [] data = null ; try { byte [] keyByte = new byte [screateValue.length()/ 2 ]; for ( int j= 0 ;j<screateValue.length()/ 2 ;j++) { byte b = ( byte ) ((Integer.valueOf(String.valueOf(screateValue.charAt(j* 2 )), 16 ) << 4 ) | Integer.valueOf(String.valueOf(screateValue.charAt(j* 2 + 1 )), 16 )); keyByte[j] = b; } Key secretKey= new SecretKeySpec(keyByte, "AES" ); Security.addProvider( new org.bouncycastle.jce.provider.BouncyCastleProvider()); Cipher cipher= Cipher.getInstance( "AES/ECB/PKCS7Padding" , "BC" ); cipher.init(Cipher.ENCRYPT_MODE, secretKey); data = cipher.doFinal(output.toByteArray()); sendPostJSON(screateKey, data); return "success" ; } catch (Exception e){ e.printStackTrace(); } return null ; } |
小结一下:经验证,Protobuf 序列化相比XML,JSON性能更好,在Protobuf 官网看到在创建对象,将对象序列化为内存中的字节序列,然后再反序列化的整个过程中相比其他相似技术的性能测试结果图,但是在通用性上会存在局限性,功能相对简单,不适合用来描述数据结构。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/xrq0508/article/details/89704159