Службы gRPC с ASP.NET Core имеют плохую производительность - PullRequest
0 голосов
/ 20 октября 2019

Я экспериментирую с gRPC на существующем WEBAPI ASP.NET core 3.0, все работает нормально, когда я сравниваю время API действия контроллера и метода службы gRPC на локальном хосте докера (Операционная система: Docker Desktop; OSType: linux;Архитектура: x86_64; ЦП: 2) с тем же объектом, определенным для службы gRPC, почти в 3 раза быстрее, чем действие контроллера.

Но когда я развернул этот образ докера на хосте док-сервера Linux (версия ядра: 5.0).0-1018-azure; Операционная система: Ubuntu 18.04.3 LTS; OSType: linux; Архитектура: x86_64) результат теста полностью отличается. Размер ответа от службы gRPC все еще меньше (1617000), чем действие контроллера (2700001), но время выполнения действия контроллера теперь быстрее, чем для службы gRPC.

Вот некоторые подробности кода и журналов:

  • Некоторые журналы из моего локального local docker container logs
  • Журналы из моего консольного приложения при вызове локального док-контейнера:
gRPC: Length 3000 - Time Execution  43ms - overrall 0:00.043 - size 1617000
HttpClient: Length 3000 - Time Execution 139ms - overrall 0:00.139 -size 2700001
gRPC: Length 3000 - Time Execution  46ms - overrall 0:00.046 - size 1617000
HttpClient: Length 3000 - Time Execution  144ms - overrall 0:00.144 -size 2700001
  • Некоторые журналы с моего сервера server docker container logs
  • Журналы из моего консольного приложения при вызове Docker-контейнера сервера:
gRPC: Length 3000 - Time Execution 1065ms - overrall 0:01.065 - size 1617000
HttpClient: Length 3000 - Time Execution  688ms - overrall 0:00.688 -size 2700001
gRPC: Length 3000 - Time Execution  1079ms - overrall 0:01.079 - size 1617000
HttpClient: Length 3000 - Time Execution 691ms - overrall 0:00.691 -size 2700001

gRPC-код сервера:

private static ProtoGetProductByIdResponseList resTest;
        public override async Task<ProtoGetProductByIdResponseList> ProtoTestGetProductsAsList(ProtoTestGetRequest request, ServerCallContext context)
        {
            var watch = new Stopwatch();
            watch.Start();
            // just to test the perf
            // if there are no response then get one from db, create list with 3000 entries
            if (resTest == null || resTest.Products == null || !resTest.Products.Any())
            {
                _logger.LogInformation("gRPC Get one from db");
                var dto = await GetProductDto(1);
                int i = 0;
                resTest = new ProtoGetProductByIdResponseList();
                while (i < 3000)
                {
                    resTest.Products.Add(dto);
                    i++;
                }
            }
            watch.Stop();
            TimeSpan timeTaken = watch.Elapsed;

            _logger.LogInformation("gRPC: Length {0} - Time {1} - overrall {2} -size {3}", resTest.Products.Count, watch.ElapsedMilliseconds, timeTaken.ToString(@"m\:ss\.fff"), resTest.CalculateSize());
            return resTest;
        }

Код клиента gRPC:

class Program
  {
    public const string APITest = "https://IP:32768/test/1";

    // Return `true` to allow certificates that are untrusted/invalid

    static async Task Main(string[] args)
    {
      //AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
      //var httpClient1 = new HttpClient
      //{
      //  BaseAddress = new Uri("http://IP:5000")
      //};

      HttpClientHandler httpClientHandler = new HttpClientHandler();
      httpClientHandler.ServerCertificateCustomValidationCallback =
         HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
      HttpClient apiClient = new HttpClient(httpClientHandler);

      var httpClient = new HttpClient(httpClientHandler);
      var channel = GrpcChannel.ForAddress("https://IP:32768/", new GrpcChannelOptions() { HttpClient= httpClient });
      var client = new ProtoProductService.ProtoProducter.ProtoProducterClient(channel);

      var watch = new Stopwatch();
      int i = 0;
      while (i < 10)
      {
        TestgRPC(client, watch);

        TestHttpClient(apiClient, watch);
        i++;
      }
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
    }

    private static void TestHttpClient(HttpClient apiClient, Stopwatch watch)
    {
      watch.Reset();
      var message = HttpRequestMessageBuilder.GetHttpRequestBuilder(HttpMethod.Get, APITest)
                                       .Build();
      watch.Start();


      var replyRes = apiClient.SendAsync(message).Result;
      var response = replyRes.HandleReponseAsync<List<object>>().Result;
      watch.Stop();
      TimeSpan timeTaken = watch.Elapsed;

      Console.WriteLine("HttpClient: Length {0} - Time {1} - overrall {2} -size {3}", response.Count, watch.ElapsedMilliseconds, timeTaken.ToString(@"m\:ss\.fff"), replyRes.Content.Headers.ContentLength);

      watch.Reset();
    }

    private static void TestgRPC(ProtoProductService.ProtoProducter.ProtoProducterClient client, Stopwatch watch)
    {
      watch.Reset();
      watch.Start();
      var reply = client.ProtoTestGetProductsAsList(new ProtoProductService.ProtoTestGetRequest()
      {
        Size = 1
      });
      watch.Stop();
      TimeSpan timeTaken = watch.Elapsed;

      Console.WriteLine("gRPC: Length {0} - Time {1} - overrall {2} - size {3}", reply.Products.Count, watch.ElapsedMilliseconds, timeTaken.ToString(@"m\:ss\.fff"), reply.CalculateSize());
      watch.Reset();
    }
  }
...