gRPC请求构建和数据传输的功能性测试

时间:2025-04-09 17:20:59

gRPC请求构建和数据传输的功能性测试

测试结论

  • 在测试中,分为以下几种情景:
  1. 普通调用。通过编译生成的对象调用 gRPC 服务上的方法,并传入参数,然后调用 protocol buffer 文件里的方法,发送请求到 gRPC 服务器,返回结果。

  2. 动态地调用 gRPC 服务。在 gRPC 客户端和服务器之间进行 JSON 和 gRPC 协议的转换,使得使 gRPC 的应用程序能够接受 JSON 格式的请求,并将结果以 JSON 格式返回给调用者。

  • 结论:

1、请求构建时的测试。请求构建的过程测试只包括从传入参数到参数构建的再到发送请求时,对比两者的测试结果可知:【动态地调用 gRPC 服务】的构建比【普通调用】的构建效率高 3% 左右

2、请求传输时的测试。请求传输的过程测试不包括请求构建时的流程,只是将构建好的参数直接拿来调用,对比两者的测试可知:【普通调用】的传输效率比【动态地调用 gRPC 服务】的传输效率高 4.2% 左右

测试环境

  • 操作系统:Windows11(x64)

  • 处理器:12th Gen Intel(R) Core(TM) i5-12500H 2.50 GHz

  • 内存:16G

  • java环境:java_1.8

  • jvm执行参数:-Xmx2048m

  • 测试框架:jmh

测试方法

测试请求构建中的性能对比

  • 通过在4个方法中传入固定参数值,分别调用,对其执行效率进行测试
@State(Scope.Thread) //每个测试线程分配一个实例
public class TestServiceBenchmark {

    private TestService testService;

    private String param;

    private GoRequest build;

    private byte[] jsonRequest;

    public static GrpcDynamicMessage.Config config;

    private GrpcDynamicMessage rpc;

    @Setup
    public void setUp() {
        param = "test";
        GoRequest build = GoRequest.newBuilder().setWhere(param).build();
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8090).usePlaintext().build();
        testService = new TestServiceImpl(channel);

        try {
            config = new GrpcDynamicMessage.Config();
            String protoFilePath = "D:/go.proto";
            String descriptorFilePath = "D:/descriptor.pb";
            config.setMethod("goWhere");
            config.setService("GoService");
            config.setFullServiceName("com.szkingdom.demo.grpc.GoService");

            FileSystemResourceLoader resourceLoader = new FileSystemResourceLoader();
            Resource descriptorFile = resourceLoader.getResource("file:" + descriptorFilePath);
            Resource protoFile = resourceLoader.getResource("file:" + protoFilePath);
            ProtobufSchema schema = ProtobufSchemaLoader.std.load(protoFile.getInputStream(), "GoRequest");
            Descriptors.Descriptor descriptor =
                DescriptorProtos.FileDescriptorProto.parseFrom(descriptorFile.getInputStream()).getDescriptorForType();
            config.setDescriptor(descriptor);
            Descriptors.Descriptor outputType = getOutputTypeDescriptor(config, descriptorFile.getInputStream());
            config.setOutputType(outputType);
            config.setSchema(schema);

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Descriptors.DescriptorValidationException e) {
            throw new RuntimeException(e);
        }
        rpc = new GrpcDynamicMessage(config);
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonNode = null;
        try {
            jsonNode = objectMapper.readTree("{\"where\":\"test\"}");
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        jsonRequest = rpc.getData(jsonNode);
    }

    @Benchmark
    public void normal() {
        testService.test(param);
    }

    @Benchmark
    public void normalTrans() {
        testService.test(build);
    }

    @Benchmark
    public void dynamicTrans() {
        testService.testDynamic(jsonRequest, rpc);
    }

    @Benchmark
    public void dynamic() throws FileNotFoundException {
        testService.testDynamic(param);
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder().mode(Mode.Throughput).timeUnit(TimeUnit.SECONDS)
            // 预热次数,默认为 10
            .warmupIterations(5)
            // 实际测量次数,默认为5
            .measurementIterations(10)
            // 设置 JVM 启动的 fork 数量为 4,表示使用4个进程来执行所有测试,默认为 1
            .forks(3)
            // 设置线程数为 4,即每个测试方法会被 4 个线程并发执行
            .threads(4)
            // 设置每次预热的时间为 1 秒,默认为1
            .warmupTime(TimeValue.seconds(1))
            // 设置每次实际测量的时间为 1 秒,默认为1
            .measurementTime(TimeValue.seconds(1))
            // 设置当测试过程中出现错误时,JMH 会抛出异常
            .shouldFailOnError(true)
            // 设置每次测试前进行垃圾回收
            .shouldDoGC(true)
            // 设置 JVM 的参数为 "-server",即使用 Server 模式来执行测试
            .jvmArgs("-server")
            // 设置测试结果输出格式为 JSON 格式
            .resultFormat(ResultFormatType.JSON)
            // 设置测试结果输出的文件名
            .result("TestServiceBenchmark.json").build();
        new Runner(options).run();
    }
}

jmh 执行参数

  • 在测试中,读取方法的线程数为 4

  • 测试中,实际测量次数为30次,共执行4组进程;执行前预热时间1s,预热次数为5次

测试结果及分析

  • 测试结果如下:
Benchmark                            Mode  Cnt     Score     Error  Units
TestServiceBenchmark.dynamic       thrpt   30  4200.561 ± 228.911  ops/s
TestServiceBenchmark.normal        thrpt   30  4079.210 ± 363.460  ops/s
TestServiceBenchmark.dynamicTrans  thrpt   30  4328.580 ± 179.022  ops/s
TestServiceBenchmark.normalTrans   thrpt   30  4505.909 ± 274.406  ops/s
  • normal 和 dynamic 表示请求构建过程的测试,normalTrans 和 dynamicTrans 表示请求传输时的测试

1、请求构建:dynamic 的执行效率比 normal 执行效率高 3% 左右

(4200.561 / 4079.210 - 1) * 100% = 3%

2、请求传输:normalTrans 的传输效率比 dynamicTrans 传输效率高 4.2% 左右

(4505.909 / 4328.580 - 1) * 100% = 3%