Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

gRPC – Bi-directional Streaming Yöntemiyle Client ve Server İletişimi

Merhaba,

Bu içeriğimizde, iki uygulama arasında iletişimin gRPC üzerinden Bi-directional yöntemiyle nasıl sağlandığını pratikte inceliyor olacağız.

İlk olarak Bi-directional yönteminin ne olduğunu hatırlayarak başlayalım.

gRPC Nedir? Ne Amaçla ve Nasıl Kullanılır?

Client’ın server’a stream mesaj gönderdiği ve server’ın stream response döndürdüğü RPC türüdür. Yani hem client hem de server karşılıklı message streaming gerçekleştirmektedir ve böylece birden çok mesaj transferi sağlanabilmektedir. bknz: Duplex messaging

Bi-directional Streaming, önceden ele aldığımız Client Streaming ile Server Streaming‘in kombine edilmiş varyasyonu olarak çalışmakta ve her iki uygulamanın birbirine stream data gönderdiği bir yaklaşım sergilememizi sağlamaktadır. Şimdi gelin, bu yöntem ile bir client ve server çalışması sergileyelim.

Proto Dosyası

Client ve server olmak üzere iki uygulama arasındaki kontratı aşağıdaki message.proto dosyası üzerinden sağlayacağız.

syntax = "proto3";
option csharp_namespace = "gRPCExample";
package message;

service Message {
  rpc GetMessage(stream MessageRequest) returns (stream MessageResponse);
}
message MessageRequest {
  string message = 1;
}
message MessageResponse {
  string message = 1;
}

Yukarıdaki message.proto içeriğine göz atarsanız eğer içerisinde ‘GetMessage’ isimli fonksiyon barındıran ‘Message’ isimli bir servis oluşturulmaktadır. İlgili fonksiyon yapılacak request neticesinde bir ‘MessageRequest’ stream’i istemekte ve response olarak geriye ‘MessageResponse’ stream’i döndürmektedir. Yani gönderilen birden fazla ‘MessageRequest’ nesnesine karşılık yine birden fazla ‘MessageResponse’ nesnesini cevap olarak göndermektedir.

Server’da Message Service’i

Client’tan gelecek olan isteği server’da karşılayacak olan ‘MessageService’i aşağıdaki gibi tasarlayalım.

    public class MessageService : MessageBase
    {
        public async override Task GetMessage(IAsyncStreamReader<MessageRequest> requestStream, IServerStreamWriter<MessageResponse> responseStream, ServerCallContext context)
        {
            Task response = Task.Run(async () =>
            {
                int count = 0;
                while (++count <= 10)
                {
                    await responseStream.WriteAsync(new MessageResponse { Message = $"{count}. İstek alındı ve işlendi..." });
                    await Task.Delay(1000);
                }
            });

            Task request = Task.Run(async () =>
            {
                while (await requestStream.MoveNext())
                {
                    Console.WriteLine($"Mesaj alınmıştır.");
                    Console.WriteLine("Gelen mesaj : ");
                    Console.WriteLine(requestStream.Current.Message);
                }
            });

            await response;
            await request;
        }
    }

Yukarıdaki servise göz atarsak eğer 5. satırda istek neticesinde ‘MessageResponse’ stream’i gönderilmekte ve 15. satırda ise gelen stream data tüketilmektedir. Tabi burada asenkron bir süreç söz konusu olduğu için ilgili işlevlerin yer değiştirmesine gerek görülmemektedir. Ayrıca gRPC’de gelen request’e response döndürebilmek için illaki ilgili request’in neticelenmesini beklemeye gerek olmayacağından dolayı, request ile datalar gelirken bir yandan da response ile gönderilebilmektedir.

Client

İstek gönderecek client’ın aşağıdaki gibi geliştirilmesi yeterli olacaktır;

    class Program
    {
        static async Task Main(string[] args)
        {
            var channel = GrpcChannel.ForAddress("https://localhost:5001");
            var messageClient = new Message.MessageClient(channel);
            Console.WriteLine("Lütfen gönderilecek mesajı giriniz.");
            var getMessage = messageClient.GetMessage();

            Task request = Task.Run(async () =>
            {
                int count = 0;
                while (++count <= 10)
                {
                    await getMessage.RequestStream.WriteAsync(new MessageRequest { Message = $"Gönderilen mesaj {count}" });
                    await Task.Delay(1000);
                }
            });

            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            Task response = Task.Run(async () =>
            {
                while (await getMessage.ResponseStream.MoveNext(cancellationTokenSource.Token))
                    Console.WriteLine(getMessage.ResponseStream.Current.Message);
            });
            await request;
            //RequestStream başarıyla sonlandırılıyor. Özellikle request'ten sonra olmasına özen gösteriyoruz.
            await getMessage.RequestStream.CompleteAsync();
            await response;
        }
    }

Client uygulamasının kodlarına göz atarsanız eğer, 10. satırda istek ile birlikte gönderilecek stream data hazırlanırken, 21. satırda ise istek neticesinde eşzamansız bir şekilde gelen response stream data tüketilmektedir. Tabi burada CancellationToken kullanılarak asenkron sürecin rahatlıkla yönetilebildiğine dikkatinizi çekerim.

Test Edelim

gRPC - Bi-directional Streaming Yöntemiyle Client ve Server İletişimi
Görüldüğü üzere Bi-directional Streaming yöntemiyle client ve server arasında başarıyla iletişim kurulabilmekte ve yapılan stream request’e karşılık stream response elde edilebilmektedir.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar dilerim…

Not : Örnek projeleri indirebilmek için aşağıdaki adreslere tıklayınız.
gRPCClientExample
gRPCServerExample

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*