top of page

gRPC Nedir ? Protobuf Nedir ? .Net uygulamalarımızda gRPC kullanımı

Güncelleme tarihi: 16 Ara 2021

Herkese Merhaba, Bu makalemde gRPC ye biraz genel anlamda birazda kullanım anlamında girmek istiyorum.


Bildiğiniz üzere gRPC teknolojisi artık çoğu şirket tarafından kullanılmaya başlandı, çıkış noktası olarak RPC (Remote Procedure Call) ve Google ın yeni nesil he hızlı bir protocol'de çalışması üzerine doğmuştur.


RPC genel itibari ile, Uzak yordam çağrısı üzerinde programı kodlama olmadan çalıştırmak için bir alt yordam veya prosedürü sağlayan süreçler arası iletişim teknolojisidir.


RPC ile sanki uzak sunucu lokalimizdeymiş gibi düşünebiliriz.


gRPC; Bir RPC teknolojisidir. Google’ın geliştirdiği Remote Procedure Call, yani başka bir servis ya da uzak sunucudaki bir metodu sanki kendi servisimizin metoduymuş gibi kullanabilmemize olanak sağlayan, client-server ilişkisindeki iletişimi kolay ve hızlıca sunan bir frameworktür.


Mikro servisler arttı ve istekler taleplerde aynı ölçüde arttı, şirketler kendi mikro servis yapılarını kurarken hız, güvenlik, işlem doğruluğu gibi konularda oldukça seçici davranmaktadırlar. En azından ben öyle düşünüyorum.


https://grpc.io/ gRPC nin resmi sitesidir, buradan güncel ve size gereken bilgiyi alabilirsiniz.


gRPC yi ben kendi şahsi bir uygulamamda kullandım. Buradaki amacım MongoDB ve ElasticSearch de olan büyük datanın, Client'lara en hızlı şekilde ve kesintisiz erişimin sağlanması oldu.


Projemin Service mimarisi aşağıdaki gibidir.

Projenin detaylarını anlatmayacağım ama bu proje kapsamında gRPC den çok fazla yararlandığımı söyleyebilirim. Özellikle BiDirectional Streaming konusunda bu projede oldukça fazla yararlandım.


Çok uzatmadan gRPC konusunu dönelim. Aşağıdaki başlıklar üzerinden giriş ve orta seviye bir yazı paylaşmaya çalışacağım.

  1. gRPC Hakkında.

  2. gRPC özellikleri ve avantajları.

  3. gRPC yapısı ve akışı.

  4. gRPC & WebApi karşılaştırması.

  5. Protocol Buffers (Protobuf).

  6. Protobuf versiyonlama.

  7. HTTP/2

  8. BloomRPC Tool.

  9. .Net & gRPC Implementation


gRPC HAKKINDA


gRPC, herhangi bir ortamda çalışabilen modern bir açık kaynaklı yüksek performanslı Uzaktan Yordam Çağrısı kütüphanesidir. Yük dengeleme, izleme, sistem durumu denetimi ve kimlik doğrulama için oldukça kolay şekilde implementasyon sağlar, client ve server arasındaki iletişimi verimli bir şekilde bağlayabilir. mobil ve web uygulamalarında performanslı ve kolay bir şekilde kullanılabilir.

  • Hızlı şekilde yeni servisler tanımlayabiliriz.

  • Ölçeklendirme işlemlerini sonradan da kolayca yapabiliriz.

  • Hemen Hemen tüm dillerde desteği vardır ve uygulanabilir.

  • Çift yönlü akış verilerinde en çok kullanılması veya tercih edilmesi gereken teknolojilerden birisidir.

Şu anda Netflix ve Cisco kendi projelerinde gRPC teknolojisini sıklıkla kullandılarını söyleyebiliriz.


gRPC ÖZELLİKLERİ VE AVANTAJLARI


gRPC performanslıdır: gRPC request-response mesajlarını, binary formatta Protobuf kullanılarak seriliaze eder.


Mesajlar HTTP 2.0’ın hızıyla birleşince, oldukça performanslı bir iletişim altyapısı sağlanmış olur

.

Aynı zamanda Multiplexing Tek bir TCP bağlantısı üzerinden birden çok HTTP 2.0 isteğinin çoklanması büyük bir avantaj sağlar.


gRPC kanalı tek bir HTTP/2 bağlantısı kullanır ve bu bağlantıda eş zamanlı çağrılar birden çok kez çağrılır.


Etkin çağrı sayısı bağlantı akışı sınırına ulaştığında istemcide ek çağrılar kuyruğa eklenir.

Kuyruğa alınan istekler gönderilmeden önce geçmiş istekler tamamlanır. Yüksek yüke veya uzun süre çalışan istekler, Kuyruğa alma çağrılarının neden olduğu performans sorunlarını görebilir. Mimariyi düzgün şekilde kurar isek bu sorun olmaktan çıkar. Örneğin ben yukarıdaki projemde 2 farklı gRPC Api ayağa kaldırdım bunun nedeni GET ve POST işlemlerinde farklı yol izlemem oldu.


gRPC servislerinde micro protobuf lar şeklinde düşünmekte fayda görüyorum. en azından bir gRPC projesinde 20, 30 adet protobuf olmaması gerektiğini düşünüyorum.


gRPC YAPISI VE AKIŞI


gRPC servislerde 4 farklı şekilde veri bağlama protokolü vardır. Aşağıda Demo da da gösterdiğim 4 Farklı kullanımı görebilirsiniz.

UNARY: Rest Client larda olduğu gibi Request karşılığında serverdan gelen response şeklinde bir davranışı vardır.

rpc GetMoviesById(MoviewRequestModel) returns (MovieResponseModel); 

SERVER-SIDE STREAMING : Bir client’ın istek attıktan sonra isteğine yanıt olarak serverın stream başlatması ve istek atılan tüm mesajların client’a aktarılmasını sağlar.

Örnek olarak bir siparişin tüm durumlarını periyodik şekilde Client a gönderilmesini söyleyebiliriz.

rpc GetMoviesFirst(google.protobuf.Empty) returns (stream MovieResponseModel);

CLIENT-SIDE STREAMING : Client bir stream açarak tüm mesajları server’a gönderir, mesaj gönderimi bitince server tek bir mesaj halinde cevap döner.

Örnek olarak Client tarafından Sunucuya dosya yüklemeyi söyleyebiliriz.

rpc SetMovies(stream MoviewRequestModel) returns (MovieListModel);

BI-DIRECTIONAL STREAMING : Çift yönlü streaming özelliği; client ve server birbirlerine tek bir mesaj ya da stream açabilir.

İki streamde birbirinden bağımsız işler. Client ve server mesajları herhangi bir sırayla okuyabilir ve yazabilir.

İstemci ve Sunucu, mesajları tek bir TCP bağlantısı üzerinden paylaşır. örnek olarak, bir sohbet uygulamasını verebiliriz.

Tabi verdiğimiz bu örnekler ilk akla gelen örnekler kendi sürecinizde bu yapılara uygun modeli ve senaryoyu oluşturmalısınız.

rpc SetGetMovies(stream MoviewRequestModel) returns (stream MovieResponseModel);

Ayrıca gRPC servislerinde LİSTE şeklinde mesaj döndüğümüz bir örneği de demo içerisinde işledim.

message MovieListModel {
  repeated MovieResponseModel movies = 1;
}

gRPC & WEBAPI KARŞILAŞTIRMASI


Bu konuda her iki teknolojiyide kullandığımız için belirgin farkları hemen gözlemleyebiliyoruz.

Örneğin WebApi ile JSON haberleşmesi yaparken gRPC de Protobuflar ile datalarımızı transfer ediyoruz.

gRPC bize Client ve Server arasında stream açarak karşılıklı veri alış verişine olanak sağlamaktadır.

Yukarıda gRPC ile WebApi net şekilde karşılaştırılmış bunları dikkatli okuyarak ve anlayarak teknoloji seçimi yapmakta fayda var.


PROTOCOL BUFFERS (PROTOBUF)


Protocol buffers (Protobuf), verileri serileştirmek için Google'ın dilden bağımsız, platformdan bağımsız, genişletilebilir mekanizmasıdır.


Veri akışlarını istediğinizi gibi tanımlarsınız, ardından tanımlanmış verilerinizi çeşitli diller kullanarak kolayca implementasyon yapabilirsiniz.


Aşağıda örnek bir protobuf dosyasını paylaşıyorum.


syntax = "proto3"; //Kotlin, Dart, Go, Ruby ve C# da proto3 kullanılıyor. farklı dil seçenekleride önümüzdeki dönemde gelecektir.

option csharp_namespace = "MyGRPC"; //Servisin genel adı Client larda bu isim kullanarak kütüphane şeklinde kullanım söz konusudur.
import "google/protobuf/empty.proto"; //google ın değişken tipleri kütüphanesinden ekmele yapabilir ve kullanabiliriz.

package V1; // Bu isim önemli loglamak ve versiyonlamak için kullanılır

service Movies{ //Servis main işlemlerimizi yaptığımız kısımdır.
	rpc GetMoviesById(MoviewRequestModel) returns (MovieResponseModel); //Unary
	rpc GetMoviesFirst(google.protobuf.Empty) returns (stream MovieResponseModel); //Server Streaming
	rpc GetCategoryMovies(stream CategoryMoviewRequestModel) returns (stream MovieListModel); //Client Streaming
	rpc StartBiStreaming(stream BiStreamModel) returns (stream BiStreamModel); // Bi Directional Streaming
}

NOT : google.protobuf.Empty aslında çok fazla kullanıyorum, Protobuf servis methotlarında parametresiz geçemezsiniz. Eğer client tarafından bir parametre istenmiyor isek, Empty şeklinde geçmelisiniz. !

message MoviewRequestModel{
	int32 id = 1;
}
message CategoryMoviewRequestModel{ //Request ve Response Modeller message olarak tanımlanır.
	int32 categoryId = 1;
}
message MovieListModel {
  repeated MovieResponseModel person = 1; //Eğer bir liste işlemi yapacak isek repeated kullanmalıyız.
}

message MovieResponseModel{
	int32  id = 1 ; //Sıralamalar önemli, değişkenler sıralı şekilde tanımlanmalıdır.
	int32 categoryId =2;
	string code =3;
	string description =4;
	int64 rating = 5;
}




PROTOBUF VERSİYONLAMA


Bu konu hakkında bir kaç önemli bilgi vermek istiyorum.


Bir gRPC uygulaması yazdık ve artık elimizde bir Stream Server bulunmakta. Deploy işlemlerinden sonra artık bir endpoint' den tüketimi sağlanabilir düzeyde.


Oluşturduğumuz protobuf dosyalarını Client larla paylaştık bir dizi eğitimden sonra sisteme entegre olmalarını sağladık.


Sonrasında ihtiyaç veya gereksinimden ötürü protobuf dosyalarında bazı değişiklikler yapmaya karar verdik. Yeni bir versiyon çıkmak istediğimizde Client ın etkilendiği senaryolar ve etkilenmediği senaryolar olabilir.


Client'ın etkilenmediği durumlar.

  • Protobuf dosyasına yeni bir servis eklediğimizde.

  • Protobuf dosyaları içerisindeki message (response-request model) ler içerisine yeni field eklediğimizde.

Client'ın etkilendiği durumlar.

  • Protobuf dosyasından servisler silindiğinde.

  • Message içerisindeki field lar silindiğinde.

  • Message içerisindeki field lar değiştirildiğinde

  • Message adını değiştirdiğimizde.

Bu durumlara dikkat ederek canlıya yayın çıkma ve mutabık kalma açısından önlemlerimizi almalıyız.


HTTP/2


HTTP/2 (orijinal ismi HTTP/2.0) Dünya Çapında Ağ Birliği (Wold Wide Web ) tarafından kullanılan HTTP ağ protokolünün ikinci ana sürümüdür.


http/2 ile birlikte hayatımıza özellikle hız anlamında bir çok özellik girmektedir.

  • Client ve Server arasındaki iletişim Binary formatta daha küçük Frame'lere ayrılması. (Binary Protocol)

  • Tek TCP bağlantısı ile birden çok paralel request ve response özelliği. (Multiplaxing)

  • HPACK ile mesajların sıkıştırılması. (Header Compression)

  • Bir request e karşılık birden fazla response. (Server Push)

Yukarıdaki resmi incelediğimizde HTTP/1.X de her bir request bir response olarak geri dönüyor. Fakat HTTP/2 ye baktığımında bir request e karşılık response parçalanmış şekilde döndürülmektedir.


Http/2 nin Http/1.1 den hız anlamındaki farkını gösteren bir site var aşağıda linkini bulabilirsiniz. http://www.http2demo.io/


HTTP/2 hakkında tüm güncel bilgileri wikipedia sayfasından erişebilirsiniz. https://tr.wikipedia.org/wiki/HTTP/2


BloomRPC TOOL


gRPC servislerini ve protobuf ları test edebilmemiz açısından güzel bir tool olduğunu düşünüyorum. POSTMAN mantığında çalışıyor diyebilirim.




.NET gRPC IMPLEMENTATION


Son Olarak .Net projelerimizde gRPC servis yazarken aşağıdaki kurallara dikkat edilmesi gerekmektedir.


Yeni eklediğimiz servisleri Startup.cs içerisinde Configure altına eklememiz gerekmektedir.


Eğer eklemeyi unutur isek aşağıdaki gibi bir hata alabiliriz.


.Net projemizde indirmemiz gereken bazı paketler vardır.


Yukarıda paylaşmış olduğum protobuf ve servis ler için yazılmış .Net kodları aşağıdaki gibidir.


 public class MoviesClients
    {
        private readonly GrpcChannel channel;
        public MoviesClients()
        {
            channel = GrpcChannel.ForAddress("http://localhost:5000");
        }

        /// <summary>
        /// Unary
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<MovieResponseModel> GetMoviesById(int Id)
        {
            var client = new Movies.MoviesClient(channel);

            var input = new MoviewRequestModel
            {
                Id = Id
            };

            MovieResponseModel reply = await client.GetMoviesByIdAsync(input);
            return reply;
        }

        /// <summary>
        /// Server Streaming
        /// </summary>
        /// <returns></returns>
        public async Task<List<MovieResponseModel>> GetMovieServerStreaming()
        {
            var client = new Movies.MoviesClient(channel);

            List<MovieResponseModel> responseList = new List<MovieResponseModel>();
            var tokenSource = new CancellationTokenSource();

            int i = 1;
            try
            {
                using (var call = client.GetMoviesFirst(new Empty()))
                {
                    while (await call.ResponseStream.MoveNext(tokenSource.Token))
                    {
                        var current = call.ResponseStream.Current;
                        Console.WriteLine("Server Streaming...");
                        responseList.Add(current);

                        if (i == 3)
                            tokenSource.Cancel();

                        i++;
                    }
                }
            }
            catch (RpcException e) when (e.Status.StatusCode == StatusCode.Cancelled)
            {
                Console.WriteLine("Server bağlantısı kesildi. (Unavailable) - " + e.Message);
            }

            return responseList;
        }

        /// <summary>
        /// Server Streaming List response
        /// </summary>
        /// <returns></returns>
        public async Task<List<MovieResponseModel>> GetMovies()
        {
            var client = new Movies.MoviesClient(channel);

            List<MovieResponseModel> responseList = new List<MovieResponseModel>();

            try
            {
                using (var call = client.GetMovies(new Empty()))
                {
                    while (await call.ResponseStream.MoveNext())
                    {
                        var current = call.ResponseStream.Current.Movies;
                        responseList = current.ToList();
                    }
                }
            }
            catch (RpcException e) when (e.Status.StatusCode == StatusCode.Cancelled)
            {
                Console.WriteLine("Server Cancelled - " + e.Message);
            }

            return responseList;
        }

        /// <summary>
        /// Client Streaming
        /// </summary>
        public async Task<List<MovieResponseModel>> SetMovies()
        {
            List<MovieResponseModel> response = new List<MovieResponseModel>();

            var client = new Movies.MoviesClient(channel);

            try
            {
                //var setMovie = client.SetMovies(deadline: DateTime.UtcNow.AddSeconds(9)); //set deadline time.
                var setMovie = client.SetMovies(deadline: null);

                for (int i = 1; i < 11; i++)
                {
                    Thread.Sleep(1000);
                    await setMovie.RequestStream.WriteAsync(new MoviewRequestModel
                    {
                        Id = i
                    });
                    Console.WriteLine(i + " Client Streaming");
                }

                await setMovie.RequestStream.CompleteAsync();
                var res = await setMovie;

                response = res.Movies.ToList();
            }
            catch (RpcException e) when (e.StatusCode == StatusCode.DeadlineExceeded)
            {
                Console.WriteLine("DeadLine exception. - " + e.Message);
            }

            return response;
        }

        /// <summary>
        /// Bi-Directional Streaming
        /// </summary>
        public async Task<MovieResponseModel> SetGetMovies(int Id)
        {
            MovieResponseModel responses = new MovieResponseModel();

            var client = new Movies.MoviesClient(channel);

            try
            {
                var setgetMessage = client.SetGetMovies();

                await Task.Run(async () =>
                {
                    await setgetMessage.RequestStream.WriteAsync(new MoviewRequestModel { Id = Id });
                });

                await setgetMessage.RequestStream.CompleteAsync();

                await Task.Run(async () =>
                {
                    while (await setgetMessage.ResponseStream.MoveNext())
                        responses = setgetMessage.ResponseStream.Current;
                });
            }

            catch (RpcException e)
            {
                Console.WriteLine(e.Message);
            }

            return responses;
        }
    }

C# & gRPC demosunda bu konu ile ilgili konulara fazlasıyla değindim.


Şimdilik bu kadar umarım faydalı bir kaynak olmuştur.


Demo projesinin kodlarına Github üzerinden erişebilirsiniz.



































374 görüntüleme0 yorum

Son Yazılar

Hepsini Gör

Comments


bottom of page