使用protobuf實(shí)現(xiàn)跨語言序列化 Java和Python實(shí)例

使用protobuf實(shí)現(xiàn)跨語言序列化 Java和Python實(shí)例

首先下載安裝protoc

  • github.releases
  • 對(duì)于OS X可以通過brew直接安裝 brew install protobuf
  • 安裝完可以通過protoc --version查看版本信息

創(chuàng)建proto文件-帶Any類型的版本

帶Any類型的只能導(dǎo)出pb2類型的Python文件描孟,沒法導(dǎo)出Python3 的版本,暫時(shí)不知道如何解決

  • Any類型的字段可以在java中實(shí)現(xiàn)泛型的功能

  • MessageDto.proto

    syntax = "proto3";
    import "google/protobuf/any.proto";
    message MessageDto {
      string action=1;
      int32 statte=2;
      google.protobuf.Any data=3;
    }
    
  • RpcCmd.proto

    syntax = "proto3";
    import "MessageDto.proto";
    message RpcCmd {
      MessageDto message=1;
      string randomKey=2;
      string remoteAddressKey=3;
    }
    
  • Point2PointMessage.proto

    syntax = "proto3";
    message Point2PointMessage {
      string targetAddressKey;
      string message;
    }
    
  • BytesData.proto

    syntax = "proto3";
    message BytesData {
        bytes content=1;
    }
    

導(dǎo)出相應(yīng)的對(duì)象定義文件

Python版

  • protoc --python_out=./gen_pb2 RpcCmd.proto MessageDto.proto Point2PointMessage.proto BytesData.proto

  • 生成的文件名為XXX_pb2.py

    Java版

  • protoc --java_out=./gen_java RpcCmd.proto MessageDto.proto Point2PointMessage.proto BytesData.proto

  • 生成的文件名為XXXOuterClass.java

在Python中使用

  • 首先要導(dǎo)入生成的文件,放到自己喜歡的包下陋率,然后修改導(dǎo)入包的地址庵朝,比如RpcCmd_pb2.py中修改

    import com.tony.proto.py2.MessageDto_pb2 as MessageDto__pb2
    
  • 然后開始使用

    from com.tony.proto.py2 import RpcCmd_pb2, Point2PointMessage_pb2, BytesData_pb2, MessageDto_pb2
    
    
    def serialize_to_file(file_path):
        p2p_msg = Point2PointMessage_pb2.Point2PointMessage()
        p2p_msg.message = "Hello, p2p from python"
        p2p_msg.targetAddressKey = "/127.0.0.1:38211"
    
        # bytes_data = BytesData_pb2.BytesData()
        # bytes_data.content = b"Hello, bytes data from python"
    
        rpc_cmd = RpcCmd_pb2.RpcCmd()
        rpc_cmd.randomKey = "random-key-key-random"
        rpc_cmd.remoteAddressKey = "/127.0.0.1:1234"
        rpc_cmd.message.action = "p2p"
        rpc_cmd.message.state = 100
        rpc_cmd.message.data.Pack(p2p_msg)
        # rpc_cmd.message.data.Pack(bytes_data)
    
        bytes_write = rpc_cmd.SerializeToString()
    
        fw = open(file_path, mode="wb")
        fw.write(bytes_write)
        fw.flush()
        fw.close()
        print("write bytes to file:", bytes_write)
    
    
    def deserialize_from_file(file_path):
        fo = open(file_path, mode="rb")
        bytes_read = fo.read()
        fo.close()
        print("read bytes from file:", bytes_read)
        rpc_cmd = RpcCmd_pb2.RpcCmd()
        rpc_cmd.ParseFromString(bytes_read)
        print(rpc_cmd)
    
        p2p_msg = Point2PointMessage_pb2.Point2PointMessage()
        # bytes_data = BytesData_pb2.BytesData()
        # rpc_cmd.message.data.Unpack(bytes_data)
        rpc_cmd.message.data.Unpack(p2p_msg)
        
        print("msg_content:", p2p_msg.message)
        print("msg_target:", p2p_msg.targetAddressKey)
        # print("bytes_data:", str(bytes_data.content, 'utf-8'))
    
    
    if __name__ == "__main__":
        serialize_file_path = "/trans-data-pb2.dat"
        serialize_to_file(serialize_file_path)
        deserialize_from_file(serialize_file_path)
    
    
  • 執(zhí)行結(jié)果如下择吊,可以將bytes_data相關(guān)的注釋取消同時(shí)注釋掉p2p_msg相關(guān)的測(cè)試BytesData類型的序列化和反序列化

    write bytes to file: b'\n]\n\x03p2p\x10d\x1aT\n&type.googleapis.com/Point2PointMessage\x12*\n\x10/127.0.0.1:38211\x12\x16Hello, p2p from python\x12\x15random-key-key-random\x1a\x0f/127.0.0.1:1234'
    read bytes from file: b'\n]\n\x03p2p\x10d\x1aT\n&type.googleapis.com/Point2PointMessage\x12*\n\x10/127.0.0.1:38211\x12\x16Hello, p2p from python\x12\x15random-key-key-random\x1a\x0f/127.0.0.1:1234'
    message {
      action: "p2p"
      state: 100
      data {
        type_url: "type.googleapis.com/Point2PointMessage"
        value: "\n\020/127.0.0.1:38211\022\026Hello, p2p from python"
      }
    }
    randomKey: "random-key-key-random"
    remoteAddressKey: "/127.0.0.1:1234"
    
    msg_content: Hello, p2p from python
    msg_target: /127.0.0.1:38211
    

在Java中使用

  • 同樣導(dǎo)入到喜歡的包下,修改對(duì)應(yīng)的包名即可

    @Slf4j
    public class ProtobufSerializeDemo {
    
        @Test
        public void serializeToFile() throws Exception {
            Point2PointMessageOuterClass.Point2PointMessage.Builder p2pMsgBuilder = Point2PointMessageOuterClass.Point2PointMessage.newBuilder();
            p2pMsgBuilder.setTargetAddressKey("/127.0.0.1:1233");
            p2pMsgBuilder.setMessage("hello from java");
    
    //        BytesDataOuterClass.BytesData.Builder bytesBuilder = BytesDataOuterClass.BytesData.newBuilder();
    //        bytesBuilder.setContent(ByteString.copyFrom("bytes data from java".getBytes(StandardCharsets.UTF_8)));
    
    
            MessageDtoOuterClass.MessageDto.Builder messageBuilder = MessageDtoOuterClass.MessageDto.newBuilder();
            messageBuilder.setAction("p2p");
            messageBuilder.setState(100);
            messageBuilder.setData(Any.pack(p2pMsgBuilder.build()));
    //        messageBuilder.setData(Any.pack(bytesBuilder.build()));
    
    
            RpcCmdOuterClass.RpcCmd.Builder builder = RpcCmdOuterClass.RpcCmd.newBuilder();
            builder.setRandomKey("RANDOM_KEY_JAVA");
            builder.setRemoteAddressKey("/127.0.0.1:1234");
            builder.setMessage(messageBuilder.build());
    
            builder.build().writeTo(new FileOutputStream("java_protobuf.dat"));
        }
    
    
        @Test
        public void deserializeFromFile() throws Exception {
            RpcCmdOuterClass.RpcCmd rpcCmd = RpcCmdOuterClass.RpcCmd.parseFrom(new FileInputStream("java_protobuf.dat"));
            Point2PointMessageOuterClass.Point2PointMessage p2pMsg = rpcCmd.getMessage().getData().unpack(Point2PointMessageOuterClass.Point2PointMessage.class);
    //        BytesDataOuterClass.BytesData bytesData = rpcCmd.getMessage().getData().unpack(BytesDataOuterClass.BytesData.class);
            log.info("deserialize rpcCmd: \n{}", rpcCmd);
            log.info("deserialize p2pMsg: \n{}", p2pMsg);
    //        log.info("deserialize bytesData: \n{}", bytesData);
        }
    }
    
  • 執(zhí)行結(jié)果缓苛,可以將bytes_data相關(guān)的注釋取消同時(shí)注釋掉p2p_msg相關(guān)的測(cè)試BytesData類型的序列化和反序列化

    10:22:03.118 [main] INFO com.tony.proto.ProtobufSerializeDemo - deserialize rpcCmd: 
    message {
      action: "p2p"
      state: 100
      data {
        type_url: "type.googleapis.com/Point2PointMessage"
        value: "\n\017/127.0.0.1:1233\022\017hello from java"
      }
    }
    randomKey: "RANDOM_KEY_JAVA"
    remoteAddressKey: "/127.0.0.1:1234"
    
    10:22:03.168 [main] INFO com.tony.proto.ProtobufSerializeDemo - deserialize p2pMsg: 
    targetAddressKey: "/127.0.0.1:1233"
    message: "hello from java"
    
    

然后是Java和Python之間互相序列化和反序列化

  • 只需要修改對(duì)應(yīng)的文件地址就可以進(jìn)行測(cè)試
Python反序列化Java
java_serialize_file_path = $path_to_java_serialized$
deserialize_from_file(java_serialize_file_path)
  • 執(zhí)行結(jié)果,這里演示的是BytesData類型的

    read bytes from file: b'\n@\n\x03p2p\x10d\x1a7\n\x1dtype.googleapis.com/BytesData\x12\x16\n\x14bytes data from java\x12\x0fRANDOM_KEY_JAVA\x1a\x0f/127.0.0.1:1234'
    message {
      action: "p2p"
      state: 100
      data {
        type_url: "type.googleapis.com/BytesData"
        value: "\n\024bytes data from java"
      }
    }
    randomKey: "RANDOM_KEY_JAVA"
    remoteAddressKey: "/127.0.0.1:1234"
    
    bytes_data: bytes data from java
    
Java反序列化Python
    @Test
    public void deserializeFromPythonFile() throws Exception {
        RpcCmdOuterClass.RpcCmd rpcCmd = RpcCmdOuterClass.RpcCmd.parseFrom(new FileInputStream($path_to_python_serialize$));
//        Point2PointMessageOuterClass.Point2PointMessage p2pMsg = rpcCmd.getMessage().getData().unpack(Point2PointMessageOuterClass.Point2PointMessage.class);
        BytesDataOuterClass.BytesData bytesData = rpcCmd.getMessage().getData().unpack(BytesDataOuterClass.BytesData.class);
        log.info("deserialize rpcCmd: \n{}", rpcCmd);
//        log.info("deserialize p2pMsg: \n{}", p2pMsg);
        log.info("deserialize bytesData: \n{}", bytesData);
    }
  • 執(zhí)行結(jié)果邓深,同樣是BytesData類型的

    10:33:03.360 [main] INFO com.tony.proto.ProtobufSerializeDemo - deserialize rpcCmd: 
    message {
      action: "p2p"
      state: 100
      data {
        type_url: "type.googleapis.com/BytesData"
        value: "\n\035Hello, bytes data from python"
      }
    }
    randomKey: "random-key-key-random"
    remoteAddressKey: "/127.0.0.1:1234"
    
    10:33:03.402 [main] INFO com.tony.proto.ProtobufSerializeDemo - deserialize bytesData: 
    content: "Hello, bytes data from python"
    
    

在Java平臺(tái)未桥,還有個(gè)更好用的工具可以不用手寫proto文件

  • 這個(gè)工具是io.protostuff

  • 通過maven導(dǎo)入依賴

    <dependency>
      <groupId>io.protostuff</groupId>
      <artifactId>protostuff-core</artifactId>
      <version>1.6.0</version>
    </dependency>
    <dependency>
      <groupId>io.protostuff</groupId>
      <artifactId>protostuff-runtime</artifactId>
      <version>1.6.0</version>
    </dependency>
    <!-- 用于創(chuàng)建對(duì)象 -->
    <dependency>
      <groupId>org.objenesis</groupId>
      <artifactId>objenesis</artifactId>
      <version>2.2</version>
    </dependency>
    

創(chuàng)建序列化工具類

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.DefaultIdStrategy;
import io.protostuff.runtime.RuntimeSchema;
import lombok.extern.slf4j.Slf4j;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;

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

/**
 * 基于Protostuff優(yōu)化版的ProtobufIOUtil實(shí)現(xiàn)序列化,理論上可以支持跨語言序列化
 *
 * @author jiangwenjie 2019/10/30
 */
@Slf4j
public class ProtobufSerializer {


    private final static Objenesis OBJENESIS = new ObjenesisStd(true);

    private ProtobufSerializer() {
    }

    private static class SingletonHolder {
        final static ProtobufSerializer INSTANCE = new ProtobufSerializer();
    }

    public static ProtobufSerializer getInstance() {
        return ProtobufSerializer.SingletonHolder.INSTANCE;
    }

    public void serialize(Object obj, OutputStream outputStream) {
        Class clz = obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema schema = getSchema(clz);
            ProtobufIOUtil.writeTo(outputStream, obj, schema, buffer);
        } catch (IOException e) {
            log.error("序列化對(duì)象失敗", e);
        } finally {
            buffer.clear();
        }
    }

    public byte[] serialize(Object obj) {
        Class clz = obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try (ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream()) {
            Schema schema = getSchema(clz);
            ProtobufIOUtil.writeTo(arrayOutputStream, obj, schema, buffer);
            return arrayOutputStream.toByteArray();
        } catch (IOException e) {
            log.error("序列化對(duì)象失敗", e);
        } finally {
            buffer.clear();
        }
        return new byte[0];
    }

    public <T> T deSerialize(InputStream inputStream, Class<T> clazz) {
        T object = OBJENESIS.newInstance(clazz);
        Schema<T> schema = getSchema(clazz);
        try {
            ProtobufIOUtil.mergeFrom(inputStream, object, schema);
            return object;
        } catch (IOException e) {
            log.error("反序列化對(duì)象失敗", e);
        }
        return null;
    }

    public <T> T deSerialize(byte[] param, Class<T> clazz) {
        T object = OBJENESIS.newInstance(clazz);
        Schema<T> schema = getSchema(clazz);
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(param)) {
            ProtobufIOUtil.mergeFrom(inputStream, object, schema);
            return object;
        } catch (IOException e) {
            log.error("反序列化對(duì)象失敗", e);
        }
        return null;
    }


    private <T> Schema<T> getSchema(Class<T> clz) {
        return RuntimeSchema.createFrom(clz, new DefaultIdStrategy());
    }
}

創(chuàng)建序列化對(duì)象

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;

/**
 * @author jiangwenjie 2019/10/22
 */
@Data
@Slf4j
public class RpcCmd implements Serializable {
    private MessageDto message;
    private String randomKey;
    /**
     * 目標(biāo)地址芥备,不需要序列化傳輸
     */
    private transient String remoteAddressKey;
}



import com.tony.constants.EnumNettyState;
import com.tony.serializer.impl.ProtobufSerializer;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;

/**
 * 消息對(duì)象
 *
 * @author jiangwenjie 2019/10/22
 */
@Slf4j
@Data
@ToString
@EqualsAndHashCode
public class MessageDto implements Serializable {

    private String action;

    private int state = 100;

    /**
     * 跨語言使用Protostuff中提供的protobuff序列化傳遞復(fù)雜對(duì)象
     */
    private byte[] bytesData;

    private Serializable serialData;

    public <T> T dataOfClazz(Class<T> clazz, boolean isStuff) {
        if (isStuff) {
            return serialDataOfClazz(clazz);
        } else {
            return bytesDataOfClass(clazz);
        }
    }

    public <T extends Serializable> void setData(T object, boolean isStuff) {
        if (isStuff) {
            setSerialData(object);
        } else {
            setBytesData(object);
        }
    }

    @SuppressWarnings("unchecked")
    private <T> T serialDataOfClazz(Class<T> clazz) {
        if (serialData == null) {
            return null;
        }
        if (clazz.isInstance(serialData)) {
            return (T)serialData;
        } else {
            throw new IllegalArgumentException("data is not instance of class:" + clazz.getName());
        }
    }

    private <T> T bytesDataOfClass(Class<T> clazz) {
        if (bytesData == null) {
            return null;
        }
        try {
            return ProtobufSerializer.getInstance().deSerialize(bytesData, clazz);
        } catch (Exception e) {
            log.error("反序列化data對(duì)象失敗冬耿,請(qǐng)確認(rèn)對(duì)象是否為:{} 類型", clazz);
        }
        return null;
    }

    private <T extends Serializable> void setBytesData(T data) {
        this.bytesData = ProtobufSerializer.getInstance().serialize(data);
    }
}


import lombok.Data;

import java.io.Serializable;

/**
 * 點(diǎn)對(duì)點(diǎn)通信data對(duì)象
 *
 * @author jiangwenjie 2019/10/26
 */
@Data
public class Point2PointMessage implements Serializable {
    private String targetAddressKey;
    private String message;
}


序列化測(cè)試

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * @author jiangwenjie 2019/11/1
 */
@Slf4j
public class JavaProtostuffSerializeDemo {

    @Test
    public void serializeToFile() throws Exception {
        Point2PointMessage p2pMsg = new Point2PointMessage();
        p2pMsg.setTargetAddressKey("/127.0.0.1:1233");
        p2pMsg.setMessage("message from java");
        MessageDto messageDto = new MessageDto();
        messageDto.setAction("p2p");
        messageDto.setState(100);
        messageDto.setData(p2pMsg, false);

        RpcCmd rpcCmd = new RpcCmd();
        rpcCmd.setMessage(messageDto);
        rpcCmd.setRandomKey("RANDOM_KEY_JAVA");
        rpcCmd.setRemoteAddressKey("/127.0.0.1:1234");
        ProtobufSerializer.getInstance().serialize(rpcCmd, new FileOutputStream("java_proto_simple.dat"));
    }

    @Test
    public void deserializeFromFile() throws Exception {
        RpcCmd rpcCmd = ProtobufSerializer.getInstance().deSerialize(new FileInputStream("java_proto_simple.dat"), RpcCmd.class);
        log.info("deserialize cmd:\n{}", rpcCmd);
        log.info("deserialize p2p msg:\n{}", rpcCmd.getMessage().dataOfClazz(Point2PointMessage.class, false));
    }
}

測(cè)試輸出

11:02:45.646 [main] INFO com.tony.simple.JavaProtostuffSerializeDemo - deserialize cmd:
RpcCmd(message=MessageDto(action=p2p, state=100, bytesData=[10, 15, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 49, 50, 51, 51, 18, 17, 109, 101, 115, 115, 97, 103, 101, 32, 102, 114, 111, 109, 32, 106, 97, 118, 97], serialData=null, isFromBuff=false), randomKey=RANDOM_KEY_JAVA, remoteAddressKey=null)
11:02:45.651 [main] INFO com.tony.simple.JavaProtostuffSerializeDemo - deserialize p2p msg:
Point2PointMessage(targetAddressKey=/127.0.0.1:1233, message=message from java)

MessageDto中的Data 可以泛型化使用

/**
 * 跨語言使用Protostuff中提供的protobuff序列化傳遞復(fù)雜對(duì)象
 */
private byte[] bytesData;
private Serializable serialData;
  • 當(dāng)序列化和反序列化不需要跨平臺(tái)使用時(shí),可以直接使用Serializable類型萌壳,反之需要用byte數(shù)組保存數(shù)據(jù)亦镶,進(jìn)行二次序列化和反序列化。同時(shí)可以在序列化工具類ProtobufSerializer中將ProtobufIOUtil修改為ProtostuffIOUtil

  • 通過setData方法進(jìn)行響應(yīng)的操作

    public <T extends Serializable> void setData(T object, boolean isStuff) {
      if (isStuff) {
        setSerialData(object);
      } else {
        setBytesData(object);
      }
    }
    

跨語言Python中反序列化

創(chuàng)建proto文件
  • MessageDto.proto

    syntax = "proto3";
    
    message MessageDto {
        string action=1;
        int32 state=2;
        bytes data=3;
    }
    
  • RpcCmd.proto

    syntax = "proto3";
    import "MessageDto.proto";
    
    message RpcCmd {
        MessageDto message = 1;
        string randomKey = 2;
        string remoteAddressKey = 3;
    }
    
  • Point2PointMessage.proto

    syntax = "proto3";
    
    message Point2PointMessage {
        bytes java_class = 127;
        string targetAddressKey = 1;
        string message = 2;
    }
    
  • BytesData.proto

    syntax = "proto3";
    message BytesData {
        bytes content=1;
    }
    
導(dǎo)出Python3對(duì)象定義文件
  • 此時(shí)沒有用到Any類型 可以直接導(dǎo)出為Python3的py文件
  • protoc --python3_out=./gen RpcCmd.proto MessageDto.proto Point2PointMessage.proto BytesData.proto
  • 和pb2的區(qū)別是序列化和反序列化的方法名稱進(jìn)行了修改
    • pb2中用的是ParseFromStringSerializeToString
    • pb3中修改成了encode_to_bytesparse_from_bytes
在Python中使用
  • 同樣的放到喜歡的包下袱瓮,修改對(duì)應(yīng)包名 這里不贅述

    #!/usr/bin/python3
    # -*-coding: utf-8 -*-
    from com.tony.proto.py3 import RpcCmd, Point2PointMessage, MessageDto, BytesData
    
    
    def serialize_to_file(file_path):
        p2p_msg = Point2PointMessage.Point2PointMessage()
        p2p_msg.message = "Hello, p2p from python"
        p2p_msg.targetAddressKey = "/127.0.0.1:38211"
    
        # bytes_data = BytesData.BytesData()
        # bytes_data.content = "bytes data from python"
    
        rpc_cmd = RpcCmd.RpcCmd()
        rpc_cmd.randomKey = "random-key-key-random"
        rpc_cmd.remoteAddressKey = "/127.0.0.1:1234"
        rpc_cmd.message.action = "p2p"
        rpc_cmd.message.state = 100
        rpc_cmd.message.data = p2p_msg.encode_to_bytes()
        # rpc_cmd.message.data = bytes_data.encode_to_bytes()
        bytes_write = rpc_cmd.encode_to_bytes()
    
        fw = open(file_path, mode="wb")
        fw.write(bytes_write)
        fw.flush()
        fw.close()
        print("write bytes to file:", bytes_write)
    
    
    def deserialize_from_file(file_path):
        fo = open(file_path, mode="rb")
        bytes_read = fo.read()
        fo.close()
        print("read bytes from file:", bytes_read)
        rpc_cmd = RpcCmd.RpcCmd()
        rpc_cmd.parse_from_bytes(bytes_read)
        print(rpc_cmd)
    
        msg_bytes = rpc_cmd.message.data
        print("message bytes", msg_bytes)
        p2p_msg = data_of(rpc_cmd.message, Point2PointMessage.Point2PointMessage)
        # bytes_data = data_of(rpc_cmd.message, BytesData.BytesData)
        print("msg_content:", p2p_msg.message)
        print("msg_target:", p2p_msg.targetAddressKey)
        # print("bytes_data:", bytes_data.content)
    
    
    def data_of(message: RpcCmd.MessageDto, message_identify):
        content = message_identify()
        content.parse_from_bytes(message.data)
        return content
    
    
    if __name__ == "__main__":
        serialize_file_path = "./trans-data.dat"
        serialize_to_file(serialize_file_path)
        deserialize_from_file(serialize_file_path)
    
    
  • 執(zhí)行結(jié)果如下缤骨,同樣的可以將bytes_data相關(guān)的注釋取消同時(shí)注釋掉p2p_msg相關(guān)的測(cè)試BytesData類型的序列化和反序列化

    write bytes to file: b'\n3\n\x03p2p\x10d\x1a*\n\x10/127.0.0.1:38211\x12\x16Hello, p2p from python\x12\x15random-key-key-random\x1a\x0f/127.0.0.1:1234'
    read bytes from file: b'\n3\n\x03p2p\x10d\x1a*\n\x10/127.0.0.1:38211\x12\x16Hello, p2p from python\x12\x15random-key-key-random\x1a\x0f/127.0.0.1:1234'
    <Message(RpcCmd)>
        <MessageField(id=1, optional)>:
            <Message(MessageDto)>            <StringField(id=1, optional)>:                p2p            <Int32Field(id=2, optional)>:                100            <BytesField(id=3, optional)>:                b'\n\x10/127.0.0.1:38211\x12\x16Hello, p2p from python'
        <StringField(id=2, optional)>:
            random-key-key-random
        <StringField(id=3, optional)>:
            /127.0.0.1:1234
    message bytes b'\n\x10/127.0.0.1:38211\x12\x16Hello, p2p from python'
    msg_content: Hello, p2p from python
    msg_target: /127.0.0.1:38211
    
Python和Java互轉(zhuǎn)
  • 同樣是僅僅修改序列化文件地址即可
Python反序列化Java
  • java_serialize_file_path = $path_to_java_serialized$
    deserialize_from_file(java_serialize_file_path)
    
  • 執(zhí)行結(jié)果

    read bytes from file: b'\n-\n\x03p2p\x10d\x1a$\n\x0f/127.0.0.1:1233\x12\x11message from java\x12\x0fRANDOM_KEY_JAVA'
    <Message(RpcCmd)>
        <MessageField(id=1, optional)>:
            <Message(MessageDto)>            <StringField(id=1, optional)>:                p2p            <Int32Field(id=2, optional)>:                100            <BytesField(id=3, optional)>:                b'\n\x0f/127.0.0.1:1233\x12\x11message from java'
        <StringField(id=2, optional)>:
            RANDOM_KEY_JAVA
    message bytes b'\n\x0f/127.0.0.1:1233\x12\x11message from java'
    msg_content: message from java
    msg_target: /127.0.0.1:1233
    
    
Java反序列化Python
  • @Test
    public void deserializeFromPythonFile() throws Exception {
      RpcCmd rpcCmd = ProtobufSerializer.getInstance()
        .deSerialize(new FileInputStream($python_serialize_path$), RpcCmd.class);
      log.info("deserialize cmd:\n{}", rpcCmd);
      log.info("deserialize p2p msg:\n{}", rpcCmd.getMessage().dataOfClazz(Point2PointMessage.class, false));
    }
    
  • 執(zhí)行結(jié)果

    13:15:17.821 [main] INFO com.tony.simple.JavaProtostuffSerializeDemo - deserialize cmd:
    RpcCmd(message=MessageDto(action=p2p, state=100, bytesData=[10, 16, 47, 49, 50, 55, 46, 48, 46, 48, 46, 49, 58, 51, 56, 50, 49, 49, 18, 22, 72, 101, 108, 108, 111, 44, 32, 112, 50, 112, 32, 102, 114, 111, 109, 32, 112, 121, 116, 104, 111, 110], serialData=null), randomKey=random-key-key-random, remoteAddressKey=null)
    13:15:17.828 [main] INFO com.tony.simple.JavaProtostuffSerializeDemo - deserialize p2p msg:
    Point2PointMessage(targetAddressKey=/127.0.0.1:38211, message=Hello, p2p from python)
    

io.protostuff使用總結(jié)

  • 在java平臺(tái)可以直接定義普通的POJO而不需要手寫proto文件并生成對(duì)應(yīng)的對(duì)象文件,僅僅通過其所提供的ProtobufIOUtil或者ProtostuffIOUtil來實(shí)現(xiàn)序列化和反序列化即可尺借。
  • 當(dāng)需要進(jìn)行跨語言序列化和反序列化時(shí)绊起,需要其他語言中編寫對(duì)應(yīng)的proto文件并生成對(duì)象文件,而在Java中的泛型實(shí)例變量則需要進(jìn)行修改燎斩,改成二次序列化的byte數(shù)組虱歪,方便在Python等語言中進(jìn)行解析蜂绎。Java中的序列化也應(yīng)采用ProtobufIOUtil來實(shí)現(xiàn)。此時(shí)笋鄙,Python中可以根據(jù)業(yè)務(wù)類型反序列化成指定的對(duì)象荡碾,Java中也以該對(duì)象來序列化,反過來也是一樣的操作局装。以此來達(dá)到的目的是定義MessageDto之后如果需要擴(kuò)展坛吁,不需要修改MessageDto,僅僅需要定義更多的data類型然后賦值給MessageDto.$data铐尚。
  • 對(duì)比純protobuf實(shí)現(xiàn)的來說拨脉,在編碼上更加簡單,不需要寫大量的Any.pack()和Any.unpack()

原文發(fā)布在我的github blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宣增,一起剝皮案震驚了整個(gè)濱河市玫膀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌爹脾,老刑警劉巖帖旨,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異灵妨,居然都是意外死亡解阅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門泌霍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來货抄,“玉大人,你說我怎么就攤上這事朱转⌒返兀” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵藤为,是天一觀的道長怪与。 經(jīng)常有香客問我,道長缅疟,這世上最難降的妖魔是什么分别? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮窿吩,結(jié)果婚禮上茎杂,老公的妹妹穿的比我還像新娘。我一直安慰自己纫雁,他們只是感情好煌往,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般刽脖。 火紅的嫁衣襯著肌膚如雪羞海。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天曲管,我揣著相機(jī)與錄音却邓,去河邊找鬼。 笑死院水,一個(gè)胖子當(dāng)著我的面吹牛腊徙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播檬某,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼撬腾,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了恢恼?” 一聲冷哼從身側(cè)響起民傻,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎场斑,沒想到半個(gè)月后漓踢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漏隐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年喧半,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锁保。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡薯酝,死狀恐怖半沽,靈堂內(nèi)的尸體忽然破棺而出爽柒,到底是詐尸還是另有隱情,我是刑警寧澤者填,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布浩村,位于F島的核電站,受9級(jí)特大地震影響占哟,放射性物質(zhì)發(fā)生泄漏心墅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一榨乎、第九天 我趴在偏房一處隱蔽的房頂上張望怎燥。 院中可真熱鬧,春花似錦蜜暑、人聲如沸铐姚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隐绵。三九已至之众,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間依许,已是汗流浹背棺禾。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留峭跳,地道東北人膘婶。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蛀醉,于是被迫代替她去往敵國和親竣付。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353