zoukankan      html  css  js  c++  java
  • Protobuf2使用

    背景

    博主最近在研究sofa-jraft的时候,看到jraft使用的protobuf,所以单独拎出来单独理解一下。

    Protobuf语法

    https://www.cnblogs.com/resentment/p/6539021.html

     

    使用案例

    1 添加proto文件

    syntax="proto2";
    
    package jraft;
    
    import "enum.proto";
    
    option java_package="com.alipay.sofa.jraft.entity1";
    option java_outer_classname = "RaftOutter1";
    
    
    message EntryMeta {
        required int64 term = 1;
        required EntryType type = 2;
        repeated string peers = 3;
        optional int64 data_len = 4;
        // Don't change field id of `old_peers' in the consideration of backward
        // compatibility
        repeated string old_peers = 5;
        // Checksum fot this log entry, since 1.2.6, added by boyan@antfin.com
        optional int64 checksum = 6;
        repeated string learners = 7;
        repeated string old_learners = 8;
    };
    
    message SnapshotMeta {
        required int64 last_included_index = 1;
        required int64 last_included_term = 2;
        repeated string peers = 3;
        repeated string old_peers = 4;
        repeated string learners = 5;
        repeated string old_learners = 6;
    }

    2 下载protoc.exe   执行:

    protoc ./raft.proto --java_out=../java/

    会看到在entity1目录下生成

    3 测试

    package com.alipay.sofa.jraft.entity1;
    
    import com.alipay.sofa.jraft.entity.EnumOutter;
    import com.google.protobuf.InvalidProtocolBufferException;
    
    import java.util.Arrays;
    
    public class PB2Byte {
        public static void main(String[] args) throws InvalidProtocolBufferException {
            RaftOutter1.EntryMeta.Builder builder = RaftOutter1.EntryMeta.newBuilder();
            // ====================================赋值================================
            builder.setType(EnumOutter.EntryType.ENTRY_TYPE_UNKNOWN);
            builder.setChecksum(4354734L);
            builder.setTerm(1);
            // builder.setPeers(0, "1");
            builder.addPeers("gdghfhf");
            builder.addOldPeers("gdghfhf1");
    
            builder.addLearners("7");
            builder.addOldLearners("8");
            // ====================================build对象================================
            RaftOutter1.EntryMeta entryMeta = builder.build();
            // ====================================对象序列化================================
            byte[] byteArray = entryMeta.toByteArray();
            System.out.println(Arrays.toString(byteArray));
    
            // ====================================反序列化================================
            RaftOutter1.EntryMeta newEntryMeta = RaftOutter1.EntryMeta.parseFrom(byteArray);
            System.out.println("newEntryMeta:" + newEntryMeta.toString());
        }
    }

    4 结果

    FileDescriptorSet的使用

    获取多个proto的FD描述符,需要使用descriptor文件,其生成的指令为  protoc --descriptor_set_out=raft.desc 

    定义一个ProtobufMsgFactory,该类的目的就是为了缓存classname 与  类对象的解析方法之间的映射关系,帮助快速序列化。

    package com.alipay.sofa.jraft.rpc;
    
    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.commons.lang.SerializationException;
    
    import com.alipay.sofa.jraft.error.MessageClassNotFoundException;
    import com.alipay.sofa.jraft.storage.io.ProtoBufFile;
    import com.alipay.sofa.jraft.util.RpcFactoryHelper;
    import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
    import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
    import com.google.protobuf.Descriptors.Descriptor;
    import com.google.protobuf.Descriptors.FileDescriptor;
    import com.google.protobuf.Message;
    
    import static java.lang.invoke.MethodType.methodType;
    
    /**
     * Protobuf message factory.
     */
    public class ProtobufMsgFactory {
    
        private static Map<String/* class name in proto file */, MethodHandle> PARSE_METHODS_4PROTO        = new HashMap<>();
        private static Map<String/* class name in java file */, MethodHandle>  PARSE_METHODS_4J            = new HashMap<>();
        private static Map<String/* class name in java file */, MethodHandle>  DEFAULT_INSTANCE_METHODS_4J = new HashMap<>();
    
        /**
         * 通过protoc --descriptor_set_out=raft.desc  来解析到所有的proto文件
         * 每一个proto文件遍历所有的messageType
         * 获取到messageType的parseFrom方法
         * 保存类名与parseFrom方法的映射关系
         */
        static {
            try {
                // raft.desc文件通过命令提前生成
                final FileDescriptorSet descriptorSet = FileDescriptorSet.parseFrom(ProtoBufFile.class
                    .getResourceAsStream("/raft.desc"));
                final List<FileDescriptor> resolveFDs = new ArrayList<>();
                final RaftRpcFactory rpcFactory = RpcFactoryHelper.rpcFactory();
                for (final FileDescriptorProto fdp : descriptorSet.getFileList()) {
    
                    final FileDescriptor[] dependencies = new FileDescriptor[resolveFDs.size()];
                    resolveFDs.toArray(dependencies);
    
                    final FileDescriptor fd = FileDescriptor.buildFrom(fdp, dependencies);
                    resolveFDs.add(fd);
                    // getMessageTypes  表示定义在proto文件中的类型名称
                    for (final Descriptor descriptor : fd.getMessageTypes()) {
    
                        final String className = fdp.getOptions().getJavaPackage() + "."
                                                 + fdp.getOptions().getJavaOuterClassname() + "$" + descriptor.getName();
                        final Class<?> clazz = Class.forName(className);
                        // 获取到MethodHandle
                        final MethodHandle parseFromHandler = MethodHandles.lookup().findStatic(clazz, "parseFrom",
                            methodType(clazz, byte[].class));  // clazz为返回值类型    byte[].class 为参数类型
                        final MethodHandle getInstanceHandler = MethodHandles.lookup().findStatic(clazz,
                            "getDefaultInstance", methodType(clazz));
                        // FullName = jraft.SnapshotMeta
                        PARSE_METHODS_4PROTO.put(descriptor.getFullName(), parseFromHandler);
                        PARSE_METHODS_4J.put(className, parseFromHandler);
                        DEFAULT_INSTANCE_METHODS_4J.put(className, getInstanceHandler);
                        rpcFactory.registerProtobufSerializer(className, getInstanceHandler.invoke());
                    }
    
                }
            } catch (final Throwable t) {
                t.printStackTrace(); // NOPMD
            }
        }
    
        public static void load() {
            if (PARSE_METHODS_4J.isEmpty() || PARSE_METHODS_4PROTO.isEmpty() || DEFAULT_INSTANCE_METHODS_4J.isEmpty()) {
                throw new IllegalStateException("Parse protocol file failed.");
            }
        }
    
        @SuppressWarnings("unchecked")
        public static <T extends Message> T getDefaultInstance(final String className) {
            final MethodHandle handle = DEFAULT_INSTANCE_METHODS_4J.get(className);
            if (handle == null) {
                throw new MessageClassNotFoundException(className + " not found");
            }
            try {
                return (T) handle.invoke();
            } catch (Throwable t) {
                throw new SerializationException(t);
            }
        }
    
        @SuppressWarnings("unchecked")
        public static <T extends Message> T newMessageByJavaClassName(final String className, final byte[] bs) {
            final MethodHandle handle = PARSE_METHODS_4J.get(className);
            if (handle == null) {
                throw new MessageClassNotFoundException(className + " not found");
            }
            try {
                return (T) handle.invoke(bs);
            } catch (Throwable t) {
                throw new SerializationException(t);
            }
        }
    
        @SuppressWarnings("unchecked")
        public static <T extends Message> T newMessageByProtoClassName(final String className, final byte[] bs) {
            final MethodHandle handle = PARSE_METHODS_4PROTO.get(className);
            if (handle == null) {
                throw new MessageClassNotFoundException(className + " not found");
            }
            try {
                return (T) handle.invoke(bs);
            } catch (Throwable t) {
                throw new SerializationException(t);
            }
        }
    
        public static void main(String[] args) {
            new ProtobufMsgFactory();
        }
    }
  • 相关阅读:
    字符串(url)拼接变量
    elementUI table数据显示效果(二)
    异常(转)
    PHP 的异常处理、错误的抛出及错误回调函数 (转)
    详细解读PHP类的封装 (转)
    什么是抽象类
    什么是类,什么是对象,类和对象之间的关系
    魔术方法
    类的声名
    self
  • 原文地址:https://www.cnblogs.com/gaojy/p/15207352.html
Copyright © 2011-2022 走看看