Protobuf 数据传输实例

Protubuf 数据传输详解

    • Content-Type 首部
    • 从客户端向服务端 POST 数据
    • 定义protobuf文件
    • 服务器端代码
    • 客户端代码
    • 运行结果
    • 比较客户端报文长度

Content-Type 首部

MIME-TYPE multipurpose internet mail extension 多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。Http报文中,Content-type首部就是用来说明实体主体的MIME类型,它的值就是标准化的MIME-TYPE。对于spring-boot,当你在controller中使用@RequestBody时,相当于默认了Content-type: application/json,当然你也可以按照字符串来读取body中的内容。

从客户端向服务端 POST 数据

我们以 Spring-Boot 为服务器端开发框架,接收由客户端发送的二进制流数据时,可以100%利用protobuf的压缩特性,高效的进行数据传输,如果把编译后的二进制流嵌入JSON中传输,那就失去了protobuf进行数据序列化的意义。下面我们通过 Spring-Boot,编写一个 Controller, 模拟网关对于各种 content-type 的处理,来理解 Protobuf 的神奇之处。

定义protobuf文件

syntax = "proto3";
message Result{
    string id = 1;
    string title = 2;
    string content = 3;
}

服务器端代码

在Spring-Boot的服务器端,我们既可以使用inputStream来接收包含protobuf的post请求,也可以直接按string来接收请求。实际上,客户端都是将数据按照protobuf规则编译成为byte-array,所以客户端在http header 中添加的content-type并不会对后台的数据处理造成影响。

import com.cso.localhazrd.demo.domain.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;


@RestController
public class LhiDemoController {

    private final static Logger logger = LoggerFactory.getLogger(LhiDemoController.class);

    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public ResponseEntity saveLocalHazard(HttpServletRequest request) throws Exception {
        Message.Result result = Message.Result.parseFrom(request.getInputStream());
        logger.info("Result object is: "+ result.getId() +"--"+ result.getTitle() + " -- content: --" + result.getContent());
        return new ResponseEntity(result.getId(), HttpStatus.OK);
    }

}

客户端代码

通过测试,我们发现无论客户端选择什么类型的MIME-Type,都不会对protobuf的传输造成影响,从google为protobuf序列化生成的方法名称我们就可以发现,方法名称为SerializeToString(),序列化的结果是一个二进制流,但是以字符串的形式进行传输。

import requests
import message_pb2


if __name__=='__main__':
    result = message_pb2.Result()
    result.id = "1"
    result.title = "aabb"
    result.content = "This is just a demo"
    headers = {'Content-type': 'application/octet-stream'}
    #headers = {'Content-type': 'application/x-protobuf'}
    data = result.SerializeToString()
    requests.post("http://localhost:8080/create",data=data,headers=headers)    

在请求中,我们将MIME-TYPE的类型定义成为了二进制流,实际上,这个header并不会影响服务器端对于数据的处理。但是为了便于后期的开发者能够理解代码,我们选择了application/octet-stream或者application/x-protobuf

运行结果

我们可以看到在服务器端的控制台中,日志成功输出了

c.c.l.demo.controller.LhiDemoController  : Result object is: 1--aabb -- content: --This is just a demo

比较客户端报文长度

from google.protobuf import message
import requests
import message_pb2


if __name__=='__main__':
    result = message_pb2.Result()
    result.id = "1"
    result.title = "aabb"
    result.content = "This is just a demo"
    headers = {'Content-type': 'application/octet-stream'}
    data = result.SerializeToString()
    #check the bytes length of proto
    proto_length=len(data)
    print("proto: the length is {}".format(proto_length))
    #check the bytes length of json
    message = "{id:\"1\", title:\"aabb\", content:\"this is just a demo\"}"
    print("json: the length is {}".format(len(bytes(message,'UTF-8'))))
    #requests.post("http://localhost:8080/create",data=data,headers=headers)     

通过在客户端修改一点代码,我们分别把数据按照json和proto,都转换为二进制流,最终我们发现结果如下:

  • JSON 的大小为53 bytes
  • Proto 序列化以后的长度为30 bytes

由此可见,将数据按照Protobuf进行序列化后传输,可以显著减少数据大小,提高传输效率。

你可能感兴趣的:(Protobuf,spring,boot,restful,java,protobuf)