Go ile Mikroservis Mimarisi: Kurulum ve Best Practices

Go (Golang), Google tarafından geliştirilen, yüksek performanslı ve concurrent yapısına sahip modern bir programlama dilidir. Mikroservis mimarisi için idealdir çünkü hızlı derleme, iyi concurrency desteği ve küçük binary size gibi avantajlara sahiptir.

Neden Go Mikroservis İçin?

Avantajlar

  • Hızlı Derleme: Büyük projelerde bile hızlı build süreleri
  • Küçük Binary Size: Container deployment için ideal
  • Built-in Concurrency: Goroutines ve channels ile kolay concurrent programming
  • Static Typing: Compile-time error catching
  • Strong Standard Library: HTTP, JSON, encoding dahil
  • Cross-platform: Windows, macOS, Linux için native binary
package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Merhaba, Mikroservis!")
    })
    
    fmt.Println("Server 8080 portunda çalışıyor...")
    http.ListenAndServe(":8080", nil)
}

Basit Mikroservis Kurulumu

Proje Yapısı

my-microservice/
├── go.mod
├── go.sum
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── handlers/
│   ├── models/
│   └── services/
├── pkg/
│   └── utils/
└── Dockerfile

main.go

package main

import (
    "log"
    "net/http"
    
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    
    // Route tanımlamaları
    r.HandleFunc("/api/users", getUsersHandler).Methods("GET")
    r.HandleFunc("/api/users/{id}", getUserHandler).Methods("GET")
    
    // Middleware
    r.Use(loggingMiddleware)
    r.Use(authMiddleware)
    
    // Server
    port := ":8080"
    log.Printf("Server %s portunda çalışıyor...", port)
    log.Fatal(http.ListenAndServe(port, r))
}

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s %s", r.Method, r.RequestURI, r.RemoteAddr)
        next.ServeHTTP(w, r)
    })
}

gRPC ile Inter-Service Communication

gRPC, Google’ın geliştirdiği bir RPC framework’tür. HTTP/2, Protocol Buffers ve streaming’i destekler.

Proto Dosyası

syntax = "proto3";

package user;

service UserService {
    rpc GetUser(GetUserRequest) returns (GetUserResponse);
    rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
}

message GetUserRequest {
    string id = 1;
}

message GetUserResponse {
    User user = 1;
}

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

Go Server Implementation

package main

import (
    "context"
    "log"
    "net"
    
    "google.golang.org/grpc"
)

type server struct {
    pb.UnimplementedUserServiceServer
}

func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
    // Veritabanından kullanıcı çek
    user := &pb.User{
        Id:    req.Id,
        Name:  "John Doe",
        Email: "john@example.com",
    }
    
    return &pb.GetUserResponse{User: user}, nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }
    
    s := grpc.NewServer()
    pb.RegisterUserServiceServer(s, &server{})
    
    log.Println("gRPC server 50051 portunda çalışıyor...")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("Failed to serve: %v", err)
    }
}

Concurrency and Performance

Goroutines

func processOrders() {
    orders := []int{1, 2, 3, 4, 5}
    var wg sync.WaitGroup
    
    for _, orderID := range orders {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            // Siparişi işle
            log.Printf("Sipariş %d işleniyor", id)
        }(orderID)
    }
    
    wg.Wait()
}

Channels

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        log.Printf("Worker %d işliyor: %d", id, j)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)
    
    // Worker'ları başlat
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }
    
    // İşleri gönder
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)
    
    // Sonuçları al
    for a := 1; a <= 5; a++ {
        <-results
    }
}

Best Practices

Error Handling

func processRequest(r *http.Request) (Response, error) {
    user, err := getUserFromRequest(r)
    if err != nil {
        return Response{}, fmt.Errorf("kullanıcı alınamadı: %w", err)
    }
    
    data, err := processData(user)
    if err != nil {
        return Response{}, fmt.Errorf("veri işlenemedi: %w", err)
    }
    
    return Response{Data: data}, nil
}

Configuration Management

type Config struct {
    ServerPort  string `env:"SERVER_PORT" envDefault:"8080"`
    DatabaseURL string `env:"DATABASE_URL" envDefault:"postgres://localhost"`
    LogLevel    string `env:"LOG_LEVEL" envDefault:"info"`
}

func LoadConfig() (*Config, error) {
    cfg := &Config{}
    if err := env.Parse(cfg); err != nil {
        return nil, err
    }
    return cfg, nil
}

Logging

import (
    "go.uber.org/zap"
)

var logger *zap.Logger

func InitLogger() error {
    var err error
    logger, err = zap.NewProduction()
    if err != nil {
        return err
    }
    return nil
}

func LogRequest(r *http.Request) {
    logger.Info("gelen istek",
        zap.String("method", r.Method),
        zap.String("path", r.URL.Path),
        zap.String("ip", r.RemoteAddr),
    )
}

Testing

package handlers_test

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestGetUsersHandler(t *testing.T) {
    req := httptest.NewRequest("GET", "/api/users", nil)
    w := httptest.NewRecorder()
    
    getUsersHandler(w, req)
    
    resp := w.Result()
    if resp.StatusCode != http.StatusOK {
        t.Errorf("beklenen durum kodu: %d, elde edilen: %d", http.StatusOK, resp.StatusCode)
    }
}

Deployment

Dockerfile

FROM golang:1.21-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main cmd/server/main.go

FROM alpine:latest

RUN apk --no-cache add ca-certificates
WORKDIR /root/

COPY --from=builder /app/main .
EXPOSE 8080

CMD ["./main"]

Öğrenme Yolu

  1. Go temelleri
  2. HTTP server’lar
  3. gRPC
  4. Testing
  5. Production deployment

Kaynaklar