Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
IYZaytsev opened this issue Jan 18, 2024 · 2 comments
Open

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

IYZaytsev opened this issue Jan 18, 2024 · 2 comments
Labels
question Further information is requested

Comments

@IYZaytsev
Copy link

IYZaytsev commented Jan 18, 2024

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
@IYZaytsev IYZaytsev added the question Further information is requested label Jan 18, 2024
@JamesNK
Copy link
Member

JamesNK commented Jan 18, 2024

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

@IYZaytsev
Copy link
Author

IYZaytsev commented Jan 18, 2024

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants