Go Programming

Go ile gRPC Kullanarak Yüksek Performanslı Mikroservisler Oluşturma

Dağıtık sistemlerin modern manzarasında performans ve verlilik tartışılmazdır. Uygulamalar büyüdükçe, servisler arasındaki iletişim yükü önemli bir darboğaza dönüşebilir. İşte gRPC'nin parladığı yer burasıdır. Protokol Tamponları'nı (Protobuf) Arayüz Tanım Dili (IDL) ve taşıma katmanı olarak HTTP/2'yi kullanarak gRPC, geleneksel REST API'lerine güçlü, verimli ve kesin tipli bir alternatif sunar; bu da onu Go mikroservisleri için ideal bir seçenek haline getirir.

Neden Go için gRPC Seçmelisiniz?

Go, eşzamanlılık modeli ve standart kütüphanesiyle ünlüdür ve bu da onu altyapı backend'leri için en güçlü adaylardan biri yapar. gRPC ile eşleştirildiğinde geliştiriciler şunlara sahip olur:

  • Güçlü Tip Güvenliği: .proto dosyalarında tanımlanır, sözleşme istikrarını sağlar.
  • İkili Verimlilik: Protobuf serileştirmesi, JSON'a kıyasla önemli ölçüde daha küçük ve daha hızlıdır.
  • Yerel Akış Desteği: İstemci, sunucu ve çift yönlü akışlar için yerleşik destek.
  • Performans: HTTP/2 çoklu kullanımı bağlantı yükünü azaltır.

Hizmet Sözleşmesini Tanımlama

Herhangi bir gRPC hizmetinin temeli .proto dosyasıdır. Bu dosya veri yapılarını ve RPC yöntemlerini tanımlar. Basit bir Kullanıcı servisi için bir hizmet tanımlayalım.

1. Protobuf Tanımı

user.proto adında bir dosya oluşturun. Kullanıcı için bir mesaj yapısı ve bir hizmet arayüzü tanımlayacağız.

syntax = "proto3";

package user;

option go_package = "github.com/example/service/user";

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
}

service UserService {
  rpc GetUser (GetUserRequest) returns (User) {}
  rpc ListUsers (ListUsersRequest) returns (stream User) {}
}

message GetUserRequest {
  int32 id = 1;
}

message ListUsersRequest {
  string filter = 1;
}

ListUsers yöntemindeki stream User kullanımına dikkat edin. Bu, sunucunun tek bir istemci isteğine bir dizi yanıt gönderdiği sunucu taraflı akışı gösterir.

Kod Üretimi

.proto dosyanız hazır olduğunda, arayüzleri uygulayan Go kodunu oluşturmanız gerekir. protoc derleyicisine ve Go eklentilerinin yüklü olduğundan emin olun.

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

protoc --go_out=. --go-grpc_out=. user.proto

Bu komut iki dosya oluşturur: user.pb.go (mesaj yapılarını içeren) ve user_grpc.pb.go (istemci ve sunucu sınıflarını içeren).

Sunucuyu Uygulama

Şimdi, UserService arayüzünü Go'da uygulayalım. Oluşturulan arayüzü karşılayan bir sunucu oluşturacağız.

package main

import (
    "context"
    "log"
    "net"

    pb "github.com/example/service/user"
    "google.golang.org/grpc"
)

// server UserService'i uygular
type server struct {
    pb.UnimplementedUserServiceServer
}

func (s *server) GetUser(ctx context.Context, in *pb.GetUserRequest) (*pb.User, error) {
    // Sahte veri alma
    return &pb.User{
        Id:    in.Id,
        Name:  "John Doe",
        Email: "john@example.com",
    }, nil
}

func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
    // Sahte kullanıcı listesi
    users := []*pb.User{
        {Id: 1, Name: "Alice", Email: "alice@example.com"},
        {Id: 2, Name: "Bob", Email: "bob@example.com"},
    }
    
    for _, user := range users {
        if err := stream.Send(user); err != nil {
            return err
        }
    }
    return nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("dinleme başarısız: %v", err)
    }

    s := grpc.NewServer()
    pb.RegisterUserServiceServer(s, &server{})

    log.Println("Sunucu :50051 üzerinde dinliyor")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("servis başarısız: %v", err)
    }
}

İstemciyi Oluşturma

Sunucu çalışırken, bir istemci bağlanabilir ve RPC'leri çağırabilir. İşte GetUser ve ListUsers yöntemlerini nasıl çağıracağınız.

package main

import (
    "context"
    "log"

    pb "github.com/example/service/user"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

func main() {
    // Sunucuya bağlantı kurma
    conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf("bağlanamadı: %v", err)
    }
    defer conn.Close()

    c := pb.NewUserServiceClient(conn)

    // Tekli RPC çağrısı
    ctx, cancel := context.WithTimeout(context.Background(), 1000000)
    defer cancel()
    
    r, err := c.GetUser(ctx, &pb.GetUserRequest{Id: 1})
    if err != nil {
        log.Fatalf("kullanıcı alınamadı: %v", err)
    }
    log.Printf("Kullanıcı: %+v", r)

    // Sunucu akışlı RPC çağrısı
    stream, err := c.ListUsers(ctx, &pb.ListUsersRequest{Filter: "all"})
    if err != nil {
        log.Fatalf("kullanıcılar listelenemedi: %v", err)
    }
    
    for {
        msg, err := stream.Recv()
        if err == nil {
            log.Printf("Kullanıcı: %+v", msg)
        } else {
            break
        }
    }
}

Sonuç

gRPC'yi Go mikroservis mimarinize entegre etmek, ölçeklenebilir ve yüksek performanslı sistemler oluşturmak için güçlü bir araç sağlar. REST, halka açık API'ler için çok yönlü bir standart olarak kalmaya devam ederken, gRPC'nin verimliliği ve tip güvenliği, içsel servisler arası iletişim için üstündür. Protobuf tanımlarına hakim olarak ve Go'nun eşzamanlılık ilkelerinden yararlanarak geliştiriciler, ölçeklenme testlerine dayanabilecek sağlam dağıtık sistemler oluşturabilir.

Share: