Dalam episode seri saya di gRPC dan ASP.NET Core kali ini, kita akan melihat cara menghubungkan fungsi kompresi respons dari layanan gRPC.
CATATAN : Dalam artikel ini, saya membahas beberapa detail kompresi yang saya pelajari dengan mempelajari tentang pengaturan dan metode panggilan. Ada kemungkinan pendekatan yang lebih akurat dan lebih efektif untuk mencapai hasil yang sama.
Artikel ini adalah bagian dari seri gRPC dan ASP.NET Core .
Kapan Anda harus mengaktifkan kompresi di GRPC?
Jawaban singkatnya: itu tergantung pada muatan Anda.
Jawaban panjang:
gRPC menggunakan buffer protokol sebagai alat untuk membuat serialisasi permintaan dan pesan respons yang dikirim melalui jaringan. Buffer protokol membuat format serialisasi biner yang dirancang untuk muatan kecil dan efisien secara default. Dibandingkan dengan payload JSON biasa, protobuf memberikan ukuran pesan yang lebih sederhana. JSON cukup bertele-tele dan mudah dibaca. Akibatnya, ini menyertakan nama properti dalam data yang dikirim melalui jaringan, yang meningkatkan jumlah byte yang harus ditransfer.
Buffer protokol menggunakan bilangan bulat sebagai pengidentifikasi untuk data yang dikirim melalui jaringan. Ini menggunakan konsep varian basis 128, yang memungkinkan bidang dengan nilai dari 0 hingga 127 hanya memerlukan satu byte untuk transportasi. Dalam banyak kasus, dimungkinkan untuk membatasi pesan Anda ke bidang dalam kisaran ini. Bilangan bulat besar membutuhkan lebih dari satu byte.
Jadi, ingat, muatan protobuf sudah cukup kecil, karena formatnya bertujuan untuk mengurangi byte yang dikirim melalui jaringan ke ukuran sekecil mungkin. Namun, masih ada potensi untuk kompresi lossless lebih lanjut menggunakan format seperti GZip. Potensi ini perlu diuji pada payload Anda karena Anda hanya akan melihat pengurangan ukuran jika payload Anda memiliki cukup data tekstual berulang untuk mendapatkan manfaat dari kompresi. Mungkin untuk pesan respons kecil, mencoba mengompresnya mungkin menghasilkan lebih banyak byte daripada menggunakan pesan yang tidak dikompresi; yang jelas tidak bagus.
Yang juga perlu diperhatikan adalah overhead kompresi prosesor, yang dapat melebihi keuntungan yang Anda peroleh dari pengurangan ukuran. Anda harus melacak CPU dan overhead memori untuk permintaan setelah mengubah tingkat kompresi untuk mendapatkan gambaran lengkap tentang layanan Anda.
Integrasi Server Inti ASP.NET tidak menggunakan kompresi secara default, tetapi kami dapat mengaktifkannya untuk seluruh server atau layanan tertentu. Ini sepertinya default yang masuk akal karena Anda dapat melacak respons Anda untuk metode yang berbeda dari waktu ke waktu dan mengevaluasi manfaat mengompresnya.
Bagaimana cara mengaktifkan kompresi respons di GRPC?
Sejauh ini saya telah menemukan dua pendekatan utama untuk menghubungkan kompresi respons gRPC. Anda dapat mengonfigurasi ini di tingkat server sehingga semua layanan gRPC menerapkan kompresi ke respons, atau di tingkat layanan individu.
Konfigurasi tingkat server
services.AddGrpc(o =>
{
o.ResponseCompressionLevel = CompressionLevel.Optimal;
o.ResponseCompressionAlgorithm = "gzip";
});
Startup.cs GitHub
Saat mendaftarkan layanan gRPC di container injeksi dependensi menggunakan metode
AddGrpcdi dalamnya ConfigureServices, kami memiliki kesempatan untuk mengonfigurasi GrpcServiceOptions. Pada level ini, parameter memengaruhi semua layanan gRPC yang diimplementasikan server.
Menggunakan overload metode ekstensi
AddGrpc, kami dapat menyediakan Action<GrpcServiceOptions>. Dalam potongan kode di atas, kami telah memilih algoritma kompresi "gzip". Kita juga bisa membangun CompressionLeveldengan memanipulasi waktu yang kita korbankan untuk kompresi data untuk mendapatkan ukuran yang lebih kecil. Jika parameter tidak ditentukan, implementasi saat ini secara default menggunakan CompressionLevel.Fastest. Dalam cuplikan sebelumnya, kami memberikan lebih banyak waktu untuk kompresi guna mengurangi jumlah byte ke ukuran sekecil mungkin.
Konfigurasi tingkat layanan
services.AddGrpc()
.AddServiceOptions<WeatherService>(o =>
{
o.ResponseCompressionLevel = CompressionLevel.Optimal;
o.ResponseCompressionAlgorithm = "gzip";
});
Startup.cs GitHub
Panggilan telepon
AddGrpckembali IGrpcServerBuilder. Kita dapat memanggil metode ekstensi yang dipanggil pada pembuat AddServiceOptionsuntuk memberikan parameter untuk setiap layanan secara terpisah. Metode ini bersifat umum dan menggunakan jenis layanan gRPC yang harus diterapkan parameter.
Pada contoh sebelumnya, kami memilih untuk memberikan parameter untuk panggilan yang ditangani oleh implementasi
WeatherService. Pada level ini, opsi yang sama tersedia yang telah kita diskusikan untuk konfigurasi level server. Dalam skenario ini, layanan gRPC lain di server ini tidak akan menerima opsi kompresi yang kami setel untuk layanan tersebut.
Permintaan Klien GRPC
Sekarang setelah kompresi respons diaktifkan, kami perlu memastikan permintaan kami menunjukkan bahwa klien kami menerima konten terkompresi. Faktanya, ini diaktifkan secara default saat digunakan
GrpcChanneldengan metode yang dibuat ForAddress, jadi kami tidak perlu melakukan apa pun di kode klien kami.
var channel = GrpcChannel.ForAddress("https://localhost:5005");
Program.cs GitHub
Saluran yang dibuat dengan cara ini sudah mengirimkan header "grpc-accept-encoding" yang menyertakan jenis kompresi gzip. Server membaca tajuk ini dan menentukan bahwa klien memungkinkan tanggapan terkompresi dikembalikan.
Salah satu cara untuk memvisualisasikan efek kompresi adalah dengan mengaktifkan pencatatan untuk aplikasi kita pada waktu desain. Ini dapat dilakukan dengan memodifikasi file
appsettings.Development.jsonsebagai berikut:
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Grpc": "Trace",
"Microsoft": "Trace"
}
}
}
appsettings.Development.json GitHub
Saat memulai server kami, kami mendapatkan log konsol yang jauh lebih detail.
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_HEADERS
trce: 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 NONE
trce: Microsoft.AspNetCore.Server.Kestrel[37]
Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 15 and flags END_STREAM, END_HEADERS
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 2158.9035ms 200 application/grpc
Log.txt GitHub
Di baris ke-16 dari log ini, kita melihat bahwa WeatherReply (pada kenyataannya, larik 100 elemen WeatherData dalam contoh ini) diserialkan ke protobuf dan berukuran 2851 byte.
Kemudian pada baris 20, kita melihat bahwa pesan tersebut dikompresi menggunakan encoding gzip, dan pada baris 26, kita dapat melihat ukuran frame data untuk panggilan ini, yaitu 978 byte. Dalam kasus ini, data dikompresi dengan baik (66%) karena elemen WeatherData yang berulang berisi teks dan banyak nilai dalam pesan tersebut diulang.
Dalam contoh ini, kompresi gzip memiliki pengaruh yang baik pada ukuran data.
Nonaktifkan kompresi respons dalam implementasi metode layanan
Kompresi respons dapat dikontrol di setiap metode. Saat ini saya sudah menemukan cara untuk mematikannya saja. Saat kompresi diaktifkan untuk layanan atau server, kami dapat memilih keluar dari kompresi sebagai bagian dari implementasi metode layanan.
Mari kita lihat log server saat memanggil metode layanan yang mengirimkan pesan WeatherData dari server. Jika Anda ingin mempelajari lebih lanjut tentang streaming ke server, Anda dapat membaca artikel saya sebelumnya Mengalirkan Data ke Server dengan gRPC dan .NET Core .
info: WeatherForecast.Grpc.Server.Services.WeatherService[0]
Sending WeatherData response
dbug: 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 NONE
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
Message sent.
Log.txt GitHub
Di baris ke-6, kita melihat bahwa pesan WeatherData individu berukuran 30 byte. Baris 8 dikompresi, dan baris 10 menunjukkan bahwa data sekarang panjangnya 50 byte - lebih dari pesan asli. Dalam hal ini, tidak ada manfaat bagi kami dari kompresi gzip, kami melihat peningkatan ukuran total pesan yang dikirim melalui jaringan.
Kami dapat menonaktifkan kompresi untuk pesan tertentu dengan mengaturnya
WriteOptionsuntuk memanggil dalam metode layanan.
public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{
context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
// ,
}
WeatherService.cs GitHub
Kami dapat mengatur
WriteOptionsdi ServerCallContextbagian atas metode layanan kami. Kami meneruskan contoh baru WriteOptionsyang WriteFlagsdisetel ke NoCompress. Parameter ini digunakan untuk entri berikutnya.
Saat mengalirkan respons, nilai ini juga bisa disetel ke
IServerStreamWriter.
public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{
responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
//
}
WeatherService.cs GitHub
Saat kami menggunakan parameter ini, log menunjukkan bahwa tidak ada kompresi yang diterapkan ke panggilan ke metode layanan ini.
info: WeatherForecast.Grpc.Server.Services.WeatherService[0]
Sending WeatherData response
dbug: 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 NONE
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
Message sent.
Log.txt GitHub
Sekarang pesan 30 byte memiliki panjang 35 byte dalam bingkai DATA. Ada overhead kecil yang merupakan tambahan 5 byte yang tidak perlu kita khawatirkan di sini.
Nonaktifkan kompresi respons dari klien GRPC
Secara default, saluran gRPC menyertakan parameter yang menentukan encoding yang diterima. Ini dapat dikonfigurasi saat membuat saluran jika Anda ingin menonaktifkan kompresi tanggapan dari klien Anda. Secara umum, saya akan menghindari ini dan membiarkan server memutuskan apa yang harus dilakukan, karena ia lebih tahu apa yang bisa dan tidak bisa dikompresi. Namun, terkadang Anda mungkin perlu memantau ini dari klien.
Satu-satunya cara yang saya temukan dalam penelitian API saya sampai saat ini adalah dengan menyiapkan saluran dengan mengirimkan sebuah contoh
GrpcChannelOptions. Salah satu properti kelas ini adalah untuk CompressionProviders- IList<ICompressionProvider>. Secara default, jika nilai ini null, implementasi klien secara otomatis menambahkan penyedia kompresi Gzip. Ini berarti server dapat menggunakan gzip untuk mengompresi pesan respons, seperti yang telah kita lihat.
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
Dalam contoh kode klien ini, kami menyetel
GrpcChanneldan mendorong instance baru GrpcChannelOptions. Kami sedang menetapkan CompressionProvidersdaftar kosong ke properti . Karena sekarang kami tidak menentukan penyedia di saluran kami saat panggilan dibuat dan dikirim melalui saluran itu, mereka tidak akan menyertakan pengkodean kompresi apa pun di header grpc-accept-encoding. Server melihat ini dan tidak meng-gzip responsnya.
Ringkasan
Dalam postingan ini, kami mempelajari kemungkinan mengompresi pesan respons dari server gRPC. Kami menemukan bahwa dalam beberapa (tetapi tidak semua) kasus, ini dapat menyebabkan muatan yang lebih kecil. Kita telah melihat bahwa secara default panggilan klien menyertakan nilai gzip "grpc-accept-encoding" di header. Jika server dikonfigurasi untuk menerapkan kompresi, itu hanya akan melakukannya jika jenis kompresi yang didukung cocok dengan header permintaan.
Kami dapat mengkonfigurasi
GrpcChannelOptionssaat membuat saluran untuk klien untuk menonaktifkan kompresi gzip. Di server, kita dapat mengkonfigurasi seluruh server sekaligus atau layanan terpisah untuk mengompresi respons. Kami juga dapat mengganti dan menonaktifkan ini di tingkat setiap metode layanan.
Untuk mempelajari lebih lanjut tentang gRPC, Anda dapat membaca semua artikel yang merupakan bagian dari mySeri gRPC dan ASP.NET Core .
SEMUA TENTANG KURSUS