gRPC ile Streaming: Server, Client ve Bidirectional Streaming
gRPC, HTTP/2'nin çoklama (multiplexing) ve akış (streaming) yeteneklerini kullanarak, geleneksel istek-yanıt modelinin ötesine geçen dört iletişim deseni sunar. Streaming, büyük veri kümelerini parça parça taşımak, canlı veri akışı sağlamak ve gerçek zamanlı etkileşim kurmak için güçlü bir araçtır. Bu yazıda, üç streaming modelini ve .NET'teki uygulamalarını ele alacağız.
1. Dört İletişim Modeli
| Model | Açıklama | Kullanım Alanı |
|---|---|---|
| Unary | Klasik istek-yanıt | CRUD işlemleri |
| Server Streaming | İstemci bir istek gönderir; sunucu bir dizi yanıt döner | Canlı veri akışı, büyük veri kümeleri |
| Client Streaming | İstemci bir dizi istek gönderir; sunucu tek yanıt döner | Dosya yükleme, toplu veri gönderimi |
| Bidirectional Streaming | Her iki taraf da bağımsız olarak akış gönderir | Sohbet, oyun, gerçek zamanlı işbirliği |
2. Proto Tanımlamaları
Streaming, .proto dosyasında stream anahtar kelimesiyle tanımlanır:
protobuf
service StreamService {
rpc GetStream (Request) returns (stream Response); // Server streaming
rpc UploadStream (stream Request) returns (Response); // Client streaming
rpc Chat (stream Request) returns (stream Response); // Bidirectional
}
3. Server Streaming: Sunucudan Sürekli Akış
Sunucu, tek bir isteğe karşılık bir dizi yanıt döner. İstemci, yanıtları geldikçe işler.
Sunucu Tarafı (.NET):
csharp
public override async Task GetStream(
Request request,
IServerStreamWriter<Response> responseStream,
ServerCallContext context)
{
for (int i = 0; i < 10; i++)
{
// İptal kontrolü: istemci bağlantıyı kestiyse dur
if (context.CancellationToken.IsCancellationRequested)
break;
await responseStream.WriteAsync(new Response
{
Id = i,
Message = $"Mesaj {i}: {request.Message}"
});
await Task.Delay(500);
}
}
İstemci Tarafı (.NET):
csharp
using var call = client.GetStream(new Request { Message = "Merhaba" });
await foreach (var response in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Alındı: {response.Message}");
}
Kullanım Alanları: Canlı borsa verisi, log takibi, büyük veri kümelerinin parça parça transferi.
4. Client Streaming: İstemciden Toplu Akış
İstemci, bir dizi istek gönderir; sunucu hepsini aldıktan sonra tek bir yanıt döner.
Sunucu Tarafı (.NET):
csharp
public override async Task<Response> UploadStream(
IAsyncStreamReader<Request> requestStream,
ServerCallContext context)
{
int count = 0;
await foreach (var request in requestStream.ReadAllAsync())
{
count++;
// Her isteği işle (ör. dosya parçasını diske yaz)
}
return new Response { Message = $"Toplam {count} istek alındı." };
}
İstemci Tarafı (.NET):
csharp
using var call = client.UploadStream();
for (int i = 0; i < 10; i++)
{
await call.RequestStream.WriteAsync(new Request { Message = $"Parça {i}" });
}
await call.RequestStream.CompleteAsync();
var response = await call.ResponseAsync;
Kullanım Alanları: Dosya yükleme (chunk'lı), telemetri toplama, toplu veri gönderimi.
5. Bidirectional Streaming: Karşılıklı Etkileşim
Her iki taraf da bağımsız olarak akış gönderir ve alır. Gerçek zamanlı, etkileşimli uygulamalar için idealdir.
Sunucu Tarafı (.NET):
csharp
public override async Task Chat(
IAsyncStreamReader<Request> requestStream,
IServerStreamWriter<Response> responseStream,
ServerCallContext context)
{
await foreach (var request in requestStream.ReadAllAsync())
{
await responseStream.WriteAsync(new Response
{
Message = $"Echo: {request.Message}"
});
}
}
İstemci Tarafı (.NET):
csharp
using var call = client.Chat();
// Sunucudan gelenleri dinleyen görev
var readTask = Task.Run(async () =>
{
await foreach (var response in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Sunucu: {response.Message}");
}
});
// İstemciden sunucuya mesaj gönder
for (int i = 0; i < 5; i++)
{
await call.RequestStream.WriteAsync(new Request { Message = $"Mesaj {i}" });
await Task.Delay(300);
}
await call.RequestStream.CompleteAsync();
await readTask;
Kullanım Alanları: Sohbet uygulamaları, çok oyunculu oyunlar, gerçek zamanlı işbirliği araçları.
6. Streaming'de Dikkat Edilmesi Gerekenler
-
İptal (Cancellation): Uzun süren akışlarda
context.CancellationTokenkontrol edilmelidir; aksi halde kaynaklar boşa tüketilir. -
Akış Kontrolü (Flow Control): HTTP/2, akış kontrolü ile tampon taşmasını önler; ancak sunucu, istemciden hızlı veri gönderirse gecikme yaşanabilir.
-
Hata Yönetimi: Akış sırasında hata oluşursa
RpcExceptionfırlatılır ve akış sonlanır. -
Mesaj Boyutu: Varsayılan maksimum mesaj boyutu 4MB'dir; büyük mesajlar için
MaxReceiveMessageSizeayarlanmalıdır.
7. Tarayıcı Desteği: gRPC-Web Sınırlaması
gRPC-Web, tarayıcıdan gRPC'ye erişim sağlar; ancak client streaming ve bidirectional streaming desteklenmez. Yalnızca unary ve server streaming kullanılabilir. Bu nedenle, tarayıcı tabanlı gerçek zamanlı uygulamalarda WebSocket veya SignalR gibi alternatifler değerlendirilmelidir.
8. Ne Zaman Hangi Model?
| Senaryo | Öneri |
|---|---|
| Tek istek, tek yanıt | Unary |
| Sunucudan sürekli veri akışı | Server Streaming |
| Toplu veri gönderimi (dosya yükleme) | Client Streaming |
| Karşılıklı, gerçek zamanlı iletişim | Bidirectional Streaming |
| Tarayıcıdan gerçek zamanlı iletişim | WebSocket / SignalR (gRPC-Web sınırlı) |
Sonuç
gRPC streaming, modern dağıtık sistemlerde büyük veri transferi ve gerçek zamanlı iletişim için güçlü bir araçtır. Server streaming, sunucudan sürekli veri akışı; client streaming, toplu veri gönderimi; bidirectional streaming ise karşılıklı etkileşim için idealdir. .NET'te IAsyncEnumerable, IServerStreamWriter ve IAsyncStreamReader ile bu modeller kolayca uygulanır. Ancak tarayıcı desteğinin sınırlı olduğunu ve akış kontrolü, iptal yönetimi gibi konulara dikkat edilmesi gerektiğini unutmamak gerekir. Unutmayın: Streaming, "her şeyi tek seferde göndermek" yerine "doğru parçayı doğru zamanda göndermek" sanatıdır.