好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Springboot集成ProtoBuf的实例

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

查看更多关于Springboot集成ProtoBuf的实例的详细内容...

  阅读:41次