Русский
Русский
English
Статистика
Реклама

Перевод Сжатие ответов в GRPC для ASP.NET CORE 3.0

Перевод статьи подготовлен в преддверии старта курса C# ASP.NET Core разработчик.



В этом эпизоде моей серии статей о gRPC и ASP.NET Core мы рассмотрим подключение функции сжатия ответов (response compression) служб gRPC.

ПРИМЕЧАНИЕ: В этой статье я рассказываю о некоторых деталях касательно сжатия, которые я узнал, изучая параметры и методы настройки вызовов. Скорее всего есть более точные и более эффективные подходы для достижения тех же результатов.

Эта статья является частью серии о gRPC и ASP.NET Core.

КОГДА СЛЕДУЕТ ВКЛЮЧАТЬ СЖАТИЕ В GRPC?


Короткий ответ: это зависит от ваших полезных нагрузок (payloads).
Длинный ответ:
gRPC использует protocol buffer в качестве инструмента сериализации сообщений запросов и ответов, отправляемых по сети. Protocol buffer создает двоичный формат сериализации, который по умолчанию предназначен для небольших эффективных полезных нагрузок. По сравнению с обычными полезными нагрузками в формате JSON, protobuf дает более скромный размер сообщений. JSON довольно подробный и удобочитаемый. В результате он включает имена свойств в данные, передаваемые по сети, что увеличивает количество байтов, которые должны быть переданы.

В качестве идентификаторов данных, передаваемых по сети, protocol buffer использует целые числа. Он использует концепцию base 128 variants, которая позволяет полям со значениями от 0 до 127 требовать только один байт для транспортировки. Во многих случаях существует возможность ограничить ваши сообщения полями в этом диапазоне. Для больших целых чисел требуется более одного байта.

Итак, напомним, полезная нагрузка protobuf уже достаточно мала, поскольку формат нацелен на сокращение байтов, отправляемых по сети, до минимально возможного размера. Тем не менее, все еще остается потенциал для дальнейшего сжатия без потерь с помощью такого формата, как GZip. Этот потенциал необходимо тестировать на ваших полезных нагрузках, поскольку вы будете наблюдать сокращение размера только в том случае, если ваша полезная нагрузка имеет достаточное количество повторяющихся текстовых данных, чтобы извлечь из сжатия хоть какую-нибудь выгоду. Возможно, для небольших сообщений ответов попытка их сжатия может привести к получению большего количества байтов, нежели использование несжатого сообщения; что явно никуда не годится.

Также следует отметить накладные расходы процессора на сжатие, которые могут перевесить выигрыш, который вы достигаете за счет уменьшения размера. Вы должны отслеживать накладные расходы процессора и памяти на запросы после изменения уровня сжатия, чтобы оценить полную картину для ваших служб.

По умолчанию серверная интеграция ASP.NET Core не использует сжатие, но мы можем включить его для всего сервера или определенных служб. Это кажется разумным вариантом по умолчанию, поскольку вы можете отслеживать свои ответы для различных методов с течением времени и оценивать выгоду от их сжатия.

КАК ВКЛЮЧИТЬ СЖАТИЕ ОТВЕТОВ В GRPC?


На данный момент я нашел два основных подхода подключения сжатия ответов gRPC. Вы можете настроить это на уровне сервера, чтобы все службы gRPC применяли сжатие к ответам, или же на уровне отдельных служб.

НАСТРОЙКА НА УРОВНЕ СЕРВЕРА


services.AddGrpc(o =>{   o.ResponseCompressionLevel = CompressionLevel.Optimal;   o.ResponseCompressionAlgorithm = "gzip";});

Startup.cs на GitHub

При регистрации сервиса gRPC в контейнере инъекции зависимостей с помощью метода AddGrpc внутри ConfigureServices, у нас есть возможность произвести настройку в GrpcServiceOptions. На этом уровне параметры влияют на все службы gRPC, которые реализует сервер.

Используя перегрузку расширяющего метода AddGrpc, мы можем предоставить Action<GrpcServiceOptions>. В приведенном выше фрагменте кода мы выбрали алгоритм сжатия gzip. Мы также можем установить CompressionLevel, манипулируя временем, которое мы жертвуем на сжатие данных для получения меньшего размера. Если параметр не уазан, текущая реализация по умолчанию использует CompressionLevel.Fastest. В предыдущем фрагменте мы предоставили для сжатия больше времени, чтобы уменьшить количество байт до минимально возможного размера.

НАСТРОЙКА НА УРОВНЕ СЕРВИСА


services.AddGrpc()   .AddServiceOptions<WeatherService>(o =>       {           o.ResponseCompressionLevel = CompressionLevel.Optimal;           o.ResponseCompressionAlgorithm = "gzip";       });

Startup.cs на GitHub

В результате вызова AddGrpc возвращается IGrpcServerBuilder. Мы можем вызвать для билдера расширяющий метод под названием AddServiceOptions, чтобы предоставить параметры для каждой службы отдельно. Этот метод является универсальным и принимает тип службы gRPC, к которой должны применяться параметры.

В предыдущем примере мы решили предоставить параметры для вызовов, которые обрабатываются реализацией WeatherService. На этом уровне доступны те же параметры, которые мы обсуждали для конфигурации на уровне сервера. В этом сценарии другие службы gRPC на этом сервере не получат параметры сжатия, установленные нами для конкретной службы.

ЗАПРОС ОТ КЛИЕНТА GRPC


Теперь, когда сжатие ответов включено, нам нужно убедиться, что в наших запросах указано, что наш клиент принимает сжатый контент. Фактически, это включено по умолчанию при использовании GrpcChannel, созданного с помощью метода ForAddress, поэтому нам не нужно ничего делать в нашем клиентском коде.

var channel = GrpcChannel.ForAddress("https://localhost:5005");

Program.cs на GitHub

Созданные таким образом каналы уже отправляют заголовок grpc-accept-encoding, который включает тип сжатия gzip. Сервер считывает этот заголовок и определяет, что клиент разрешает возвращать сжатые ответы.

Один из способов визуализировать эффект сжатия включить логирование для нашего приложения во время разработки. Этого можно сделать, изменив файл appsettings.Development.json следующим образом:

{ "Logging": {   "LogLevel": {       "Default": "Debug",       "System": "Information",       "Grpc": "Trace",       "Microsoft": "Trace"   } }}

appsettings.Development.json на GitHub

При запуске нашего сервера мы получаем гораздо более подробные консольные логи.

info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]     Executing endpoint 'gRPC - /WeatherForecast.WeatherForecasts/GetWeather'dbug: Grpc.AspNetCore.Server.ServerCallHandler[1]     Reading message.dbug: Microsoft.AspNetCore.Server.Kestrel[25]     Connection id "0HLQB6EMBPUIA", Request id "0HLQB6EMBPUIA:00000001": started reading request body.dbug: Microsoft.AspNetCore.Server.Kestrel[26]     Connection id "0HLQB6EMBPUIA", Request id "0HLQB6EMBPUIA:00000001": done reading request body.trce: Grpc.AspNetCore.Server.ServerCallHandler[3]     Deserializing 0 byte message to 'Google.Protobuf.WellKnownTypes.Empty'.trce: Grpc.AspNetCore.Server.ServerCallHandler[4]     Received message.dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]     Sending message.trce: Grpc.AspNetCore.Server.ServerCallHandler[9]     Serialized 'WeatherForecast.WeatherReply' to 2851 byte message.trce: Microsoft.AspNetCore.Server.Kestrel[37]     Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 104 and flags END_HEADERStrce: Grpc.AspNetCore.Server.ServerCallHandler[10]     Compressing message with 'gzip' encoding.trce: Grpc.AspNetCore.Server.ServerCallHandler[7]     Message sent.info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]     Executed endpoint 'gRPC - /WeatherForecast.WeatherForecasts/GetWeather'trce: Microsoft.AspNetCore.Server.Kestrel[37]     Connection id "0HLQB6EMBPUIA" sending DATA frame for stream ID 1 with length 978 and flags NONEtrce: Microsoft.AspNetCore.Server.Kestrel[37]     Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 15 and flags END_STREAM, END_HEADERSinfo: Microsoft.AspNetCore.Hosting.Diagnostics[2]     Request finished in 2158.9035ms 200 application/grpc

Log.txt на GitHub

В 16-й строке этого лога мы видим, что WeatherReply (по сути, массив из 100 элементов WeatherData в данном примере) был сериализован в protobuf и имеет размер 2851 байт.

Позже, в 20-й строке мы видим, что сообщение было сжато с помощью кодировки gzip, а в 26-й строке мы можем увидеть размер фрейма данных для этого вызова, который составляет 978 байт. В данном случае данные были хорошо сжаты (на 66%), поскольку повторяющиеся элементы WeatherData содержат текст, и многие значения в сообщении повторяются.

В этом примере сжатие gzip хорошо повлияло на размер данных.

ОТКЛЮЧЕНИЕ СЖАТИЯ ОТВЕТА В РЕАЛИЗАЦИИ МЕТОДА СЛУЖБ


Сжатие ответа можно контролировать в каждом методе. В настоящее время я нашел способ только как отключать его. Когда сжатие включено для службы или сервера, мы можем отказаться от сжатия в рамках реализации метода службы.

Давайте посмотрим на серверный лог при вызове метода службы, который передает сообщения WeatherData с сервера. Если вы хотите узнать больше о потоковой передаче на сервере, вы можете почитать мою предыдущую статью Потоковая передача данных на сервере с gRPC и .NET Core.

info: WeatherForecast.Grpc.Server.Services.WeatherService[0]     Sending WeatherData responsedbug: Grpc.AspNetCore.Server.ServerCallHandler[6]     Sending message.trce: Grpc.AspNetCore.Server.ServerCallHandler[9]     Serialized 'WeatherForecast.WeatherData' to 30 byte message.trce: Grpc.AspNetCore.Server.ServerCallHandler[10]     Compressing message with 'gzip' encoding.trce: Microsoft.AspNetCore.Server.Kestrel[37]     Connection id "0HLQBMRRH10JQ" sending DATA frame for stream ID 1 with length 50 and flags NONEtrce: Grpc.AspNetCore.Server.ServerCallHandler[7]     Message sent.

Log.txt на GitHub

В 6-й строке мы видим, что отдельное сообщение WeatherData имеет размер 30 байт. В 8-й строке происходит сжатие, а в 10-й мы видим, что длина данных теперь составляет 50 байтов больше, чем исходное сообщение. В этом случае для нас нет никакой выгоды от gzip сжатия, мы видим увеличение общего размера сообщения, отправляемого по сети.

Мы можем отключить сжатие для конкретного сообщения, установив WriteOptions для вызова в методе службы.

public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context){   context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);   // реализация метода, который записывает в поток}

WeatherService.cs на GitHub

Мы можем установить WriteOptions в ServerCallContext в верхней части нашего метода службы. Мы передаем новый экземпляр WriteOptions, для которого значение WriteFlags установлено в NoCompress. Эти параметры используются для следующей записи.

При потоковой передаче ответов это значение также можно установить в IServerStreamWriter.

public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context){      responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);   // реализация метода записи в поток}

WeatherService.cs на GitHub

Когда мы применяем этот параметр, логи показывают, что сжатие для вызовов этого метода службы не применяется.

info: WeatherForecast.Grpc.Server.Services.WeatherService[0]     Sending WeatherData responsedbug: Grpc.AspNetCore.Server.ServerCallHandler[6]     Sending message.trce: Grpc.AspNetCore.Server.ServerCallHandler[9]     Serialized 'WeatherForecast.WeatherData' to 30 byte message.trce: Microsoft.AspNetCore.Server.Kestrel[37]     Connection id "0HLQBMTL1HLM8" sending DATA frame for stream ID 1 with length 35 and flags NONEtrce: Grpc.AspNetCore.Server.ServerCallHandler[7]     Message sent.

Log.txt на GitHub

Теперь 30-байтное сообщение имеет длину 35 байтов в DATA фрейме. Есть небольшие накладные расходы, которые составляют дополнительные 5 байтов, о которых нам здесь не нужно беспокоиться.

ОТКЛЮЧЕНИЕ СЖАТИЯ ОТВЕТА ИЗ КЛИЕНТА GRPC


По умолчанию канал gRPC включает параметры, которые определяют, какие кодировки он принимает. Их можно настроить при создании канала, если вы хотите отключить сжатие ответов из вашего клиента. Как правило, я бы избегал этого и позволял серверу решать, что делать, поскольку он лучше знает, что можно и что нельзя сжимать. Тем не менее, иногда вам может потребоваться контролировать это из клиента.

Единственный способ, который я нашел в своем исследовании API на сегодняшний день, это настроить канал, передав экземпляр GrpcChannelOptions. Одно из свойств этого класса предназначено для CompressionProviders IList<ICompressionProvider>. По умолчанию, когда это значение равно null, реализация клиента автоматически добавляет поставщика сжатия Gzip. Это означает, что сервер может использовать gzip для сжатия сообщений ответов, как мы уже видели.

private static async Task Main(){   using var channel = GrpcChannel.ForAddress("https://localhost:5005", new GrpcChannelOptions { CompressionProviders = new List<ICompressionProvider>() });   var client = new WeatherForecastsClient(channel);   var reply = await client.GetWeatherAsync(new Empty());   foreach (var forecast in reply.WeatherData)  {       Console.WriteLine($"{forecast.DateTimeStamp.ToDateTime():s} | {forecast.Summary} | {forecast.TemperatureC} C");   }   Console.WriteLine("Press a key to exit");   Console.ReadKey();}

Program.cs на GitHub
В этом примере клиентского кода мы устанавливаем GrpcChannel и передаем новый экземпляр GrpcChannelOptions. Мы присваиваем свойству CompressionProviders пустой список. Поскольку теперь мы не указываем поставщиков в нашем канале, когда вызовы создаются и отправляются через этот канал, они не будут включать какие-либо кодировки сжатия в заголовок grpc-accept-encoding. Сервер видит это и не применяет gzip сжатие к ответу.

РЕЗЮМЕ


В этом посте мы исследовали возможность сжатия сообщений ответов от сервера gRPC. Мы обнаружили, что в некоторых случаях (но не во всех) это может привести к уменьшению размера полезной нагрузки. Мы видели, что по умолчанию вызовы клиентов включают в заголовки значение gzip grpc-accept-encoding. Если сервер настроен на применение сжатия, он будет делать это только в том случае, если поддерживаемый тип сжатия совпадает с заголовком запроса.

Мы можем настроить GrpcChannelOptions при создании канала для клиента, чтобы отключить gzip сжатие. На сервере мы можем настроить сразу весь сервер или отдельную службу на сжатие ответов. Мы также можем переопределить и отключить это на уровне каждого метода службы.

Чтобы узнать больше о gRPC, вы можете прочитать все статьи, которые являются частью моей серии о gRPC и ASP.NET Core.



ВСЁ О КУРСЕ




Читать ещё:

Источник: habr.com
К списку статей
Опубликовано: 27.08.2020 12:18:32
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus. онлайн-образование

Net

Asp

C

Compression

Grpc

Grpc server

Gzip

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru