grpc / grpc-dotnet

gRPC for .NET
Apache License 2.0
4.21k stars 774 forks source link

Performance of Grpc.AspNetCore vs REST over HTTP/2 Similar? #2363

Open IYZaytsev opened 10 months ago

IYZaytsev commented 10 months ago

I wrote these two APIs to talk to another service(TestApp2). One forwards via GRPC and the other uses HTTP/2.

    public async Task<Results<BadRequest, Ok<Company>>> ForwardRequestGRPC()
    {
        try
        {
            var response = await GRPCGreeterClient.SayHelloAsync(new HelloRequest { Name = "World" });
            return TypedResults.Ok(response);
        }
        catch (Exception ex)
        {
            ApplicationInstance.Logger.LogInformation(ex.Message);
            ApplicationInstance.Logger.LogInformation(ex.StackTrace);
            return TypedResults.BadRequest();
        }

    }

    public async Task<Results<BadRequest, Ok<RESTCompany>>> ForwardRequest()
    {
        try
        {
            var payload = new { name = "Hello" };
            var jsonPayload = JsonSerializer.Serialize(payload);
            var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
            var response = await RestClient.PostAsync(Program.TestApp2Address + "/sayhello", content);
            var jsonString = await response.Content.ReadAsStringAsync();
            var info = JsonSerializer.Deserialize<RESTCompany>(jsonString, SerializerOptions);
            return TypedResults.Ok(info);
        }
        catch (Exception ex)
        {
            ApplicationInstance.Logger.LogInformation(ex.Message);
            ApplicationInstance.Logger.LogInformation(ex.StackTrace);
            return TypedResults.BadRequest();
        }

    }

    }

Using these settings.

        // Create the GRPC client and REST client
        // Register endpoints
        var channel = GrpcChannel.ForAddress(TestApp2Address);
        var grpcclient = new Greeter.GreeterClient(channel);
        app.Logger.LogInformation("Using http2");

        HttpClient httpclient = new HttpClient
        {
            DefaultRequestVersion = HttpVersion.Version20,
            DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact
        };
        var endpoints = new EndPoints(app, grpcclient, httpclient);
        endpoints.RegisterEndpoints();
        app.Run();

TestApp2 has these settings

    static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.WebHost.ConfigureKestrel(options =>
        {
            options.Listen(IPAddress.Any, 9001, listenOptions =>
            {
                listenOptions.Protocols = HttpProtocols.Http2;
            });
        });

        builder.Services.AddGrpc();
        var app = builder.Build();
        ApplicationInstance = app;
        app.MapGrpcService<GreeterService>();
        app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
        app.MapPost("/sayhello", SayHello).Produces<Company>().Produces(400).WithName("SayHello");
        app.Run();
    }

And the two handlers being.

    public override Task<Company> SayHello(HelloRequest request,
        ServerCallContext context)
    {
        Employee employee1 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
        Employee employee2 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
        Employee employee3 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
        Employee employee4 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
        Employee employee5 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };

        Department department1 = new Department { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
        department1.Employees.Add(employee1);
        department1.Employees.Add(employee2);
        department1.Employees.Add(employee3);
        department1.Employees.Add(employee4);
        department1.Employees.Add(employee5);

        //_logger.LogInformation("Saying hello to {Name}", request.Name);
        return Task.FromResult(new Company
        {
            Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR",
            Departments = { department1 }
        });
    }
    static Results<BadRequest, Ok<Company>> SayHello()
    {
        try
        {
            //ApplicationInstance?.Logger.LogInformation("Saying hello to REST");
            Employee employee1 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
            Employee employee2 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
            Employee employee3 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
            Employee employee4 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
            Employee employee5 = new Employee { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Position = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };

            Department department1 = new Department { Id = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR", Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR" };
            department1?.Employees?.Add(employee1);
            department1?.Employees?.Add(employee2);
            department1?.Employees?.Add(employee3);
            department1?.Employees?.Add(employee4);
            department1?.Employees?.Add(employee5);
            var company = new Company
            {
                Name = "24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR24klP9hY8pfQStmPSFnGraxc8jAfPQhSS8zwW9Ltcvw6whCmXeRxDwXcLcAzl5SR",
            };

            company?.Departments?.Add(department1);

            return TypedResults.Ok(company);
        }
        catch (Exception ex)
        {
            ApplicationInstance?.Logger.LogInformation(ex.Message);
            ApplicationInstance?.Logger.LogInformation(ex.StackTrace);
            return TypedResults.BadRequest();
        }

    }

I wrote a program to try to measure the latency of each API and what I found is that both have almost the same performance. I understand that GRPC uses HTTP/2 but I thought the serialization and deserialization would still make it faster.

using System.Diagnostics;

class Program
{
    static string payload = "";

    static async Task<string> MakeRequestAsync(string url)
    {
        var httpClient = new HttpClient();
        var response = await httpClient.PostAsync(url, new StringContent(payload));
        return response.Content.ToString();
    }

    static async Task<double> RunPerfTest(string url, int numRequests)
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        var tasks = new List<Task>();

        for (int i = 0; i < numRequests; i++)
        {
            tasks.Add(MakeRequestAsync(url));
        }

        Task.WhenAll(tasks).Wait();
        stopwatch.Stop();

        double averageTimeInMS = stopwatch.Elapsed.TotalMilliseconds / numRequests;
        Console.WriteLine("--------------------------------------------------------------------");
        Console.WriteLine($"Total time for {numRequests} requests: {stopwatch.Elapsed.TotalSeconds} seconds");
        Console.WriteLine($"{url}");
        Console.WriteLine($"Average time per request: {averageTimeInMS} milliseconds");
        Console.WriteLine("--------------------------------------------------------------------");
        return averageTimeInMS;
    }

    static async Task Main(string[] args)
    {
        // Check if there are any command-line arguments
        if (args.Length > 0)
        {
            // Iterate through the command-line arguments
            for (int i = 0; i < args.Length; i++)
            {
                Console.WriteLine($"Argument {i + 1}: {args[i]}");
            }
        }
        else
        {
            Console.WriteLine("No command-line arguments provided.");
            Environment.Exit(-1);
        }

        int numRequests = int.Parse(args[0]);
        int rounds = int.Parse(args[1]);
        string urlGRPC = "http://localhost:9000/forwardGRPC";
        string urlREST = "http://localhost:9000/forward";

        string testcase = args[2];

        List<double> grpc = new List<double>();
        List<double> rest = new List<double>();
        if (testcase == "grpc")
        {
            for (int i = 0; i < rounds; i++)
            {
                grpc.Add(await RunPerfTest(urlGRPC, numRequests));
                grpc.Add(await RunPerfTest(urlGRPC, numRequests));
                grpc.Add(await RunPerfTest(urlGRPC, numRequests));
            }
        }
        if (testcase == "rest")
        {
            for (int i = 0; i < rounds; i++)
            {
                rest.Add(await RunPerfTest(urlREST, numRequests));
                rest.Add(await RunPerfTest(urlREST, numRequests));
                rest.Add(await RunPerfTest(urlREST, numRequests));
            }

        }
        if (testcase == "both")
        {
            for (int i = 0; i < rounds; i++)
            {
                grpc.Add(await RunPerfTest(urlGRPC, numRequests));
                grpc.Add(await RunPerfTest(urlGRPC, numRequests));
                grpc.Add(await RunPerfTest(urlGRPC, numRequests));

                rest.Add(await RunPerfTest(urlREST, numRequests));
                rest.Add(await RunPerfTest(urlREST, numRequests));
                rest.Add(await RunPerfTest(urlREST, numRequests));
            }

        }

        Console.WriteLine("--------------------------------------------------------------------");
        Console.WriteLine("--------------------------------------------------------------------");
        Console.WriteLine("--------------------------------------------------------------------");
        Console.WriteLine($"GRPC {grpc.Sum() / grpc.Count}");
        Console.WriteLine($"REST {rest.Sum() / rest.Count}");

    }
}
ivanzaytsev@Ivans-MacBook-Air net8.0 % ./speedtest 1000 100 both
GRPC 0.30555692599999995
REST 0.3093026813333334
JamesNK commented 10 months ago

That's expected. Serialization is a small piece of making an HTTP request.

IYZaytsev commented 10 months ago

That's expected. Serialization is a small piece of making an HTTP request.

But I thought that the whole point of protobuf was faster serialization/deserialization and smaller size. Shouldn't there be at least some performance improvement? Are you sure I am not doing something wrong and that these two calls are equivalent, or maybe some configuration I missed? If there is not something wrong with my methods, System.Text.Json must have some crazy good performance. If I am not using streaming but just unary what is the benefit of using GRPC?