maven集成protobuf插件实现proto文件转换java类文件

提示:Maven集成protobuf插件实现proto文件转换java类文件


前言

在网络通信中大家经常会接触到网络数据传输,然后不同的数据格式在传输中底层格式会有不同的却别。反之,不然以任何什么数据格式都是为了提高传输效率以及节省带宽消耗。常见的传输数据格式有fastjson(alibaba)、xml、protobuf(Google RPC)、hessian(Dubbo RPC)、jackson2(SpringMVC默认)、java原生序列化技术等。那这么多序列化方式我们到底选那种呢,这个就得根据具体的业务常见,框架来决定。如果对数据传输效率和空间有严格的要求就推荐大家用protobuf,如果比较折中方便上手可以建议用fastjson。下面我会从以上每种序列化技术实际占用空间大小来证明谁的压缩性能最好便于传输。


一、protobuf如何使用

本文以java开发环境为例,给大家分析如何使用google的protobuf序列化技术。

1、protobuf插件集成

<!--protobuf工具依赖包-->
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.30.2</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.30.2</version>
</dependency>

<!--protobuf转换插件-->
<build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.2</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.32.1:exe:${os.detected.classifier}</pluginArtifact>
                    <clearOutputDirectory>false</clearOutputDirectory>
                    <!--
                    只要根据实际工程目录,修改一下两个配置。
                    protoSourceRoot:表示proto源文件所在的目录
                    outputDirectory:生成java类文件包所在的目录
                    -->
                    <protoSourceRoot>src/main/proto</protoSourceRoot>
                    <outputDirectory>src/main/java</outputDirectory>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2、工程目录

在这里插入图片描述

3、Test.proto文件分析

在使用protobuf序列化技术的时候前提大家都得先定义proto这个文件,然后通过maven protobuf插件逆向生成java文件,这样在java工程就能使用了。

// 显示声明使用proto3语法, 否则使用默认的proto2,类似于制定JDK版本
syntax = "proto3";

// 生成java文件的全包名
option java_package = "com.nb.proto.dto";
// 生成类的文件名,否则默认生成的类名为proto文件名的驼峰命名
option java_outer_classname = "TestProto";
// 定义的所有消息、枚举和服务生成对应的多个类文件,而不是以内部类的形式出现
option java_multiple_files = false;

// TestService
service TestService {
  // test方法,格式为"方法名 请求参数 返回参数"
  rpc test (Request) returns (Reply) {}
  // test2
  rpc test2 (Request) returns (Reply) {}
}

// 方法请求,包含用户名。最终会是TestProto类中的一个静态内部类。也是我们业务最终实际需要用到的类。
message Request {
  string name = 1;
}

// 方法响应,包含响应的消息。最终会是TestProto类中的一个静态内部类。也是我们业务最终实际需要用到的类。
message Reply {
  string message = 1;
}

4、触发生成java文件

在工程pom同级目录执行一下命令:

mvn clean compile

生成下面2个文件
在这里插入图片描述
由于以上两个文件内容太长,这里就不贴出来分析了。关于当前文章我们核心关注TestProto.java文件。TestServiceGrpc会涉及到google的grpc调用,这篇文章不做分析,本文核心分析序列化本身技术,对于grpc感兴趣的同学可以自行研究查阅相关资料。

5、简单使用

package com.nb.proto.test;

import com.alibaba.fastjson2.JSON;
import com.nb.proto.dto.TestProto;

/**
 * @author duanshouzhi
 * @create 2023-08-03 10:20
 */
public class ProtoTestMain {

    public static void main(String[] args) {
        TestProto.Request build = TestProto.Request.newBuilder().setName("666").build();
        System.out.println(JSON.toJSONString(build));
    }
}

打印出:
在这里插入图片描述

二、常见序列化技术

ISerializer

用于定义序列化基础的两个职责。一个序列化serialize、一个发序列化deserialize操作。

package com.serialized.demo;

import java.io.IOException;

/**
 * @Auther: ShouZhi@Duan
 * @Description: 序列化工具
 */
public interface ISerializer {

    /**
     * 序列化
     */
    <T> byte[] serialize(T obj) throws IOException;

    /**
     * 反序列化
     */
    <T> T deserialize(byte[] data);
}

1、Hessian

package com.serialized.demo;

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * @Auther: ShouZhi@Duan
 * @Description: Hessian序列、反序列方式
 */
public class HessianSerializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        HessianOutput hessianOutput = new HessianOutput(byteArrayOutputStream);
        try {
            hessianOutput.writeObject(obj);
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new byte[0];
    }

    @Override
    public <T> T deserialize(byte[] data) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
        HessianInput hessianInput = new HessianInput(byteArrayInputStream);
        try {
            return (T) hessianInput.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

2、JavaSerializer

package com.serialized.demo;

import java.io.*;

/**
 * @Auther: ShouZhi@Duan
 * @Description: JAVA序列、反序列方式
 */
public class JavaSerializer implements ISerializer {

    /**
     * 序列化
     */
    @Override
    public <T> byte[] serialize(T obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        //写到文件中
        //ObjectOutputStream oosFile = new ObjectOutputStream(new FileOutputStream("E:/test/serial"));
        oos.writeObject(obj);
        byte[] bytes = baos.toByteArray();
        return bytes;
    }

    /**
     * 反序列化
     */
    @Override
    public <T> T deserialize(byte[] data) {
        ByteArrayInputStream byteArrayOutputStream = new ByteArrayInputStream(data);
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayOutputStream);
            //从文件中读取
            //ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(new File("E:/test/serial")));
            return (T) objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

3、XmlSerializer

package com.serialized.demo;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import lombok.extern.slf4j.Slf4j;

/**
 * @Auther: ShouZhi@Duan
 * @Description: XML序列、反序列方式
 */
@Slf4j
public class XmlSerializer implements ISerializer {

    XStream stream;

    public XmlSerializer() {
        stream = new XStream(new DomDriver());
    }

    @Override
    public <T> byte[] serialize(T obj) {
        byte[] bytes = this.stream.toXML(obj).getBytes();
        return bytes;
    }

    @Override
    public <T> T deserialize(byte[] data) {
        return (T) this.stream.fromXML(new String(data));
    }
}

三、序列化空间占用对比实验

1、SerialTest

测试基准:同一个对象实例:DataDTO("java", 6, "浙江省杭州市滨江区")。分析最终序列化后的空间大小,谁的结果最小说明谁的数据压缩效果最好。

package com.serialized.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.ByteString;
import com.serialized.dto.DataDTO;
import com.serialized.proto.DataDTOProto;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * @Auther: ShouZhi@Duan
 * @Description: 各种序列技术性能测试
 */
@Slf4j
public class SerialTest {

    private final static DataDTO dataDTO = new DataDTO("java", 6, "浙江省杭州市滨江区");

    /**
     * Java序列测试 233个字节
     */
    @Test
    public void javaSerialTest() throws IOException {
        ISerializer serializer = new JavaSerializer();
        //序列  233个字节
        byte[] serialize = serializer.serialize(dataDTO);
        log.info("JAVA序列后的对象大小:" + serialize.length + "字节");
        //反序列
        DataDTO deserialize = serializer.deserialize(serialize);
        log.info("JAVA反序列得到的对象数据:" + deserialize);
        log.info("JAVA序列测试完成");
    }

    /**
     * Xml序列测试 144个字节
     */
    @Test
    public void xmlSerialTest() throws IOException {
        ISerializer serializer = new XmlSerializer();
        //序列  144个字节
        byte[] serialize = serializer.serialize(dataDTO);
        log.info("XML序列后的对象大小:" + serialize.length + "字节");
        //反序列
        DataDTO deserialize = serializer.deserialize(serialize);
        log.info("XML反序列得到的对象数据:" + deserialize);
        log.info("XML序列测试完成");
    }

    /**
     * Hessian序列测试 97个字节
     */
    @Test
    public void hessianSerialTest() throws IOException {
        ISerializer serializer = new HessianSerializer();
        //序列  97个字节
        byte[] serialize = serializer.serialize(dataDTO);
        log.info("HESSIAN序列后的对象大小:" + serialize.length + "字节");
        //反序列
        DataDTO deserialize = serializer.deserialize(serialize);
        log.info("HESSIAN反序列得到的对象数据:" + deserialize);
        log.info("HESSIAN序列测试完成");
    }

    /**
     * Jackson2序列测试 60个字节
     */
    @Test
    public void jacksoSerialTest() throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectMapper o = new ObjectMapper();
        o.writeValue(out, dataDTO);
        log.info("JACKSON2序列后对象大小:" + out.toByteArray().length);
    }

    /**
     * FastJson序列测试 40字节
     */
    @Test
    public void jsonSerialTest() {
        System.out.println(JSON.toJSONString(dataDTO, SerializerFeature.BeanToArray));
        byte[] bytes = JSON.toJSONBytes(dataDTO, SerializerFeature.BeanToArray);
        log.info("FASTJSON序列后对象大小:" + bytes.length);
    }

    /**
     * ProtoBuf序列测试 37个字节
     */
    @Test
    public void protoBufSerialTest() {
        DataDTOProto.DataDTO dataDTO = DataDTOProto.DataDTO.newBuilder().setName("java").setAge(6).setAddr("浙江省杭州市滨江区").build();
        ByteString bs = dataDTO.toByteString();
        //序列 37个字节
        byte[] bytes = bs.toByteArray();
        log.info("PROTOC序列后的对象大小:" + bytes.length + "字节");
    }

}

2、结果分析

  • ProtoBuf序列测试 37个字节
  • FastJson序列测试 40字节
  • Jackson2序列测试 60个字节
  • Hessian序列测试 97个字节
  • Xml序列测试 144个字节
  • Java序列测试 233个字节
从上面结果肉眼数字很容易看出来protobuf的压缩性能是最好的也是最小的空间占用、其次是fastjson。二者相差百分比不是很大。由此可见阿里的fastjson性能相对还是比较可靠的。

四、SpringBoot如何使用protobuf序列化

这个其实很简单,网上有很多相关资料
推荐:https://blog.csdn.net/zhou870498/article/details/130871074

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值