Back to Research
DevSecOps

Go ile Sıfırdan gRPC Servisi Nasıl Yazılır? Adım Adım Production Rehberi

İsa CanBackend Developer
April 7, 2026
5 min read

REST API'ler hâlâ iş görüyor, fakat gerçek zamanlı veri akışının ve düşük gecikmenin (latency) her şey olduğu ortamlarda — loglama, metrik toplama, IoT sinyalleri — artık gRPC standardı öne çıkıyor. Google, Netflix, Spotify ve Cloudflare gibi devler tüm dahili iletişimlerini gRPC üzerine kurdu. Peki neden REST yetmiyor ve Go ile kendi gRPC servisinizi sıfırdan nasıl yazarsınız?

Bu rehberde, Eresus Security'nin açık kaynak olarak yayınladığı EresusLog projesini referans alarak, Go ile tam bir production-grade gRPC servisi inşa edeceğiz. Protobuf tanımlamadan, veritabanı entegrasyonuna, kimlik doğrulama (Auth), istek loglama ve hız sınırlama (Rate Limiting) interceptor'lerine kadar her şeyi adım adım kodlayacağız.


1. gRPC Nedir ve REST'ten Farkı Ne?

gRPC, Google'ın geliştirdiği açık kaynaklı bir Remote Procedure Call (Uzak Prosedür Çağrısı) framework'üdür. REST API'lerle karşılaştırıldığında:

| Özellik | REST (JSON/HTTP) | gRPC (Protobuf/HTTP/2) | | :--- | :--- | :--- | | Veri Formatı | JSON (text) | Protocol Buffers (binary) | | Aktarım Hızı | Yavaş (JSON serialization) | Çok hızlı (ikili sıkıştırma) | | Streaming | Yok (WebSocket lazım) | Doğal destek (4 farklı pattern) | | Tip Güvenliği | Yok | Derleme zamanında kontrol | | HTTP Versiyonu | HTTP/1.1 | HTTP/2 (multiplexing) |

Eğer servisler arası iletişimde (microservice-to-microservice) milisaniyeler önemliyse, gRPC tek doğru cevaptır.


2. Adım 1: Protobuf Servis Tanımı

Her gRPC projesi bir .proto dosyasıyla başlar. Bu dosya hem veri yapılarınızı (message) hem de API metodlarınızı (service) tanımlar:

syntax = "proto3";
package logger;

option go_package = "github.com/EresusSecurity/eresuslog/api/proto;logger";

service LoggerService {
  // Tek bir log gönder
  rpc Log(LogRequest) returns (LogResponse) {}
  
  // İstemciden sunucuya log akışı (client-streaming)
  rpc StreamLogs(stream LogRequest) returns (LogResponse) {}
  
  // Veritabanındaki logları sorgula
  rpc FetchLogs(FetchRequest) returns (FetchResponse) {}
  
  // Gerçek zamanlı log dinle (server-streaming)
  rpc SubscribeLogs(SubscribeRequest) returns (stream LogEntry) {}
}

message LogRequest {
  string service_name = 1;
  string level        = 2;
  string message      = 3;
  int64  timestamp    = 4;
  map<string, string> metadata = 5;
}

Dikkat edin: 4 farklı RPC pattern'ı tek bir servis altında tanımlandı — Unary, Client-Streaming, Server-Streaming ve gerekirse Bidirectional.

protoc ile Go kodlarını üretiyoruz:

protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    api/proto/logger.proto

Bu komut logger.pb.go (veri yapıları) ve logger_grpc.pb.go (servis arayüzleri) dosyalarını otomatik oluşturur.


3. Adım 2: Veritabanı Katmanı (PostgreSQL + GORM)

Loglarımızı kalıcı bir şekilde depolamak için GORM ORM'ini kullanıyoruz:

// internal/db/models.go
type Log struct {
    gorm.Model
    ServiceName string    `gorm:"index"`
    Level       string    `gorm:"index"`
    Message     string
    Timestamp   time.Time
    Metadata    string // JSON encoded
}

Repository katmanı ile veritabanı işlemleri temiz bir şekilde izole edilir:

// internal/db/repository.go
func (r *Repository) SaveLog(ctx context.Context, serviceName, level, message string, 
    timestamp int64, metadata map[string]string) error {
    
    metadataJson, _ := json.Marshal(metadata)
    log := &Log{
        ServiceName: serviceName,
        Level:       level,
        Message:     message,
        Timestamp:   time.Unix(timestamp, 0),
        Metadata:    string(metadataJson),
    }
    return r.db.WithContext(ctx).Create(log).Error
}

4. Adım 3: gRPC Sunucu Implementasyonu

Servisimizi UnimplementedLoggerServiceServer üzerine inşa ediyoruz. İşte real-time pub/sub mekanizmasının kalp kodu:

// internal/server/server.go
type LoggerServer struct {
    pb.UnimplementedLoggerServiceServer
    repo        *db.Repository
    subscribers []chan *pb.LogEntry
    mu          sync.RWMutex
}

func (s *LoggerServer) Log(ctx context.Context, req *pb.LogRequest) (*pb.LogResponse, error) {
    err := s.repo.SaveLog(ctx, req.ServiceName, req.Level, req.Message, 
        req.Timestamp, req.Metadata)
    if err != nil {
        return &pb.LogResponse{Success: false, Message: err.Error()}, nil
    }

    // Tüm aktif dinleyicilere yayınla (broadcast)
    s.broadcast(&pb.LogEntry{
        ServiceName: req.ServiceName,
        Level:       req.Level,
        Message:     req.Message,
        Timestamp:   req.Timestamp,
        Metadata:    req.Metadata,
    })

    return &pb.LogResponse{Success: true, Message: "Log saved"}, nil
}

SubscribeLogs metoduyla herhangi bir istemci canlı log akışına abone olabilir — Tıpkı bir tail -f gibi, ama ağ üzerinde ve tip-güvenli (type-safe).


5. Adım 4: Interceptor Zinciri (Güvenlik Katmanları)

gRPC'nin en güçlü silahı Interceptor'lerdir. REST'teki middleware konseptinin karşılığıdır. EresusLog'da 3 katmanlı bir zincir kuruyoruz:

5.1 Request Logger Interceptor

Her gelen isteğin IP adresini, hangi metodu çağırdığını, ne kadar sürdüğünü ve sonucunu loglar:

func (r *RequestLoggerInterceptor) Unary() grpc.UnaryServerInterceptor {
    return func(ctx context.Context, req interface{}, 
        info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
        
        start := time.Now()
        resp, err := handler(ctx, req)
        
        clientIP := "unknown"
        if p, ok := peer.FromContext(ctx); ok {
            clientIP = p.Addr.String()
        }
        st, _ := status.FromError(err)
        log.Printf("[gRPC] %s | %s | %s | %v",
            info.FullMethod, clientIP, st.Code(), time.Since(start))
        
        return resp, err
    }
}

5.2 Rate Limiter Interceptor

Her IP adresi için kayan pencere (sliding window) hız sınırlaması — DDoS ve log spam saldırılarını engeller:

rl := server.NewRateLimiter(100, 10*time.Second) // IP başına 10sn'de 100 istek

5.3 Auth Interceptor (API Key)

gRPC metadata'sından Authorization: Bearer <key> token'ını doğrular:

func (i *AuthInterceptor) authorize(ctx context.Context) error {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return status.Errorf(codes.Unauthenticated, "metadata is not provided")
    }
    values := md["authorization"]
    if len(values) == 0 {
        return status.Errorf(codes.Unauthenticated, "token is missing")
    }
    token := strings.TrimPrefix(values[0], "Bearer ")
    if token != i.validAPIKey {
        return status.Errorf(codes.Unauthenticated, "invalid API key")
    }
    return nil
}

Bunları sunucuya zincir halinde bağlıyoruz:

s := grpc.NewServer(
    grpc.ChainUnaryInterceptor(
        reqLogger.Unary(),
        rateLimiter.UnaryInterceptor(),
        authInterceptor.Unary(),
    ),
    grpc.ChainStreamInterceptor(
        reqLogger.Stream(),
        rateLimiter.StreamInterceptor(),
        authInterceptor.Stream(),
    ),
)

6. Adım 5: Health Check ve Production Hazırlığı

Kubernetes veya AWS ALB arkasında çalışacak her gRPC servisi bir Health Check endpoint'ine sahip olmalıdır:

healthServer := health.NewServer()
healthpb.RegisterHealthServer(s, healthServer)
healthServer.SetServingStatus("logger.LoggerService", 
    healthpb.HealthCheckResponse_SERVING)

Terminal'den test:

grpcurl -plaintext localhost:50051 grpc.health.v1.Health/Check

7. Sonuç: Tüm Kodu Alın ve Çalıştırın

Bu rehberdeki tüm kodu tek bir yerde görmek ve hemen çalıştırmak isterseniz, EresusLog'un tüm kaynak koduna açık kaynak olarak erişebilirsiniz:

git clone https://github.com/EresusSecurity/eresuslog.git
cd eresuslog
cp .env.example .env
go run cmd/server/main.go

Projeye GitHub üzerinden yıldız (⭐) vererek destek olabilir, katkıda bulunabilirsiniz!

Eresus Security olarak sistemlerinizin güvenliğini otonom yapay zeka ajanlarıyla korumak, DevSecOps altyapınızı güçlendirmek veya sızma testi (pentest) yaptırmak için bizimle iletişime geçebilirsiniz.