Springboot集成ProtoBuf
ProtoBuf是一种序列化和解析速度远高于JSON和XML的数据格式,项目中使用了CouchBase作为缓存服务器,从数据库中拿到数据后通过protobuf序列化后放入CouchBase作为缓存,查询数据的时候解压并反序列化成数据对象,下面是ProtoBuf的具体使用方法
1、pom.xml引入相关依赖
<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
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;
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标签需要按照顺序往下排,如果需要新增字段,只能接着往下排,不能改变已存在的标签序号
package com.xrq.demo.bo;
import java.io.Serializable;
import java.util.Date;
import io.protostuff.Tag;
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、使用方式
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依赖(可能一开始自己的项目中存在固有的配置,不要删除,在对应的地方添加下面的配置即可,不需要修改)
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.4.0</version>
</dependency>
<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文件(这里贴一部分代码)
// 版本号
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即可) 进行数据序列化
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位加密
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 官网看到在创建对象,将对象序列化为内存中的字节序列,然后再反序列化的整个过程中相比其他相似技术的性能测试结果图,但是在通用性上会存在局限性,功能相对简单,不适合用来描述数据结构。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。