调用一个gRPC服务需要客户端持有服务定义的proto文件, 通过proto文件生成本地调用的stub. 如何在没有proto定义的情况下也能调用服务端提供的服务呢. gRPC 提供了 grpc.reflection.v1alpha.ServerReflection 服务,在 Server 端添加后可以通过该服务获取所有服务的信息,包括服务定义,方法,属性等; 根据获取到的服务信息实现泛化调用

  1. // Copyright 2016 The gRPC Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Service exported by server reflection
  15. // Warning: this entire file is deprecated. Use this instead:
  16. // https://github.com/grpc/grpc-proto/blob/master/grpc/reflection/v1/reflection.proto
  17. syntax = "proto3";
  18. package grpc.reflection.v1alpha;
  19. option deprecated = true;
  20. option java_multiple_files = true;
  21. option java_package = "io.grpc.reflection.v1alpha";
  22. option java_outer_classname = "ServerReflectionProto";
  23. service ServerReflection {
  24. // The reflection service is structured as a bidirectional stream, ensuring
  25. // all related requests go to a single server.
  26. rpc ServerReflectionInfo(stream ServerReflectionRequest)
  27. returns (stream ServerReflectionResponse);
  28. }
  29. // The message sent by the client when calling ServerReflectionInfo method.
  30. message ServerReflectionRequest {
  31. string host = 1;
  32. // To use reflection service, the client should set one of the following
  33. // fields in message_request. The server distinguishes requests by their
  34. // defined field and then handles them using corresponding methods.
  35. oneof message_request {
  36. // Find a proto file by the file name.
  37. string file_by_filename = 3;
  38. // Find the proto file that declares the given fully-qualified symbol name.
  39. // This field should be a fully-qualified symbol name
  40. // (e.g. <package>.<service>[.<method>] or <package>.<type>).
  41. string file_containing_symbol = 4;
  42. // Find the proto file which defines an extension extending the given
  43. // message type with the given field number.
  44. ExtensionRequest file_containing_extension = 5;
  45. // Finds the tag numbers used by all known extensions of extendee_type, and
  46. // appends them to ExtensionNumberResponse in an undefined order.
  47. // Its corresponding method is best-effort: it's not guaranteed that the
  48. // reflection service will implement this method, and it's not guaranteed
  49. // that this method will provide all extensions. Returns
  50. // StatusCode::UNIMPLEMENTED if it's not implemented.
  51. // This field should be a fully-qualified type name. The format is
  52. // <package>.<type>
  53. string all_extension_numbers_of_type = 6;
  54. // List the full names of registered services. The content will not be
  55. // checked.
  56. string list_services = 7;
  57. }
  58. }
  59. // The type name and extension number sent by the client when requesting
  60. // file_containing_extension.
  61. message ExtensionRequest {
  62. // Fully-qualified type name. The format should be <package>.<type>
  63. string containing_type = 1;
  64. int32 extension_number = 2;
  65. }
  66. // The message sent by the server to answer ServerReflectionInfo method.
  67. message ServerReflectionResponse {
  68. string valid_host = 1;
  69. ServerReflectionRequest original_request = 2;
  70. // The server set one of the following fields accroding to the message_request
  71. // in the request.
  72. oneof message_response {
  73. // This message is used to answer file_by_filename, file_containing_symbol,
  74. // file_containing_extension requests with transitive dependencies. As
  75. // the repeated label is not allowed in oneof fields, we use a
  76. // FileDescriptorResponse message to encapsulate the repeated fields.
  77. // The reflection service is allowed to avoid sending FileDescriptorProtos
  78. // that were previously sent in response to earlier requests in the stream.
  79. FileDescriptorResponse file_descriptor_response = 4;
  80. // This message is used to answer all_extension_numbers_of_type requst.
  81. ExtensionNumberResponse all_extension_numbers_response = 5;
  82. // This message is used to answer list_services request.
  83. ListServiceResponse list_services_response = 6;
  84. // This message is used when an error occurs.
  85. ErrorResponse error_response = 7;
  86. }
  87. }
  88. // Serialized FileDescriptorProto messages sent by the server answering
  89. // a file_by_filename, file_containing_symbol, or file_containing_extension
  90. // request.
  91. message FileDescriptorResponse {
  92. // Serialized FileDescriptorProto messages. We avoid taking a dependency on
  93. // descriptor.proto, which uses proto2 only features, by making them opaque
  94. // bytes instead.
  95. repeated bytes file_descriptor_proto = 1;
  96. }
  97. // A list of extension numbers sent by the server answering
  98. // all_extension_numbers_of_type request.
  99. message ExtensionNumberResponse {
  100. // Full name of the base type, including the package name. The format
  101. // is <package>.<type>
  102. string base_type_name = 1;
  103. repeated int32 extension_number = 2;
  104. }
  105. // A list of ServiceResponse sent by the server answering list_services request.
  106. message ListServiceResponse {
  107. // The information of each service may be expanded in the future, so we use
  108. // ServiceResponse message to encapsulate it.
  109. repeated ServiceResponse service = 1;
  110. }
  111. // The information of a single service used by ListServiceResponse to answer
  112. // list_services request.
  113. message ServiceResponse {
  114. // Full name of a registered service, including its package name. The format
  115. // is <package>.<service>
  116. string name = 1;
  117. }
  118. // The error code and error message sent by the server when an error occurs.
  119. message ErrorResponse {
  120. // This field uses the error codes defined in grpc::StatusCode.
  121. int32 error_code = 1;
  122. string error_message = 2;
  123. }

该服务只有一个双向流的方法 ServerReflectionInfo,调用时根据请求参数不同,调用不同的方法进行处理,并返回响应;该方法的流控是非自动的,只有当一个请求完成之后才会获取下一个请求

使用方式

添加maven依赖:

...
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-services</artifactId>
  <version>${VERSION}</version>
</dependency>
...

Server端:

public static void main(String[] args) {
    // 构建 Server
    int port = 50051;
    server = ServerBuilder.forPort(port)
        .addService(new GreeterImpl())
        // 添加反射服务
        .addService(ProtoReflectionService.newInstance())
        .build()
        .start();
    logger.info("Server started, listening on " + port);
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            // Use stderr here since the logger may have been reset by its JVM shutdown hook.
            System.err.println("*** shutting down gRPC server since JVM is shutting down");
            try {
                HelloWorldServer.this.stop();
            } catch (InterruptedException e) {
                e.printStackTrace(System.err);
            }
            System.err.println("*** server shut down");
        }
    });
    server.awaitTermination();
}

通过反射服务动态调用

凡事涉及到反射, 处理过程都是很复杂的, 以java为例, 处理流程如下:

  1. 使用完整的服务名, 通过反射服务获取服务的proto定义, java中以 DescriptorProtos.FileDescriptorSet 对象描述
  2. 通过获取到的 DescriptorProtos.FileDescriptorSet 解析出方法描述, java中以 Descriptors.MethodDescriptor 对象描述
  3. 根据方法描述, 创建动态message builder, DynamicMessage.newBuilder(methodDescriptor.getInputType())
  4. 使用 JsonFormat.parser().merge(“json对象”, messageBuilder), 将json格式的参数转换成protobuf message对象
  5. 根据方法的类型 (Unary, Server streaming, Client streaming, Bidirectional streaming), 调用不同的 ClientCalls方法(同步/异步)
    1. ClientCalls.asyncUnaryCall
    2. ClientCalls.asyncServerStreamingCall
    3. ClientCalls.asyncClientStreamingCall
    4. ClientCalls.asyncBidiStreamingCall
    5. ClientCalls.blockingUnaryCall
    6. ClientCalls.blockingServerStreamingCall
  6. 通过JsonFormat.printer().print(response), 输出为json

完全示例代码如下:


import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.protobuf.*;
import com.google.protobuf.util.JsonFormat;
import io.grpc.*;
import io.grpc.reflection.v1alpha.ServerReflectionGrpc;
import io.grpc.reflection.v1alpha.ServerReflectionRequest;
import io.grpc.reflection.v1alpha.ServerReflectionResponse;
import io.grpc.reflection.v1alpha.ServiceResponse;
import io.grpc.stub.StreamObserver;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import static io.grpc.MethodDescriptor.generateFullMethodName;
import static io.grpc.stub.ClientCalls.*;

public class HelloWorldReflectionClient {
    private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());

    private final ServerReflectionGrpc.ServerReflectionStub stub;

    public HelloWorldReflectionClient(Channel channel) {
        this.stub = ServerReflectionGrpc.newStub(channel);
    }

    /**
     * 获取服务列表
     */
    public void listServices() {
        StreamObserver<ServerReflectionRequest> requestStreamObserver = stub.serverReflectionInfo(new StreamObserver<ServerReflectionResponse>() {

            @Override
            public void onNext(ServerReflectionResponse value) {
                for (ServiceResponse serviceResponse : value.getListServicesResponse().getServiceList()) {
                    logger.log(Level.INFO, "reflection service: {0}", serviceResponse.getName());
                }
            }

            @Override
            public void onError(Throwable t) {

            }

            @Override
            public void onCompleted() {

            }
        });
        ServerReflectionRequest request = ServerReflectionRequest.newBuilder()
                .setListServices("")
                .build();
        requestStreamObserver.onNext(request);
        requestStreamObserver.onCompleted();
    }

    /**
     * 反射调用
     * @param serviceName 完整服务名
     * @param methodName 方法名
     * @param name
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public void reflectionCall(String serviceName, String methodName, String name) throws ExecutionException, InterruptedException {
        LookupServiceHandler                    rpcHandler            = new LookupServiceHandler(serviceName);
        StreamObserver<ServerReflectionRequest> requestStreamObserver = stub.serverReflectionInfo(rpcHandler);
        // 获取proto文件描述
        DescriptorProtos.FileDescriptorSet      fileDescriptorSet     = rpcHandler.start(requestStreamObserver).get();
        if (fileDescriptorSet == null) {
            logger.log(Level.WARNING, "Service: " + serviceName + " not found.");
            return;
        }
        ServiceResolver serviceResolver = ServiceResolver.fromFileDescriptorSet(fileDescriptorSet);
        // 通过方法名, 获取方法描述
        Descriptors.MethodDescriptor methodDescriptor = serviceResolver.resolveServiceMethod(serviceName, methodName);
        // 构建动态消息对象
        DynamicMessage.Builder       messageBuilder   = DynamicMessage.newBuilder(methodDescriptor.getInputType());
        Message message = null;
        try {
            // 通过json转换
            serviceResolver.getParser().merge("{\"name\": \""+name+"\"}", messageBuilder);
            message = messageBuilder.build();
        } catch (InvalidProtocolBufferException e) {
            logger.log(Level.WARNING, "invalid protocol buffer, ", e);
            return;
        }
        JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(serviceResolver.getRegistry());
        MethodDescriptor.MethodType methodType = fetchMethodType(methodDescriptor);
        StreamObserver<Message> responseStreamObserver = new StreamObserver<Message>() {
            @Override
            public void onNext(Message value) {
                try {
                    logger.info("Reflection call response: " + printer.print(value));
                } catch (InvalidProtocolBufferException e) {
                }
            }

            @Override
            public void onError(Throwable t) {

            }

            @Override
            public void onCompleted() {

            }
        };

        // 根据不同的方法类型, 进行调用
        switch (methodType) {
            case UNARY:
                asyncUnaryCall(stub.getChannel().newCall(createGrpcMethodDescriptor(methodDescriptor)
                        , CallOptions.DEFAULT), message, responseStreamObserver);
                break;
            case SERVER_STREAMING:
                asyncServerStreamingCall(stub.getChannel().newCall(createGrpcMethodDescriptor(methodDescriptor)
                        , CallOptions.DEFAULT), message, responseStreamObserver);
                break;
            case CLIENT_STREAMING:
                StreamObserver<Message> reqStreamObserver = asyncClientStreamingCall(stub.getChannel().newCall(createGrpcMethodDescriptor(methodDescriptor)
                        , CallOptions.DEFAULT), responseStreamObserver);
                reqStreamObserver.onNext(message);
                reqStreamObserver.onCompleted();
            case BIDI_STREAMING:
                StreamObserver<Message> biReqStreamObserver = asyncBidiStreamingCall(stub.getChannel().newCall(createGrpcMethodDescriptor(methodDescriptor)
                        , CallOptions.DEFAULT), responseStreamObserver);
                biReqStreamObserver.onNext(message);
                biReqStreamObserver.onCompleted();
                break;
            default:
                logger.info("Unknown methodType: " + methodType);
        }

    }

    private io.grpc.MethodDescriptor<Message, Message> createGrpcMethodDescriptor(final Descriptors.MethodDescriptor descriptor) {
        return io.grpc.MethodDescriptor.<Message, Message>newBuilder()
                .setType(fetchMethodType(descriptor))
                .setFullMethodName(fetchFullMethodName(descriptor))
                .setRequestMarshaller(new DynamicMessageMarshaller(descriptor.getInputType()))
                .setResponseMarshaller(new DynamicMessageMarshaller(descriptor.getOutputType()))
                .build();
    }

    public String fetchFullMethodName(final Descriptors.MethodDescriptor methodDescriptor) {
        String serviceName = methodDescriptor.getService().getFullName();
        String methodName  = methodDescriptor.getName();
        return generateFullMethodName(serviceName, methodName);
    }

    public MethodDescriptor.MethodType fetchMethodType(final Descriptors.MethodDescriptor methodDescriptor) {
        boolean clientStreaming = methodDescriptor.toProto().getClientStreaming();
        boolean serverStreaming = methodDescriptor.toProto().getServerStreaming();
        if (clientStreaming && serverStreaming) {
            return MethodDescriptor.MethodType.BIDI_STREAMING;
        } else if (!clientStreaming && !serverStreaming) {
            return MethodDescriptor.MethodType.UNARY;
        } else if (!clientStreaming) {
            return MethodDescriptor.MethodType.SERVER_STREAMING;
        } else {
            return MethodDescriptor.MethodType.SERVER_STREAMING;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        String target = "localhost:50051";
        ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
                .usePlaintext()
                .build();
        try {
            HelloWorldReflectionClient client = new HelloWorldReflectionClient(channel);
            client.listServices();
            client.reflectionCall("helloworld.Greeter", "SayHello", "test reflection call");
            // 等待异步执行完成
            Thread.sleep(5000);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } finally {
            // ManagedChannels use resources like threads and TCP connections. To prevent leaking these
            // resources the channel should be shut down when it will no longer be used. If it may be used
            // again leave it running.
            channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
        }
    }

    public static class LookupServiceHandler implements StreamObserver<ServerReflectionResponse> {

        private final String serviceName;

        private final Set<String> requestedDescriptors;

        private final SettableFuture<DescriptorProtos.FileDescriptorSet> resultFuture;

        private final Map<String, DescriptorProtos.FileDescriptorProto> resolvedDescriptors;

        private StreamObserver<ServerReflectionRequest> requestStream;

        private int outstandingRequests;

        public LookupServiceHandler(final String serviceName) {
            this.serviceName = serviceName;
            this.resultFuture = SettableFuture.create();
            this.resolvedDescriptors = new HashMap<>();
            this.requestedDescriptors = new HashSet<>();
            this.outstandingRequests = 0;
        }

        /**
         * Start the handler.
         *
         * @param requestStream stream
         * @return ListenableFuture future
         */
        public ListenableFuture<DescriptorProtos.FileDescriptorSet> start(final StreamObserver<ServerReflectionRequest> requestStream) {
            this.requestStream = requestStream;
            requestStream.onNext(requestForSymbol(serviceName));
            ++outstandingRequests;
            return resultFuture;
        }

        @Override
        public void onNext(final ServerReflectionResponse response) {
            ServerReflectionResponse.MessageResponseCase responseCase = response.getMessageResponseCase();
            if (responseCase == ServerReflectionResponse.MessageResponseCase.FILE_DESCRIPTOR_RESPONSE) {
                ImmutableSet<DescriptorProtos.FileDescriptorProto> descriptors =
                        parseDescriptors(response.getFileDescriptorResponse().getFileDescriptorProtoList());
                descriptors.forEach(d -> resolvedDescriptors.put(d.getName(), d));
                descriptors.forEach(this::processDependencies);
            }
        }

        @Override
        public void onError(final Throwable t) {
            resultFuture.setException(new RuntimeException("Reflection lookup rpc failed for: " + serviceName, t));
        }

        @Override
        public void onCompleted() {
            if (!resultFuture.isDone()) {
                resultFuture.setException(new RuntimeException("Unexpected end of rpc"));
            }
        }

        private ImmutableSet<DescriptorProtos.FileDescriptorProto> parseDescriptors(final List<ByteString> descriptorBytes) {
            ImmutableSet.Builder<DescriptorProtos.FileDescriptorProto> resultBuilder = ImmutableSet.builder();
            for (ByteString fileDescriptorBytes : descriptorBytes) {
                try {
                    resultBuilder.add(DescriptorProtos.FileDescriptorProto.parseFrom(fileDescriptorBytes));
                } catch (InvalidProtocolBufferException e) {
                    throw new RuntimeException(e);
                }
            }
            return resultBuilder.build();
        }

        private void processDependencies(final DescriptorProtos.FileDescriptorProto fileDescriptor) {
            fileDescriptor.getDependencyList().forEach(dep -> {
                if (!resolvedDescriptors.containsKey(dep) && !requestedDescriptors.contains(dep)) {
                    requestedDescriptors.add(dep);
                    ++outstandingRequests;
                    requestStream.onNext(requestForDescriptor(dep));
                }
            });

            --outstandingRequests;
            if (outstandingRequests == 0) {
                resultFuture.set(DescriptorProtos.FileDescriptorSet.newBuilder()
                        .addAllFile(resolvedDescriptors.values())
                        .build());
                requestStream.onCompleted();
            }
        }

        private static ServerReflectionRequest requestForDescriptor(final String name) {
            return ServerReflectionRequest.newBuilder()
                    .setFileByFilename(name)
                    .build();
        }

        private static ServerReflectionRequest requestForSymbol(final String symbol) {
            return ServerReflectionRequest.newBuilder()
                    .setFileContainingSymbol(symbol)
                    .build();
        }
    }

    public static class ServiceResolver {

        private final ImmutableList<Descriptors.FileDescriptor> fileDescriptors;
        private final JsonFormat.TypeRegistry                   registry;
        private final JsonFormat.Parser                         parser;

        private ServiceResolver(final Iterable<Descriptors.FileDescriptor> fileDescriptors) {
            this.fileDescriptors = ImmutableList.copyOf(fileDescriptors);
            this.registry = JsonFormat.TypeRegistry.newBuilder().add(listMessageTypes()).build();
            this.parser = JsonFormat.parser().usingTypeRegistry(registry).ignoringUnknownFields();
        }

        /**
         * Creates a resolver.
         *
         * @param descriptorSet descriptorSet
         * @return ServiceResolver serviceResolver
         */
        public static ServiceResolver fromFileDescriptorSet(final DescriptorProtos.FileDescriptorSet descriptorSet) {
            ImmutableMap<String, DescriptorProtos.FileDescriptorProto> descriptorProtoIndex =
                    computeDescriptorProtoIndex(descriptorSet);
            Map<String, Descriptors.FileDescriptor> descriptorCache = new HashMap<>(8);

            ImmutableList.Builder<Descriptors.FileDescriptor> result = ImmutableList.builder();
            for (DescriptorProtos.FileDescriptorProto descriptorProto : descriptorSet.getFileList()) {
                try {
                    result.add(descriptorFromProto(descriptorProto, descriptorProtoIndex, descriptorCache));
                } catch (Descriptors.DescriptorValidationException e) {
                    logger.log(Level.WARNING, "Skipped descriptor " + descriptorProto.getName() + " due to error", e);
                }
            }
            return new ServiceResolver(result.build());
        }

        /**
         * Lists all the known message types.
         *
         * @return ImmutableSet set
         */
        public ImmutableSet<Descriptors.Descriptor> listMessageTypes() {
            ImmutableSet.Builder<Descriptors.Descriptor> resultBuilder = ImmutableSet.builder();
            fileDescriptors.forEach(d -> resultBuilder.addAll(d.getMessageTypes()));
            return resultBuilder.build();
        }

        /**
         * Resolve service method.
         *
         * @return MethodDescriptor
         */
        public Descriptors.MethodDescriptor resolveServiceMethod(final String fullServiceName, final String methodName) {
            String                        serviceName = fullServiceName.substring(fullServiceName.lastIndexOf(".") + 1);
            String                        packageName = fullServiceName.substring(0, fullServiceName.lastIndexOf("."));
            Descriptors.ServiceDescriptor service     = findService(packageName, serviceName);
            Descriptors.MethodDescriptor  method      = service.findMethodByName(methodName);
            if (method == null) {
                throw new IllegalArgumentException("Unable to find method " + methodName + " in service " + serviceName);
            }
            return method;
        }

        public Descriptors.ServiceDescriptor findService(String fullServiceName) {
            String serviceName = fullServiceName.substring(fullServiceName.lastIndexOf(".") + 1);
            String packageName = fullServiceName.substring(0, fullServiceName.lastIndexOf("."));
            return findService(packageName, serviceName);
        }

        private Descriptors.ServiceDescriptor findService(final String packageName, final String serviceName) {
            for (Descriptors.FileDescriptor fileDescriptor : fileDescriptors) {
                if (!fileDescriptor.getPackage().equals(packageName)) {
                    continue;
                }
                Descriptors.ServiceDescriptor serviceDescriptor = fileDescriptor.findServiceByName(serviceName);
                if (serviceDescriptor != null) {
                    return serviceDescriptor;
                }
            }
            throw new IllegalArgumentException("Unable to find service with name: " + serviceName);
        }

        private static ImmutableMap<String, DescriptorProtos.FileDescriptorProto> computeDescriptorProtoIndex(final DescriptorProtos.FileDescriptorSet fileDescriptorSet) {
            ImmutableMap.Builder<String, DescriptorProtos.FileDescriptorProto> resultBuilder = ImmutableMap.builder();
            for (DescriptorProtos.FileDescriptorProto descriptorProto : fileDescriptorSet.getFileList()) {
                resultBuilder.put(descriptorProto.getName(), descriptorProto);
            }
            return resultBuilder.build();
        }

        private static Descriptors.FileDescriptor descriptorFromProto(
                final DescriptorProtos.FileDescriptorProto descriptorProto,
                final ImmutableMap<String, DescriptorProtos.FileDescriptorProto> descriptorProtoIndex,
                final Map<String, Descriptors.FileDescriptor> descriptorCache) throws Descriptors.DescriptorValidationException {
            // First check the cache.
            String descriptorName = descriptorProto.getName();
            if (descriptorCache.containsKey(descriptorName)) {
                return descriptorCache.get(descriptorName);
            }

            // Then fetch all the required dependencies recursively.
            ImmutableList.Builder<Descriptors.FileDescriptor> dependencies = ImmutableList.builder();
            for (String dependencyName : descriptorProto.getDependencyList()) {
                if (!descriptorProtoIndex.containsKey(dependencyName)) {
                    throw new IllegalArgumentException("Could not find dependency: " + dependencyName);
                }
                DescriptorProtos.FileDescriptorProto dependencyProto      = descriptorProtoIndex.get(dependencyName);
                Descriptors.FileDescriptor           dependencyDescriptor = descriptorFromProto(dependencyProto, descriptorProtoIndex, descriptorCache);
                dependencies.add(dependencyDescriptor);
            }
            // Finally construct the actual descriptor.
            Descriptors.FileDescriptor[] empty          = new Descriptors.FileDescriptor[0];
            Descriptors.FileDescriptor   fileDescriptor = Descriptors.FileDescriptor.buildFrom(descriptorProto, dependencies.build().toArray(empty));
            descriptorCache.put(descriptorName, fileDescriptor);
            return fileDescriptor;
        }

        public JsonFormat.TypeRegistry getRegistry() {
            return registry;
        }

        public JsonFormat.Parser getParser() {
            return parser;
        }
    }

    public static class DynamicMessageMarshaller implements MethodDescriptor.Marshaller<Message> {

        private final Descriptors.Descriptor messageDescriptor;

        public DynamicMessageMarshaller(final Descriptors.Descriptor messageDescriptor) {
            this.messageDescriptor = messageDescriptor;
        }

        @Override
        public Message parse(final InputStream inputStream) {
            try {
                return DynamicMessage.newBuilder(messageDescriptor)
                        .mergeFrom(inputStream, ExtensionRegistryLite.getEmptyRegistry())
                        .build();
            } catch (IOException e) {
                throw new RuntimeException("Unable to merge from the supplied input stream", e);
            }
        }

        @Override
        public InputStream stream(final Message abstractMessage) {
            return abstractMessage.toByteString().newInput();
        }
    }

}

基于反射服务的gRPC调试工具