Go ve gRPC ile Yuksek Performansli Mikroservisler: 2026 Rehberi ve Mulakat Sorulari

Go ve gRPC ile yuksek performansli mikroservis gelistirme rehberi. Protocol Buffers, unary ve streaming RPC'ler, interceptor'lar, uretim ortami desenleri ve backend mulakat sorulari.

Go gRPC mikroservisler illustrasyonu: Protocol Buffers ile yuksek performansli sunucu iletisimi

gRPC, düşük gecikme süresi ve katı API sözleşmeleri gerektiren Go mikroservisleri için varsayılan iletişim katmanı haline gelmiştir. grpc-go v1.81, Go 1.26 ve go-grpc-middleware ekosisteminin olgunlaşmasıyla birlikte, Go'da üretim kalitesinde gRPC servisleri geliştirmek her zamankinden daha kolaydır. Aynı zamanda gRPC, backend mühendislik mülakatlarında en sık test edilen konulardan biri olmaya devam etmektedir.

gRPC ve REST Karsilastirmasi

gRPC, ikili serileştirme için HTTP/2 ve Protocol Buffers kullanmaktadır. Bu sayede 10 kata kadar daha küçük veri paketleri ve yerel çift yönlü akış desteği sunmaktadır. REST, genel kullanıma açık API'ler için daha uygun bir seçenek olmaya devam etmektedir; gRPC ise performans ve tip güvenliğinin önemli olduğu servisler arası iletişimde öne çıkmaktadır.

gRPC Neden Go Backend Iletisiminde Baskin Konumda

Uç temel özellik, gRPC'yi Go mikroservisleri için doğal tercih haline getirmektedir. Birincisi, Protocol Buffers derleme zamanında güçlü tipli Go kodu üretmektedir ve sözleşme uyumsuzluklarını dağıtımdan önce yakalamaktadır. İkincisi, HTTP/2 çoklama (multiplexing) özelliği head-of-line blocking sorununu ortadan kaldırmakta ve tek bir TCP bağlantısı üzerinden tam çift yönlü akış desteklemektedir. Üçüncüsü, interceptor modeli Go'nun kalıtım yerine kompozisyon felsefesine temiz bir şekilde uyum sağlamakta ve kimlik doğrulama, izleme gibi kesişen ilgilerin (cross-cutting concerns) birleştirilebilir olmasını mümkün kılmaktadır.

Go'daki gRPC ekosistemi, savaşta test edilmiş birkaç kütüphane etrafında konsolidasyona ulaşmıştır. grpc-go çekirdek taşıma katmanı ve kod üretimini yönetmektedir. go-grpc-middleware v2 paketi; günlükleme, metrik, kimlik doğrulama ve kurtarma için üretim düzeyinde interceptor'lar sağlamaktadır. OpenTelemetry'nin gRPC stats handler'ı ise özel interceptor kodu yazmaya gerek kalmadan dağıtık izlemeyi kapsamaktadır.

Protocol Buffers ile Servis Tanimlama

Her gRPC servisi bir .proto dosyasıyla başlamaktadır. Şema; servis sözleşmesini, mesaj tiplerini ve RPC metotlarını tanımlamaktadır. Aşağıdaki örnek, tekli (unary) arama ve sunucu akışlı (server streaming) aktivite akışı metotlarına sahip bir kullanıcı servisini modellemektedir.

user_service.protoprotobuf
syntax = "proto3";

package user.v1;

option go_package = "gen/user/v1;userv1";

service UserService {
  // Unary RPC: single request, single response
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  // Server streaming: single request, stream of responses
  rpc StreamActivity(StreamActivityRequest) returns (stream ActivityEvent);
}

message GetUserRequest {
  string user_id = 1;
}

message GetUserResponse {
  string user_id = 1;
  string email = 2;
  string display_name = 3;
  int64 created_at_unix = 4;
}

message StreamActivityRequest {
  string user_id = 1;
  int32 limit = 2;
}

message ActivityEvent {
  string event_id = 1;
  string action = 2;
  string resource = 3;
  int64 timestamp_unix = 4;
}

protoc --go_out=. --go-grpc_out=. user_service.proto komutu çalıştırıldığında iki dosya üretilmektedir: biri mesaj tiplerini, diğeri gRPC istemci/sunucu arayüzlerini içermektedir. Üretilen sunucu arayüzü, Go implementasyonunun karşılaması gereken sözleşmeyi oluşturmaktadır.

Go'da gRPC Sunucu Implementasyonu

Sunucu implementasyonu, üretilen UnimplementedUserServiceServer yapısını gömmektedir (embed). Bu yaklaşım ileriye dönük uyumluluk sağlamaktadır: proto dosyasına yeni RPC'ler eklendiğinde, mevcut sunucu kodu ilgili metot açıkça implemente edilene kadar bozulmamaktadır.

server/user_server.gogo
package server

import (
	"context"
	"fmt"
	"time"

	userv1 "myapp/gen/user/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type UserServer struct {
	userv1.UnimplementedUserServiceServer
	store UserStore // interface for DB access
}

func NewUserServer(store UserStore) *UserServer {
	return &UserServer{store: store}
}

// GetUser handles the unary RPC
func (s *UserServer) GetUser(ctx context.Context, req *userv1.GetUserRequest) (*userv1.GetUserResponse, error) {
	if req.GetUserId() == "" {
		return nil, status.Error(codes.InvalidArgument, "user_id is required")
	}

	user, err := s.store.FindByID(ctx, req.GetUserId())
	if err != nil {
		return nil, status.Errorf(codes.Internal, "lookup failed: %v", err)
	}
	if user == nil {
		return nil, status.Error(codes.NotFound, "user not found")
	}

	return &userv1.GetUserResponse{
		UserId:       user.ID,
		Email:        user.Email,
		DisplayName:  user.DisplayName,
		CreatedAtUnix: user.CreatedAt.Unix(),
	}, nil
}

// StreamActivity sends activity events as a server stream
func (s *UserServer) StreamActivity(req *userv1.StreamActivityRequest, stream userv1.UserService_StreamActivityServer) error {
	events, err := s.store.GetActivity(stream.Context(), req.GetUserId(), int(req.GetLimit()))
	if err != nil {
		return status.Errorf(codes.Internal, "activity fetch failed: %v", err)
	}

	for _, evt := range events {
		if err := stream.Send(&userv1.ActivityEvent{
			EventId:       evt.ID,
			Action:        evt.Action,
			Resource:      evt.Resource,
			TimestampUnix: evt.Timestamp.Unix(),
		}); err != nil {
			return fmt.Errorf("stream send: %w", err)
		}
	}

	return nil
}

Dikkat edilmesi gereken iki temel desen bulunmaktadır: status.Error ve status.Errorf, istemcilerin programatik olarak inceleyebileceği uygun durum kodlarına sahip gRPC-yerel hatalar üretmektedir. UnimplementedUserServiceServer gömme işlemi ise proto evrildikçe derleme zamanı güvenliği sağlamaktadır.

Interceptor'lar ile Uretim Ortami Sunucu Yapilandirmasi

Temel bir gRPC sunucusu istekleri işleyebilmektedir ancak gözlemlenebilirlik, kimlik doğrulama ve panik kurtarma mekanizmalarından yoksundur. Üretim ortamı servisleri bir interceptor zincirine ihtiyaç duymaktadır. go-grpc-middleware v2 kütüphanesi, grpc.ChainUnaryInterceptor ile zincirlenen birleştirilebilir interceptor'lar sunmaktadır.

cmd/server/main.gogo
package main

import (
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"

	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/ratelimit"

	userv1 "myapp/gen/user/v1"
	"myapp/server"
)

func main() {
	// Interceptor order matters: recovery first, then metrics, then auth
	srv := grpc.NewServer(
		// OpenTelemetry tracing via stats handler (not interceptor)
		grpc.StatsHandler(otelgrpc.NewServerHandler()),

		grpc.ChainUnaryInterceptor(
			recovery.UnaryServerInterceptor(),          // catch panics
			ratelimit.UnaryServerInterceptor(limiter),   // rate limiting
			logging.UnaryServerInterceptor(logger),      // structured logging
			authInterceptor,                              // token validation
		),
		grpc.ChainStreamInterceptor(
			recovery.StreamServerInterceptor(),
			ratelimit.StreamServerInterceptor(limiter),
			logging.StreamServerInterceptor(logger),
		),
	)

	// Register service implementation
	userv1.RegisterUserServiceServer(srv, server.NewUserServer(store))

	// Enable reflection for grpcurl and debugging
	reflection.Register(srv)

	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("listen: %v", err)
	}

	// Graceful shutdown on SIGTERM
	go func() {
		sig := make(chan os.Signal, 1)
		signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
		<-sig
		log.Println("shutting down gRPC server")
		srv.GracefulStop()
	}()

	log.Printf("gRPC server listening on :50051")
	if err := srv.Serve(lis); err != nil {
		log.Fatalf("serve: %v", err)
	}
}

Interceptor sıralaması yürütme önceliğini belirlemektedir. Recovery ilk sırada çalışarak alt akıştaki herhangi bir interceptor'dan kaynaklanan panikleri yakalamaktadır. Hız sınırlama (rate limiting), kimlik doğrulama katmanını kötüye kullanımdan korumak amacıyla auth'dan önce çalışmaktadır. OpenTelemetry izleme, interceptor yerine StatsHandler kullanmaktadır; bu, grpc-go v1.81 itibarıyla önerilen yaklaşımdır çünkü stats handler'lar interceptor'ların göremediği bayt sayıları ve bağlantı yaşam döngüsü olayları gibi daha düşük seviye taşıma olaylarına erişim sağlamaktadır.

Go mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Go'da gRPC Hata Yonetimi Desenleri

Doğru hata yönetimi, üretim düzeyindeki gRPC servislerini prototiplerden ayıran temel unsurdur. gRPC, her istemcinin anlayabileceği bir dizi standart durum kodu tanımlamaktadır. Go'nun status paketi bu kodları hatalara eşlemektedir.

Temel desenler aşağıdaki gibidir:

  • Hatalı biçimlendirilmiş istekler (istemci hatası) için codes.InvalidArgument döndürülmelidir
  • Kaynak bulunamadığında codes.NotFound döndürülmelidir
  • Beklenmeyen sunucu hataları için codes.Internal döndürülmelidir
  • Kimlik bilgileri eksik veya geçersiz olduğunda codes.Unauthenticated döndürülmelidir
  • Kimlik bilgileri geçerli ancak yetersiz olduğunda codes.PermissionDenied döndürülmelidir
errors.go — reusable error constructorsgo
package rpcerr

import (
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// NotFound wraps a resource-not-found error with a consistent message
func NotFound(resource, id string) error {
	return status.Errorf(codes.NotFound, "%s %q not found", resource, id)
}

// InvalidArg wraps a validation error
func InvalidArg(field, reason string) error {
	return status.Errorf(codes.InvalidArgument, "%s: %s", field, reason)
}

// Internal wraps an unexpected error, hiding internals from the client
func Internal(err error) error {
	// Log the real error server-side; return generic message to client
	return status.Error(codes.Internal, "internal server error")
}

Hataları alan bazlı yapıcı fonksiyonlarla (constructor) sarmalamak, durum kodlarının tüm RPC'ler boyunca tutarlı kalmasını sağlamaktadır. İstemciler, hata dizelerini ayrıştırmak zorunda kalmadan her durumu işlemek için status.Code(err) üzerinden ayrım yapabilmektedir.

bufconn ile gRPC Servislerini Test Etme

Bir gRPC servisinin entegrasyon testi normalde gerçek bir TCP sunucusunun başlatılmasını gerektirmektedir. bufconn paketi, port tahsisi ve ağ yükünü tamamen ortadan kaldıran bellek içi (in-memory) bir dinleyici sağlamaktadır.

server_test.gogo
package server_test

import (
	"context"
	"net"
	"testing"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/test/bufconn"

	userv1 "myapp/gen/user/v1"
	"myapp/server"
)

const bufSize = 1024 * 1024

func setupServer(t *testing.T) userv1.UserServiceClient {
	t.Helper()

	lis := bufconn.Listen(bufSize) // in-memory listener
	srv := grpc.NewServer()
	userv1.RegisterUserServiceServer(srv, server.NewUserServer(mockStore{}))

	go func() {
		if err := srv.Serve(lis); err != nil {
			t.Errorf("server exited: %v", err)
		}
	}()
	t.Cleanup(srv.GracefulStop)

	// Dial the in-memory listener
	conn, err := grpc.NewClient(
		"passthrough:///bufconn",
		grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
			return lis.DialContext(ctx)
		}),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		t.Fatalf("dial bufconn: %v", err)
	}
	t.Cleanup(func() { conn.Close() })

	return userv1.NewUserServiceClient(conn)
}

func TestGetUser_NotFound(t *testing.T) {
	client := setupServer(t)

	_, err := client.GetUser(context.Background(), &userv1.GetUserRequest{
		UserId: "nonexistent",
	})

	// Verify gRPC status code
	st, ok := status.FromError(err)
	if !ok || st.Code() != codes.NotFound {
		t.Errorf("expected NotFound, got %v", err)
	}
}

bufconn, serileştirme ve interceptor yürütme dahil tam gRPC bağlantısını TCP olmadan oluşturmaktadır. Testler daha hızlı çalışmakta ve port çakışması olmadan paralel olarak yürütülebilmektedir. Bu, grpc-go deposunun kendi bünyesinde kullanılan standart test desenidir.

mTLS ve Token Kimlik Dogrulama ile gRPC Guvenligi

Üretim ortamı gRPC servisleri taşıma katmanı güvenliği gerektirmektedir. İki yaklaşım baskın konumdadır: her iki tarafın da sertifika sunduğu servisler arası kimlik doğrulama için mTLS ve halihazırda güvenliği sağlanmış bir kanal içinde istemci kimliği için token tabanlı kimlik doğrulama (JWT veya API anahtarları).

mTLS yapılandırması, sunucu başlangıcında sertifikaları yüklemektedir:

tls.go — mTLS server credentialsgo
package main

import (
	"crypto/tls"
	"crypto/x509"
	"os"

	"google.golang.org/grpc/credentials"
)

func loadTLSCredentials(certFile, keyFile, caFile string) (credentials.TransportCredentials, error) {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}

	caPool := x509.NewCertPool()
	caPEM, err := os.ReadFile(caFile)
	if err != nil {
		return nil, err
	}
	caPool.AppendCertsFromPEM(caPEM)

	tlsCfg := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientAuth:   tls.RequireAndVerifyClientCert, // enforce mTLS
		ClientCAs:    caPool,
		MinVersion:   tls.VersionTLS13, // TLS 1.3 minimum in 2026
	}

	return credentials.NewTLS(tlsCfg), nil
}

TLS 1.3, 2026 yılında gRPC servisleri için temel güvenlik standardıdır. TLS 1.2'ye kıyasla daha hızlı el sıkışma (1-RTT) ve daha güçlü şifre takımları sunmaktadır. TLS üzerine katmanlanan token tabanlı kimlik doğrulama için, bir unary interceptor gRPC meta verilerinden token'ı çıkarmakta ve handler yürütülmeden önce doğrulamaktadır.

mTLS Sertifika Rotasyonu

Sabit kodlanmış sertifika yolları, rotasyon için sunucu yeniden başlatmalarını gerektirmektedir. Üretim ortamı dağıtımlarında, kesinti yaşanmadan otomatik sertifika yenileme işlemi için tls.Config.GetCertificate kullanılması veya Envoy gibi bir sidecar proxy tercih edilmesi gerekmektedir.

Mulakat Sorulari: gRPC ve Go Mikroservisleri

Aşağıdaki sorular, Go ve dağıtık sistemlere odaklanan backend mühendislik mülakatlarında sıklıkla karşılaşılmaktadır. Her cevap, kıdemli seviyede beklenen derinliği hedeflemektedir.

gRPC'deki dort RPC tipi nelerdir ve her biri ne zaman uygundur?

Unary (tekli istek, tekli yanıt) çoğu CRUD işlemini kapsamaktadır. Sunucu akışı (server streaming), sunucunun birden fazla sonuç ilettiği senaryolara uygundur: aktivite akışları, arama sonuçları ve gerçek zamanlı güncellemeler. İstemci akışı (client streaming), yükleme senaryoları veya istemcinin sunucu yanıt vermeden önce birden fazla mesaj gönderdiği toplu yazma işlemleri için geçerlidir. Çift yönlü akış (bidirectional streaming), sohbet, ortak düzenleme veya her iki tarafın bağımsız olarak mesaj gönderdiği herhangi bir protokol için hizmet vermektedir.

Bir proto alanı kaldırıldığında gRPC geriye dönük uyumluluğu nasıl sağlamaktadır?

Proto3, alan numaralarını asla yeniden kullanmamaktadır. Bir alan kaldırıldığında, eski bir istemci hala bu değeri gönderiyorsa sunucu bunu yok saymaktadır; yanıtı okuyan eski istemciler ise kaldırılan alan için sıfır değerini görmektedir. reserved anahtar kelimesi, alan numaralarının veya adlarının yanlışlıkla yeniden kullanılmasını engellemektedir. Bu durum, alan kaldırmanın deserialization hatalarına neden olabileceği JSON API'lerinden temelden farklıdır.

Protobuf Wire Uyumluluk Kurali

Mevcut bir alanın tipi veya numarası asla değiştirilmemelidir. Yeni alanlar yeni numaralarla eklenmelidir. Eski numaraları kullanımdan kaldırmak için reserved kullanılmalıdır. Bu kural tüm programlama dillerinde geçerlidir ve sıfır kesinti süreli dağıtımları garanti etmektedir.

Uretim ortami gRPC servisinde interceptor'larin rolunu aciklayiniz.

Interceptor'lar, gRPC'nin middleware katmanıdır. Handler'dan önce ve sonra, kayıt edildikleri sırayla yürütülmektedir. Tipik bir üretim zinciri: recovery (panik yakalama) > hız sınırlama > günlükleme > auth > doğrulama şeklindedir. go-grpc-middleware v2 kütüphanesi bu deseni takip eden birleştirilebilir interceptor'lar sağlamaktadır. Interceptor'lar taşıma katmanını (TLS, portlar) değiştirememektedir; yalnızca RPC seviyesinde çalışmaktadır.

grpc.StatsHandler ile interceptor arasindaki fark nedir?

Interceptor'lar RPC handler'ını sarmalamakta ve uygulama seviyesinde çalışmaktadır. Stats handler'lar ise taşıma seviyesi olaylarını almaktadır: bağlantı başlangıçları, mesaj gönderme/alma ve RPC tamamlanma olayları. OpenTelemetry, interceptor'ların göremediği bayt sayıları ve bağlantı yaşam döngüsü olayları gibi metrikleri yakaladığı için stats handler'ları kullanmaktadır. grpc-go v1.66+ itibarıyla resmi öneri, gözlemlenebilirlik için stats handler'ların, iş mantığı için interceptor'ların kullanılması yönündedir.

Bir gRPC sunucusu icin graceful shutdown nasil uygulanir?

GracefulStop(), yeni bağlantıları kabul etmeyi durdurmakta ve devam eden RPC'lerin tamamlanmasını beklemektedir. Desen şu şekildedir: bir goroutine'de SIGTERM dinlenmekte, GracefulStop() çağrılmakta ve ardından Serve() çağrısı geri dönmektedir. Süresiz çalışabilecek streaming RPC'ler için, context iptal ile bir son tarih belirlenmeli veya kapatma başlamadan önce SERVING durumunu döndürmeyi bırakan bir sağlık kontrolü kullanılmalıdır. Bu sayede yük dengeleyicilere trafiği boşaltmaları için süre tanınmaktadır.

Dahili servisler icin bir ekip gRPC'yi REST yerine ne zaman secmelidir?

gRPC, şu durumlarda daha güçlü bir seçimdir: servisler derleme zamanında uygulanan katı API sözleşmelerine ihtiyaç duyduğunda; sistem akış gerektirdiğinde (sunucu push, çift yönlü); gecikme süresi önemli olduğunda ve ikili serileştirme veri paketi boyutunu küçülttüğünde; ekip hem istemci hem de sunucu kodunu yönettiğinde. REST ise şu durumlarda tercih edilmektedir: tarayıcılar tarafından tüketilen genel API'ler (yerel JSON, proxy gerekmez), HTTP semantikleri ile yoğun önbellek kullanan API'ler veya maksimum araç uyumluluğuna ihtiyaç duyan ekipler için. Birçok mimari her ikisini birlikte kullanmaktadır: dahili iletişim için gRPC, harici tüketiciler için REST gateway.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Sonuc

  • Protocol Buffers, API sözleşmelerini derleme zamanında uygulamaktadır; bozucu değişiklikler çalışma zamanında değil, kod üretimi sırasında ortaya çıkmaktadır
  • UnimplementedServer deseni, gelişen bir servise yeni RPC'ler eklendiğinde ileriye dönük uyumluluk sağlamaktadır
  • Interceptor sırası kritik öneme sahiptir: önce recovery, ardından hız sınırlama, sonra auth; go-grpc-middleware v2 kütüphanesi zincirlemeyi yönetmektedir
  • İzleme için grpc.StatsHandler ve OpenTelemetry kullanılmalı; auth ve doğrulama gibi iş mantığı için interceptor'lar ayrılmalıdır
  • bufconn, serileştirme ve interceptor'lar dahil tam gRPC yığınını kapsayan hızlı ve port gerektirmeyen entegrasyon testleri sağlamaktadır
  • TLS 1.3 ile mTLS, 2026'da servisler arası güvenlik için temel standarttır; mesh içinde kimlik doğrulama için üzerine token auth katmanlanmalıdır
  • gRPC mulakat sorularina hazirlanirken dort RPC tipi, proto geriye donuk uyumluluk kurallari ve interceptor ile stats handler ayrimi anlasilmalidir

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Etiketler

#go
#grpc
#microservices
#backend
#interview
#protocol-buffers

Paylaş

İlgili makaleler