Springboot集成ProtoBuf的实例
作者:xrq0508
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; /** * 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标签需要按照顺序往下排,如果需要新增字段,只能接着往下排,不能改变已存在的标签序号
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、使用方式
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 官网看到在创建对象,将对象序列化为内存中的字节序列,然后再反序列化的整个过程中相比其他相似技术的性能测试结果图,但是在通用性上会存在局限性,功能相对简单,不适合用来描述数据结构。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。