gRPC, Google tarafından geliştirilen, açık kaynaklı uzaktan prosedür çağırma (rpc) kütüphanesi (sistemi)'dir. Aktarım için HTTP/2
kullanır. Arayüz tanımlama dili olarak protokol tamponları (buffers)
kullanır. Kimlik doğrulama, Çift yönlü akış, engelleme ve engelleme olmayan bağlantılar, iptal ve zaman aşımı işlemleri için kullanılır.
JSON ve HTTP API'larının aksine, daha katı bir spesifikasyona sahiptirler. Tek bir spesifikasyona sahip olduğu için, gRPC tartışmalarını ortadan kaldırır ve geliştiriciye zaman kazandırır. Çünkü gRPC, platformlar ve uygulamalar arasında tutarlıdır.
Özet olarak, uzaktan prosedür çağırarak client (müşteri) ve server (sunucu) programları arasındaki iletişimi sağlayan bir kütüphanedir.
İki uygulama arasındaki iletişimin nasıl olacağını belirleyen sözleşmedir.
Bu örneğimizde sunucuya mesaj gönderip, karşılığında mesaj alan bir uygulama yazacağız. Bir nevi chat uygulaması olacak.
Öncelikle, iletişim protokülünü Go koduna dönüştürebilmemiz (generating) için protoc
'yi bilgisayarımıza kuralım.
İndirmek için buradaki adrese gidin. Buradaki versiyon v3.13.0 versiyonu. Daha güncel versiyonlar için buradaki sayfayı kontrol etmeyi unutmayın.
GNU/Linux İS kullanan arkadaşlarımız uygulama depolarından protobuf adıyla yükleyebilirler. Böylece PATH olarak eklemelerine gerek kalmaz.
Eğer GitHub sayfasından indirdiyseniz, sisteminize dosya yolunu tanıtmanız gerekir. Bunun için örnek olarak;
Örnek olarak protoc.exe
dosyamız C:\\protoc\bin
klasörü içerisinde olsun.
Başlar menüsünde "Ortam değişkenleri" yazarak aratalım. Açılan pencerede Gelişmiş sekmesinde alt tarafta Ortam Değişkenleri butonuna basalım.
PATH seçeneğini Düzenle diyerek protoc.exe
'nin konumunu ekleyelim.
protoc
çalıştırılabilir dosyamızı örnek olarak Home (Ev)
dizinine atmış olalım.
Örnek: ~/protoc/bin
olsun.
Ev dizinimizdeki .bashrc
dosyamızın en altına şunları ekleyelim.
export PATH="~/protoc/bin:$PATH"
.bashrc
dosyasını kaydettikten sonra komut satırına source ~/.bashrc
yazarak dosyada yaptığımız değişimi onaylayalım.
Komut satırına aşağıdakileri yazarak protoc
'nin versiyonuna bakalım.
protoc --version
Protoc
'ye Golang desteğini eklemek için aşağıdaki paketleri kuralım.
go get google.golang.org/protobuf/cmd/protoc-gen-go
go get google.golang.org/protobuf/cmd/protoc-gen-go-grpc
Aynı şekilde Windows üzerinde C:\\Users\isim\go\bin
klasörünü ekli değilse ortam değişkenlerine ekleyelim. Unix-like sistemler için ~/go/bin
'i path'e ekleyelim.
Yukarıda belirttiğimiz örnek chat
uygulamasını yazmak için aşağıdaki gibi bir proje yapımız olacak.
.
├── chat
│ └── chat.go
├── chat.proto
├── client.go
└── server.go
Client ve Server arasındaki iletişim protokolünü belirleyelim.
chat.proto
dosyamız şöyle olsun.
//Söz dizimi için proto3'ü kullanacağız
syntax = "proto3";
//Go paketi için ayarlarımız
package chat;
option go_package=".;chat";
//iletişimde kullanılacak mesajın yapısı
message Message {
string body = 1;
//buradaki 1 sıra numarasıdır. Değer ataması değildir.
}
//rpc servisimiz
service ChatService {
rpc SayHello(Message) returns (Message) {}
}
Yukarıdaki chat.proto
dosyasını Go koduna dönüştürelim. Bunun için proje dizinindeyken aşağıdaki komutu yazalım.
protoc --go_out=plugins=grpc:chat chat.proto
Bu işlem sonucunda chat klasörümüzüm içerisinde chat.pb.go
dosyamız oluşacak. Bu dosyanın en başında yorum olarak düzenleme yapmamızın uygun olamayacağı yazıyor. O yüzden bu dosyayla çok uğraşmamakta fayda var.
chat
klasörümüzdeki chat.go
dosyasını oluşturalım.
package chat
import (
"log"
"golang.org/x/net/context"
)
type Server struct {
//Burası şuanlık boş olacak
}
//server nesnemize iliştirilecek fonksiyonumuz
func (s *Server) SayHello(ctx context.Context, message *Message) (*Message, error) {
//Client'ten gelen mesajı ekrana bastıralım
log.Printf("Clientten mesaj alındı: %s", message.Body)
//mesaj gelince değer döndürelim (mesaj ve hata)
return &Message{Body: "Server'dan merhaba!"}, nil //hata nil olsun
}
Go modules'tan faydalanarak chat paketini diğer paketlerde kullanabiliriz. Bunun için proje dizinindeyken komut satırına:
go mod init github.com/ksckaan1/grpcOrnek
yazalım. Buradaki yaptığımız şey, GitHub'a paket yüklemeden sanal olarak yazdığımız chat
paketini kullanabileceğiz. Bu komuttan sonra proje dizinimizde go.mod
dosyası oluşacak. Böylece chat klasörünü paket olarak diğer yerlerde de kullanabileceğiz.
server.go
dosyamızı oluşturalım.
package main
import (
"log"
"net"
"github.com/ksckaan1/grpcExample/chat"
"google.golang.org/grpc"
)
func main() {
//tcp dinleyelim
lis, err := net.Listen("tcp", ":9080")
//hata kontrolü yapalım
if err != nil {
log.Fatalf("9080 portu dinlenirken hata oluştu: %v \n", err)
}
//chat server nesnesi oluşturalım
s := chat.Server{}
//grpc server'ı oluşturalım.
grpcServer := grpc.NewServer()
//tcp sunucu ile grpc'yi bağlayalım.
chat.RegisterChatServiceServer(grpcServer, &s)
//hata kontrolü
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("grpcServer oluşturulurken hata : %v", err)
}
}
go run server.go
komutunu yazarak test edelim. Eğer çıktı vermiyorsa ve program kapanmıyorsa server dinleniyor demektir. Yani şuana kadar başarılıyız.
Şimdi de Server'a mesaj yollayabilmek için Client'i oluşturalım. client.go
dosyamız aşağıdaki gibi olsun.
package main
import (
"log"
//chat klasörünü kullanacağımız için
//go mod init üzerinde belirlediğimiz deponun
//sonuna /chat ekliyoruz.
"github.com/ksckaan1/grpcExample/chat"
"golang.org/x/net/context"
//grpc kütüphanemiz
"google.golang.org/grpc"
)
func main() {
//grpc client bağlantı nesnesi
var conn *grpc.ClientConn
//grpc ye 9080 portunu dinlemesini söyleyelim
conn, err := grpc.Dial(":9080", grpc.WithInsecure())
if err != nil {
log.Fatalf("9080 portu dinlenirken hata oluştu: %v", err)
}
//bağlantığı kapatmayı unutmayalım
defer conn.Close()
//Client nesnesi oluşturalım
c := chat.NewChatServiceClient(conn)
//server'a gönderilecek mesajı belirleyelim
message := chat.Message{
Body: "Client'ten merhabalar!",
}
//Server'dan dönen cevabı alalım
response, err := c.SayHello(context.Background(), &message)
//hata kontrolü yapalım.
if err != nil {
log.Fatalf("SayHello fonksiyonu çağırılıken hata oluştu: %v", err)
}
//server'dan gelen mesajı ekrana bastıralım
log.Printf("Server'dan gelen cevap: %s", response.Body)
}
Yukarıda yazdığımız kodların çalışma mantığını açıklayacak olursak:
- Server
9080
portunu belirlediğimiz protokol sözleşmesine (chat.proto) göre dinliyor. Client.go
dosyamız çalıştırılınca server'a mesaj yolluyor.- Client'ten gelen mesajı server ekrana bastırıyor ve bunun karşılığında server client'e cevap veriyor.
- Server'dan gelen cevabı da client ekrana bastırıyor.
- Son olarak client programımız sonlanıyor (kapanıyor).
Gif olarak sonucu göstermek gerekirse: