İçeriğe geç

gRPC ve Protobuf ile Yüksek Performanslı Uygulamalar Geliştirme

Günümüz dünyasında özellikle mikroservis teknolojisinin kullanımıyla adını daha sık duyduğumuz gRPC, Google tarafından geliştirilmiş olan açık kaynaklı ve HTTP tabanlı bir iletişim protokolüdür. Protobuf’ın desteğiyle beraber verileri efektif olarak serileştirir ve bu sayede hız ve bant genişliği tasarrufu sağlar. Şimdi, bu teknolojileri daha iyi anlayabilmek için detaylarına göz atalım.

gRPC nedir?

gRPC, günümüz dünyasında özellikle mikroservis alanında sıkça kullanılan yüksek performanslı RPC (Remote Procedure Call) çağırımları yapabilen bir framework’tür.

RPC genel bir kavram olup 1970’li yıllara dayanan bir geçmişi vardır. Bu teknolojinin temel fikri, bir RPC çağrısı yapıldığında uzak bir makinedeki fonksiyonları sanki aynı yerel bilgisayardan çağırıyormuş gibi çalıştırmasıdır. Bunun avantajı ise, isteğin karmaşık ağ katmanlarından geçmek zorunda kalmadan iletişimi basit bir şekilde yerel bilgisayardan çalıştırarak hızlıca işlemesidir.

gRPC ise Google’ın RPC teknolojisini yorumlamasıyla ortaya çıkardığı açık kaynaklı bir üründür. Her ne kadar dışarıya açılarak kullanılsa da özellikle mikroservis mimarilerinde servisler arasındaki iletişim yöntemi olarak tercih edilmesi ideal bir yaklaşımdır. HTTP/2 protokolü üzerinden çalıştığı için düşük gecikme süresi ve yüksek verimlilik sunar.

Neden gRPC kullanmalıyız?

Malumunuz servislerinizdeki iletişim protokolü seçeneklerinden biri de gRPC’dir. Son zamanlarda, özellikle mikroservis mimarilerinin artmasıyla beraber daha fazla tercih edilen bu teknolojinin neden kullanıldığını ve tercih sebeplerini incelemeye çalışalım.

gRPC; C# / .NET, Java, C++, Go, Node.js, Ruby gibi bir çok modern programlama dilleri tarafından desteklenmektedir. Bu programlama dillerinde Protobuf ile tanımlanan ortak modeller ile aracı kodlar otomatik olarak üretilir. Bu üretilen kodlara stub denir ve her model değiştiğinde kolaylıkla tekrar üretilebilir.

İletişim şekli binary olduğu için geleneksel REST API iletişim modeline göre daha hızlıdır. Mesajlar Protobuf ile serialize ve deserialize işlemiyle beraber binary formata dönüştürülürerek server ve client arasında düşük boyutlu iletişim sağlanır.

Kullandığı protokol HTTP/2 olması sebebiyle, HTTP/1.1’den daha düşük gecikmeli olarak veri transferi yapabilir. Mesaj boyutları küçük olduğu için hızlıca transfer işlemi gerçekleştirilir. Bu network üzerindeki yükü ve bandwith kullanımını azaltarak fayda sağlar.

Bir diğer özelliği ise streaming yapabilmesidir. Kullanıcılar büyük boyutlu verileri sunucudan isterken tek seferde almak zorunda kalmaz. Böylece beklemeden verilerin akışı sağlanarak istenilen veriler elde edilebilir.

Load balancer araçlarıyla uyumlu çalışır, tracing ve health check kullanarak sistemin sağlığı izlenebilir, authentication mekanizmalarıyla güvenli bir şekilde bu protokol kullanılabilir.

Protobuf (Protocol Buffers) nedir ve nasıl çalışır?

Protocol Buffers teriminin kısaltması olan Protobuf, ücretsiz ve açık kaynaklı olan, yapısal verileri dil bağımsız bir şekilde serileştirme yapabilmemize olanak tanıyan bir mekanizmadır. Bu teknoloji biz yazılım geliştiricilerine .proto formatında bir dosya ile veri yapısı tanımı yapmasını sağlayarak dahili yada harici uygulamalarla aynı veri yapısını kullanarak haberleşme olanağı sağlamaktadır.

Protobuf verileri binary formatta serialize eder ve dolayısıyla JSON gibi metin tabanlı formata göre daha küçük veri boyutu kullanır ve iletişimin performansı artmış olur.

Neden Protobuf?

Veri iletişiminde her zaman hız ve verimlilik önce gelir. Protobuf temel olarak bu alandaki ihtiyacı karşılamak üzere tasarlanmış bir yapıdadır. Özellikle mikroservis mimarilerinde yüksek performans gerektiren uygulamalar için güzel bir seçenektir.

Tercih sebeplerinden biri serileştirme hızıdır. Protobuf binary veri formatı kullandığı için JSON gibi metin tabanlı formata göre çok daha hızlı serialization ve de-searialization işlemi yapar. Daha düşük boyutlu olması sebebiyle ağ trafiğini azaltarak bant genişliği kullanımını da azaltır.

Diğer önemli bir sebebi ise veri bütünlüğünü korumaktır. Tip kontollü şema yapısı sayesinde hata yapıldığında, yanlış/eksik veri oluşturulduğunda daha kodu yazarken sizi uyararak sorunları daha erken tespit etmemize yarar.

Mevcut şema üzerinde güncellemeler yapılabilir, yeni alanlar kolaylıkla eklenebilir. Böylece eski .proto dosyasını kullanan sistemler bu değişikliklerden etkilenmez, geriye dönük uyumluluk sayesinde mesajları anlamaya devam ederler.

Programlama dili açısından bir çok desteği bulunmaktadır. Ve gRPC’de kullanılan temel bileşen olduğunu söyleyebiliriz. Bu yüzden bir çok senaryoda gRPC denildiğinde Protobuf akla gelir, yada tam tersi.

Protobuf nasıl çalışır?

Dosya oluşturulması: Protobuf’u temel olarak bir şablon dosyası şeklinde düşünebiliriz. Bu şablon dosyası .proto uzantısına sahiptir ve bu dosya içinde verimizin yapısını oluştururuz. Bu dosya ile uygulamalar arasında sağlanacak olan iletişimin kuralları belirlenmiş olur. Aşağıda örnek bir proto dosyasını görebilirsiniz. Buna blogpost.proto dosyası diyebiliriz.

syntax = "proto3";

message BlogPost {
  string title = 1;
  string author = 2;
  string content = 3;
  repeated string tags = 5;
}

Stub kodunun üretilmesi: Sonraki aşama bu .proto dosyasını seçtiğimiz yada kullandığımız programlama dilinde kullanmak olacaktır. Bunun için protoc aracının kullanılması gerekiyor (Bu aracı protoc installation yada latest releases sayfalarından edinebilirsiniz). Bu araç, oluşturulan proto dosyasını alır ve istemiş olduğunuz programlama dilinde auto-generated olarak kodlar üretir. Bu üretilen kodlara stub denir. Şimdi gelin bu proto ile aşağıdaki şekilde stub kodumuzu üretelim.

protoc --csharp_out=./output blogpost.proto

Proto dosyasının kullanılması: Üretilen stub kodunu kullandığınız programlama dilinin projesine ekleyerek kullanım sağlayabilirsiniz. Örnek olarak ben C# için Visual Studio’ya ekledim.

var post = new BlogPost()
{
    Title = "gRPC ve Protobuf Nedir?",
    Author = "Erhan Kocabuga",
    Content = "Bu blog yazısı gRPC ve Protobuf ile ilgilidir.",
    Tags = { "gRPC", "Protobuf", ".NET", "C#" }
};

Burada size manuel olarak .proto dosyası ile stub kodlarınızı üreteceğinizi göstermeye çalıştım. Ancak manuel kodları üretmenin yanında bu işlemleri kolaylıkla, otomatik olarak yapmanın bir yolu daha var. Bir çok modern IDE’nin artık Protobuf desteği var ya da eklentilerle bu sağlanabiliyor. Gerekli ayarlamaları yaptıktan sonra size sadece .proto dosyasını projeye eklemek kalıyor. Bunu da söylemiş olayım. Bununla ilgili bir örneği sonraki yazımızda yapmayı düşünüyorum.

Örnek başka bir protobuf dosyası

Şimdi gelin sizlerle beraber sık kullanılan veri türlerinin olduğu bir .proto dosyasına göz atalım.

syntax = "proto3";

import "google/protobuf/timestamp.proto";

package developerportal.userprofile.v1;

message User {
  int64 user_id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;
  bool is_active = 5;
  Status status = 6;
  UserSettings settings = 7;
  bytes profile_picture = 8;
  google.protobuf.Timestamp last_login = 9;
}

message UserSettings {
  bool notifications_enabled = 1; 
  string theme = 2; 
  string language = 3; 
}

enum Status {
  UNKNOWN = 0;
  ACTIVE = 1;
  SUSPENDED = 2;
  DEACTIVATED = 3;
}

Burada developerportal namespace’i altında oluşturduğumuz protobuf dosyası ile beraber bir User yapısı oluşturduk. Genel olarak baktığımızda .NET, Java veya diğer programlama dillerindeki model/class yapısına çok benzediğini fark etmişsinizdir.

Protobuf alan numaraları

İlk baktığınızda alanların yanındaki sıra numalarını fark edeceksinizdir. Bu numaralar .proto dosyasında serialization ve deserialization işlemlerinde önemli rol oynarlar.

Proto dosyaları uygulamalar arasında kullanılırken binary formatta dönüşüm yapılır. Dolayısıyla veri karşı tarafa giderken alan adıyla değil, bu alan numarasıyla gider. Karşı taraf bu veriyi deserialize ettiğinde, örneğin 3 numaralı verinin email olduğunu anlar. O yüzden bir kez bu alan numaraları değiştirilirken dikkatli olunmalıdır. Ardışık olması gerekli değildir, 1, 2, 3 yerine 1, 10, 30 gibi numaralar da verebilirsiniz, sadece numaların sabit kalması yeterli olacaktır.

Her message türünde bu alanlar tekrar 1’den başlarlar. 0 geçersiz veri anlamına geldiği için genelde enum türleri için belirsiz/geçersiz statü durumu için ilk elemanda 0 kullanılır.

Protobuf veri türleri

Genel string ve int alanların dışında diğer bir dikkatinizi çekecek alanlardan biri muhtemelen last_login’in veri türü olan google.protobuf.Timestamp olacaktır.

Bu biraz garip gelebilir ancak Protobuf üzerinde bir datetime veri türü yok. Biraz şaşırtıcı ancak bunun sebebi ise Protobuf’un minimalist bir yaklaşımı benimsemesi ve platform bağımsız olmasından kaynaklanıyor. Çünkü her programlama dilindeki datetime türü aynı olmuyor. Örneğin C#’da System.DateTime bulunurken, Java’da ZonedDateTime, LocalDate olması gibi. Bu noktada çözüm olarak istersek tarih ve saat verimizi unix epoch time’a çevirerek int64 veri türünde taşıyabilir ya da Google’ın protobuf datetime modeli olan google.protobuf.Timestamp türünü kullanarak tarih ve saat şeklinde kullanabiliriz.

Oluşturduğumuz bu proto modelini kullanarak veri gönderimi yapmak istediğimizde, doldurmadığımız field’lar kendi türlerine göre default değerleri alacaklardır. Varsayılan değerlere baktığımızda string alanlar için boş string (“”), numeric alanlar (int, float, double vb) için 0, boolean alanlar için false, enum türündeki alanlar için ilk tanımlanan değeri alırlar. Bu protobuf dosyamıdaki örnekte UNKNOWN = 0 değerini alırlar. Son olarak google.protobuf.Timestamp için ise varsayılan değer şudur: “1970-01-01T00:00:00Z”

Protobuf versiyonlama

Burada ayrıca paket adında versiyonlama yaptığımı da göreceksiniz. Her ne kadar alanların sayılarını artırarak yeni alanlar ekleyebilsek de geriye dönük uyumluluk ve modele hakimiyet bakımından versiyonlu çalışmanızı tavsiye ederim.

gRPC ve REST API’lar arasındaki farklar

Farklılıklara bakmadan önce gRPC ve REST teknolojisinin benzerliklerine baktığımızda şunları görürüz. İki teknoloji de client ve server arasında veri alış verişini sağlar ve ikisi de HTTP tabanlı teknolojilerdir. REST ve gRPC bir çok programlama dilinde kullanılabilir. Stateless yapıdadırlar ve asenkron iletişimi desteklerler.

Benzerliklerden sonra gRPC ve REST arasındaki farkları listeleyelim:

  • Her iki teknoloji HTTP tabanlı demiştik buna karşın gRPC HTTP/2 kullanırken REST HTTP/1.1 kullanır. REST, genellikle verileri JSON veya XML kullanarak serialization yaparken, gRPC varsayılan olarak Protocol Buffers (Protobuf) kullanır. Ancak diğer serileştirme formatlarını da destekler.
  • REST tek yönlü olarak client-server iletişimi kullanırken gRPC hem tek yönlü hem de çift yönlü iletişimi destekler. Çift yönlü iletişimden kasıt streaming desteğidir. HTTP/2 protokolü ile client veya server aynı anda veri gönderimini normal veya stream olarak gönderebilir.
  • REST metin bazlı bir iletişim sağlarken, gRPC binary formatı sayesinde düşük bant genişliği kllanarak daha hızlı bir iletişim sağlar.
  • gRPC ile Protobuf kullanılarak belirli bir formatta veri gönderildiği için type safety bir yapı oluşmuş olurken, REST’de bu tür kısıtlamaları daha esnek ve hataya açıktır.
  • REST teknolojisi HTTP protokolünü ve JSON veri standardını desteklediği için web tarayıcılardan kolaylıkla tüketilebilirken, gRPC tarafında genellikle bir ara katman veya proxy üzerinden tarayıcılara hizmet verebilir. Bknz: gRPC-Web
  • Öğrenme eğrisi bakımından REST API kullanımı, gRPC ve Protobuf’a göre daha kolaydır.

Evet bu yazımda gRPC ve Protobuf’ı kullandığım kadarıyla yüzeysel olarak işlemeye çalıştım 😊

Görmüş olduğumuz gibi, gRPC ve Protobuf yazılım dünyasında güçlü bir ikili ve gün geçtikçe adını daha fazla duyacağımıza benziyor. Özellikle mikroservis dünyasında kullanıldığında REST’e göre daha performanslı uygulamalar yazabilmemize olanak sağlıyor. Bu teknolojiyi deneyimlemek ve başlangıç olarak bir Hello-World uygulaması yapmanızı öneririm. Şimdiden bol şans, sonraki yazımızda görüşmek üzere 😊

Kaynaklar

İlk Yorumu Siz Yapın

    Bir yanıt yazın

    E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir