gRPC 通信的性能比 REST 更差?

问题描述 投票:0回答:1

我最近开始使用 gRPC,并且总是听说它比 REST 快得多。所以我创建了一个基准项目,因为我想知道它实际上快了多少。事实证明,经过几种不同的方法后,REST 总是比 gRPC 稍好一些。我对基准测试和 gRPC 都没有经验,所以一定是出了问题。我的猜测是,也许我的基准测试设置不正确。

所以我的问题是:我的基准测试有什么问题?或者说结果真的有意义吗?

我在这里创建了一个包含完整代码的存储库:https://github.com/henridd/RestVsGrpcBenchmark。对于这个问题,我将讨论 BenchmarkWithSamePayload 运行。

这些是上次测试的结果。

// * Summary *

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3155/23H2/2023Update/SunValley3)
AMD Ryzen 5 5600X, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.201
  [Host]     : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2


| Method        | Mean     | Error   | StdDev  |
|-------------- |---------:|--------:|--------:|
| BenchmarkGrpc | 147.2 us | 0.71 us | 0.59 us |
| BenchmarkRest | 111.6 us | 0.68 us | 0.90 us |

// * Hints *
Outliers
  BenchmarkWithSamePayload.BenchmarkGrpc: Default -> 2 outliers were removed, 3 outliers were detected (145.56 us, 150.79 us, 152.36 us)
  BenchmarkWithSamePayload.BenchmarkRest: Default -> 8 outliers were removed (118.18 us..126.86 us)

// * Legends *
  Mean   : Arithmetic mean of all measurements
  Error  : Half of 99.9% confidence interval
  StdDev : Standard deviation of all measurements
  1 us   : 1 Microsecond (0.000001 sec)

您可以在下面找到此基准测试的相关代码。

Grpc

原型

rpc SayHello (HelloRequest) returns (HelloReply);
message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

客户

public class Sender
{
    private GrpcChannel _grpcChannel;
    private Greeter.GreeterClient _greeter;
    private HelloRequest _defaultRequest;

    public Sender()
    {
        _grpcChannel = GrpcChannel.ForAddress("http://localhost:5264");
        _greeter = new Greeter.GreeterClient(_grpcChannel);
        _defaultRequest = new HelloRequest() { Name = "default" };
    }

    public async Task<string> PostDefault()
    {
        var reply = await _greeter.SayHelloAsync(_defaultRequest);

        return reply.Message;
    }
}

服务

节目

using GrpcService.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddGrpc();

var app = builder.Build();

// Configure the HTTP request pipeline.
app.MapGrpcService<Service>();

app.Run();

服务

public class Service : Greeter.GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply
        {
            Message = "Hello " + request.Name
        });
    }
}

休息

留言

public class HelloRequest
{
    public string Name { get; set; }
}

你好回复

public class HelloResponse
{
    public string Message { get; set; }
}

客户

public class Sender
{
    private HttpClient _httpClient;
    private HelloRequest _defaultRequest;

    public Sender()
    {
        _httpClient = new HttpClient();
        _defaultRequest = new HelloRequest() { Name = "default" };
    }


    public async Task<string> PostDefault()
    {
        var content = JsonContent.Create(_defaultRequest);
        var response = await _httpClient.PostAsync("http://localhost:5082/greet", content);
        var responseString = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<HelloResponse>(responseString)!.Message;
    }

}

服务器

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var app = builder.Build();

app.MapPost("/greet", (HelloRequest request) =>
{
    return new HelloResponse() { Message = "Hello " + request.Name };
});

app.Run();

基准

节目

var summary = BenchmarkDotNet.Running.BenchmarkRunner.Run<BenchmarkWithSamePayload>();

具有相同有效负载的基准

public class BenchmarkWithSamePayload : BenchmarkBase
{
    [Benchmark]
    public async Task<string> BenchmarkGrpc()
    {
        return await _gRpcSender.PostDefault();
    }

    [Benchmark]
    public async Task<string> BenchmarkRest()
    {
        return await _restSender.PostDefault();
    }
}

基准库

namespace BenchmarkRunner
{
    using gRpcSender = GrpcClient.Sender;
    using RestSender = RestClient.Sender;

    public abstract class BenchmarkBase
    {
        protected gRpcSender _gRpcSender;
        protected RestSender _restSender;

        [GlobalSetup]
        public void Setup()
        {
            _gRpcSender = new gRpcSender();
            _restSender = new RestSender();
        }
    }
}

BenchmarkConfig(防病毒所需)

public class BenchmarkConfig : ManualConfig
{
    public BenchmarkConfig()
    {
        AddJob(Job.MediumRun.WithToolchain(InProcessNoEmitToolchain.Instance));
    }
}
c# performance rest grpc benchmarkdotnet
1个回答
0
投票

首先,使用微基准测试工具对 Web 应用程序进行性能测试可能被认为并不理想,因为 Web 服务通常设计为提供比延迟更好的吞吐量(即处理更多负载而不是更快地处理单个请求)。

其次(基于代码)您正在非常特定的情况下执行测试 - 您正在同一台机器上测试“简单”响应,而 GRPC 的目标是减少通过网络发送的消息的大小,这可能是较小的因素在同一台机器上通过 TCP 进行单线程测试。生成的 GRPC 服务器是为吞吐量而设计的(因此方法签名中的

Task
),因此已经存在一个小差异 - REST 不会打扰
Task.FromResult
,这也会增加一些开销(尽管不是那么明显) ).

TL;博士

最好通过网络使用一些负载测试工具(例如NBomber),并检查负载服务能够处理的差异。

© www.soinside.com 2019 - 2024. All rights reserved.