İçerikler

Go ile Modern API Protokolleri

İçerikler

Go ile Modern API Protokolleri: Kapsamlı Bir İnceleme

Kısa Özet (TL;DR)

  • REST: Basit entegrasyon, geniş istemci uyumu; CRUD ve public API’ler için ideal.
  • gRPC: Düşük gecikme, yüksek throughput; mikro servisler arası en iyi seçim.
  • GraphQL: Esnek sorgulama ve tek endpoint; frontend/mobil ağırlıklı uygulamalar.
  • WebSocket: Gerçek zamanlı, çift yönlü; sohbet, trade, işbirliği, oyunlar.
  • Webhook: Olay güdümlü entegrasyonlar ve otomasyon.
  • gRPC‑Web: Gateway ile tarayıcı dostu gRPC; tip güvenliği ve hız.
  • tRPC: TypeScript yığınlarında uçtan uca tip güvenliği; hızlı geliştirme.

Hızlı Seçim Rehberi

  • Gerçek zamanlı mı? Evet → Çift yönlü mü? Evet: WebSocket, Hayır: gRPC‑Web
  • İç ağda yüksek performans mı? gRPC
  • Esnek veri/tek endpoint mi? GraphQL
  • Basit CRUD ve en geniş uyumluluk mu? REST
  • Üçüncü taraf olay bildirimleri mi? Webhook
  • Tarayıcıda tip güvenliği mi? gRPC‑Web veya tRPC

Not: Kod listeleri örnekleyicidir; bazı importlar veya iskeletler kısaltılmış olabilir.

1. Giriş ve Temel Kavramlar

1.1 Giriş

Modern yazılım geliştirmede, farklı sistemler arasında iletişim kurmak için çeşitli API protokolleri kullanılır. Bu makale, Go diliyle en popüler API protokollerini pratik örnekler ve en iyi uygulamalarla kapsamlı biçimde inceler. Her protokolün kendine özgü kullanım alanları ve avantajları vardır:

  • REST API: Web tabanlı uygulamalarda en yaygın; basitlik ve geniş uyumluluk
  • SOAP: Kurumsal ve güvenliği yüksek senaryolar; güçlü standartlar ve güvenlik
  • gRPC: Mikro servisler arası yüksek performans; verimli ikili serileştirme
  • GraphQL: Esnek veri sorgulama ve istemci kontrolü; tam isabetli veri çekme
  • Webhook: Olay güdümlü mimariler ve asenkron bildirimler; gerçek zamanlı tetiklemeler
  • WebSocket: Gerçek zamanlı çift yönlü iletişim gerektiren uygulamalar
  • gRPC‑Web: Tarayıcı tabanlı gRPC; gRPC verimliliği + web uyumluluğu
  • tRPC: Uçtan uca tip güvenliği sağlayan tip emniyetli RPC çağrıları

1.2 Go’nun API Geliştirmedeki Güçlü Yanları

Go, modern API geliştirme için pek çok avantaj sunar:

  1. Yüksek Performans

    • Düşük bellek kullanımı ve verimli çöp toplayıcı
    • Hızlı başlangıç ve statik derleme
    • Goroutine’lerle yerleşik eşzamanlılık
    • Üstün CPU/bellek profil araçları
    • Verimli ağ işleme
    • Optimize standart kütüphane
  2. Eşzamanlılık Desteği

    • Hafif iş parçacıkları (goroutine)
    • Kanallarla güvenli iletişim
    • select ile çoklu akış yönetimi
    • context ile süreç kontrolü
    • Work‑stealing zamanlayıcı
    • Atomik işlemler ve verimli senkronizasyon
  3. Güçlü Standart Kütüphane

    • HTTP/2 destekli sunucu/istemci
    • JSON/XML/Protocol Buffers işleme
    • Şifreleme ve güvenlik paketleri
    • Veritabanı sürücüleri ve bağlantı havuzları
    • Test ve benchmark araçları
    • Çoklu platform derleme
    • Zengin ağ yetenekleri
  4. Geliştirme Kolaylığı

    • Basit ve anlaşılır söz dizimi
    • Hızlı derleme süresi
    • Zengin araç ekosistemi
    • Kapsamlı dokümantasyon
    • Yerleşik kod biçimlendirme
    • Go modules ile bağımlılık yönetimi
    • Mükemmel IDE desteği
  5. Bulut‑Yerel Destek

    • Konteyner dostu tasarım
    • Mikro servis mimarisi desteği
    • Service mesh entegrasyonu
    • Bulut platform uyumu
    • Dağıtık izleme desteği
    • Metrikler ve izlenebilirlik
    • Kubernetes entegrasyonu

1.3 Performans Özellikleri

1.4 API Protokol Kullanım Senaryoları

  1. REST API

    • Web tabanlı uygulamalar
    • Mobil arka uçlar
    • Public API’ler
    • CRUD işlemleri
    • Kaynak odaklı sistemler
    • Önbellek dostu uygulamalar
    • Üçüncü taraf entegrasyonları
  2. gRPC

    • Mikro servis iletişimi
    • Yüksek performanslı sistemler
    • Akış (stream) işlemleri
    • Çok dillilik (polyglot)
    • Gerçek zamanlı veri işleme
    • Service mesh içi iletişim
    • Dahili API’ler
  3. GraphQL

    • Karmaşık veri gereksinimleri
    • Mobil uygulamalar
    • Gerçek zamanlı güncellemeler
    • Tek endpoint ile çok veri
    • İstemci güdümlü veri çekme
    • Şema‑önce geliştirme
    • Frontend ağırlıklı uygulamalar
  4. WebSocket

    • Gerçek zamanlı uygulamalar
    • Sohbet sistemleri
    • Canlı veri akışı
    • Oyun sunucuları
    • İşbirliği araçları
    • Finansal işlem platformları
    • IoT uygulamaları
  5. Webhook

    • Olay güdümlü mimariler
    • Üçüncü taraf entegrasyonları
    • Asenkron bildirimler
    • Otomatik tetikleyiciler
    • Sistem senkronizasyonu
    • İş akışı otomasyonu
    • Ödeme süreçleri
  6. gRPC‑Web

    • Tarayıcı tabanlı gRPC uygulamaları
    • Tip güvenli istemci‑sunucu iletişimi
    • Tarayıcıda çift yönlü akış
    • Mikro servis ön yüz entegrasyonu
    • Gerçek zamanlı web uygulamaları
    • Modern web çatıları
    • Bulut‑yerel web uygulamaları
  7. tRPC

    • Tip güvenli API geliştirme
    • Uçtan uca tip güvenliği
    • Tam yığın TypeScript uygulamaları
    • Hızlı API geliştirme
    • Modern web uygulamaları
    • Mikro servis mimarisi
    • Gerçek zamanlı uygulamalar

1.5 Performans Karşılaştırması

1.6 Ayrıntılı Performans Karşılaştırması

Farklı API protokollerini daha somut kıyaslamak için her protokolün Go ile uygulanmış sürümü üzerinde benchmark çalıştırdık. Test koşulları (AWS EC2 c5.xlarge: 4 vCPU, 8GB RAM):

  • Test Süresi: Protokol başına 5 dakika
  • Eşzamanlı Kullanıcı: 1000
  • İstek Deseni: Karma okuma/yazma
  • Ağ Koşulu: 50ms gecikme

Yöntem notları:

  • Tüm protokoller için aynı veri seti ve endpoint’ler; warmup dışlandı.
  • İstemci ve sunucu aynı AZ’de; crypto değişkenliğini kaldırmak için TLS kapalı.
  • Değerler gösterimseldir; gerçek sonuçlar şema/serileştirme, I/O ve iş mantığına bağlıdır.

Gecikme Karşılaştırması (ms)

Protokol p50 p90 p95 p99
REST 45 120 180 320
gRPC 12 35 55 95
GraphQL 65 150 210 380
WebSocket 8 25 40 85
gRPC-Web 25 70 110 190
tRPC 18 50 80 140

Throughput Karşılaştırması (istek/s)

Protokol Tek Örnek Kümeli (3 düğüm)
REST 1,850 5,200
gRPC 8,500 24,000
GraphQL 950 2,700
WebSocket 12,000 32,000
gRPC-Web 3,200 9,100
tRPC 5,500 15,500

Kaynak Kullanımı

Protokol CPU (%) Bellek (MB) Ağ I/O (MB/s)
REST 45 320 12
gRPC 65 280 8
GraphQL 70 450 15
WebSocket 75 380 6
gRPC-Web 60 310 9
tRPC 55 290 10

Soğuk Başlangıç Süresi (ms)

Protokol İlk İstek Sonraki İstekler
REST 120 35
gRPC 180 10
GraphQL 250 60
WebSocket 150 5
gRPC-Web 200 20
tRPC 160 15

Bu benchmarklar, performans kritik senaryolarda gRPC ve WebSocket’in öne çıktığını; REST ve GraphQL’in ise geliştirme deneyimi açısından avantajlı olduğunu gösterir. Seçiminizi, gereksinimlerinize göre performans ile geliştirme verimliliği arasında dengeleyin.

2. API Protokollerinin Karşılaştırması

2.1 Karşılaştırma Tablosu

Özellik REST SOAP gRPC GraphQL WebSocket Webhook gRPC‑Web tRPC
Veri Biçimi JSON XML Protocol Buffers JSON Binary/Text JSON Protocol Buffers Protocol Buffers
İletişim Modeli İstek‑Yanıt İstek‑Yanıt İstek‑Yanıt İstek‑Yanıt Tam çift yönlü Olay güdümlü Tam çift yönlü İstek‑Yanıt
Performans Orta Düşük Yüksek Orta Yüksek Orta Orta Yüksek
Ölçeklenebilirlik İyi Orta Mükemmel İyi İyi İyi İyi İyi
Karmaşıklık Düşük Yüksek Orta Orta Orta Düşük Orta Orta
Güvenlik İyi Mükemmel İyi İyi İyi İyi İyi İyi
Dokümantasyon Zengin Zengin Orta Zengin Orta Orta Orta Zengin
Önbellekleme Mükemmel İyi Sınırlı İyi Sınırlı Sınırlı Sınırlı Sınırlı
Durum Yönetimi Durumsuz Durumlu Durumsuz Durumsuz Durumlu Durumsuz Durumlu Durumsuz
Hata Yönetimi HTTP Statü SOAP Fault gRPC Statü GraphQL Hataları Özel HTTP Statü Özel Özel
Versiyonlama URL/Header Namespace Paket Şema Protokol URL/Header Protokol URL/Header
Tarayıcı Desteği Mükemmel Sınırlı Sınırlı Mükemmel İyi İyi İyi Sınırlı
Mobil Desteği Mükemmel Sınırlı İyi Mükemmel İyi İyi İyi Sınırlı
Geliştirme Hızı Hızlı Yavaş Orta Hızlı Orta Hızlı Orta Hızlı
Öğrenme Eğrisi Düşük Yüksek Orta Orta Orta Düşük Orta Orta

2.2 Protokol Seçim Kriterleri

2.3 Seçim Rehberi

  • REST: Web, basit CRUD, geniş istemci uyumu, önbellek ve durumsuz mimari, public API.
  • gRPC: Mikro servisler arası, düşük gecikme/yüksek throughput, streaming, polyglot.
  • GraphQL: Esnek sorgu, tek endpoint, karmaşık ilişkiler, mobil/ön yüz odaklı.
  • WebSocket: Kalıcı bağlantı, çift yönlü iletişim, gerçek zamanlı akış.
  • Webhook: Olay güdümlü, asenkron bildirim/entegrasyon, otomasyon.
  • gRPC‑Web: Tarayıcıda gRPC, tip güvenliği; mevcut gRPC arka uçlarla ideal.
  • tRPC: Uçtan uca tip güvenliği, TypeScript ekosistemi, hızlı geliştirme.

2.4 Performans Metrikleri

2.5 Gerçek Dünya Örnekleri

REST: GitHub, Twitter, Stripe… gRPC: Google, Netflix, Uber… GraphQL: Facebook, GitHub, Shopify… WebSocket: Slack, Discord… Webhook: GitHub, Stripe…

3. Temel API Protokolleri

3.1 REST API

REST (Representational State Transfer) web servisleri için en yaygın protokoldür. HTTP metodlarını kullanarak kaynaklar üzerinde CRUD işlemleri yapar.

REST Mimarisi

En İyi Uygulamalar

  • Kaynak adlandırma, doğru HTTP metod/statü, tutarlı JSON, versiyonlama (URL/Header), oran sınırlama, güvenlik başlıkları, metrik ve loglama.

Go REST API Örneği

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package main

import (
    "github.com/gin-gonic/gin"
    "github.com/gin-contrib/cors"
    "github.com/gin-contrib/helmet"
    "github.com/ulule/limiter/v3"
    "github.com/ulule/limiter/v3/drivers/store/memory"
    "github.com/go-redis/redis/v8"
    "github.com/prometheus/client_golang/prometheus"
    "go.uber.org/zap"
    "net/http"
    "time"
)

type Config struct {
    Port            string
    RedisAddr       string
    RateLimit       int
    RateWindow      time.Duration
    EnableMetrics   bool
    EnableTracing   bool
}

type Server struct {
    router  *gin.Engine
    redis   *redis.Client
    limiter *limiter.Limiter
    logger  *zap.Logger
    metrics *prometheus.Registry
}

func NewServer(cfg *Config) (*Server, error) {
    // Initialize Redis
    rdb := redis.NewClient(&redis.Options{
        Addr: cfg.RedisAddr,
    })

    // Initialize rate limiter
    rate := limiter.Rate{
        Period: cfg.RateWindow,
        Limit:  int64(cfg.RateLimit),
    }
    store := memory.NewStore()
    limiter := limiter.New(store, rate)

    // Initialize logger
    logger, err := zap.NewProduction()
    if err != nil {
        return nil, err
    }

    // Initialize metrics
    metrics := prometheus.NewRegistry()

    // Create server
    s := &Server{
        router:  gin.Default(),
        redis:   rdb,
        limiter: limiter,
        logger:  logger,
        metrics: metrics,
    }

    // Setup middleware
    s.setupMiddleware()

    // Setup routes
    s.setupRoutes()

    return s, nil
}

func (s *Server) setupMiddleware() {
    // CORS
    s.router.Use(cors.New(cors.Config{
        AllowOrigins:     []string{"*"},
        AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
        AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
        MaxAge:           12 * time.Hour,
    }))

    // Security headers
    s.router.Use(helmet.Default())

    // Rate limiting
    s.router.Use(s.rateLimitMiddleware())

    // Logging
    s.router.Use(s.loggingMiddleware())

    // Metrics
    s.router.Use(s.metricsMiddleware())

    // Recovery
    s.router.Use(gin.Recovery())
}

func (s *Server) setupRoutes() {
    // Health check
    s.router.GET("/health", s.healthCheck)

    // API routes
    api := s.router.Group("/api/v1")
    {
        // User routes
        users := api.Group("/users")
        {
            users.GET("", s.getUsers)
            users.GET("/:id", s.getUser)
            users.POST("", s.createUser)
            users.PUT("/:id", s.updateUser)
            users.DELETE("/:id", s.deleteUser)
        }

        // Product routes
        products := api.Group("/products")
        {
            products.GET("", s.getProducts)
            products.GET("/:id", s.getProduct)
            products.POST("", s.createProduct)
            products.PUT("/:id", s.updateProduct)
            products.DELETE("/:id", s.deleteProduct)
        }
    }
}

func (s *Server) rateLimitMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        ip := c.ClientIP()
        context, err := s.limiter.Get(c, ip)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "rate limit error"})
            c.Abort()
            return
        }

        if context.Reached {
            c.JSON(http.StatusTooManyRequests, gin.H{
                "error": "rate limit exceeded",
                "retry_after": context.Reset,
            })
            c.Abort()
            return
        }

        c.Next()
    }
}

func (s *Server) loggingMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path
        query := c.Request.URL.RawQuery

        c.Next()

        latency := time.Since(start)
        status := c.Writer.Status()
        clientIP := c.ClientIP()
        method := c.Request.Method
        userAgent := c.Request.UserAgent()

        s.logger.Info("request completed",
            zap.String("path", path),
            zap.String("query", query),
            zap.Int("status", status),
            zap.Duration("latency", latency),
            zap.String("ip", clientIP),
            zap.String("method", method),
            zap.String("user-agent", userAgent),
        )
    }
}

func (s *Server) metricsMiddleware() gin.HandlerFunc {
    httpRequests := prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total number of HTTP requests",
        },
        []string{"method", "path", "status"},
    )

    httpDuration := prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request duration in seconds",
            Buckets: prometheus.DefBuckets,
        },
        []string{"method", "path"},
    )

    s.metrics.MustRegister(httpRequests, httpDuration)

    return func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path

        c.Next()

        status := fmt.Sprintf("%d", c.Writer.Status())
        duration := time.Since(start).Seconds()

        httpRequests.WithLabelValues(c.Request.Method, path, status).Inc()
        httpDuration.WithLabelValues(c.Request.Method, path).Observe(duration)
    }
}

func (s *Server) healthCheck(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "status": "healthy",
        "time":   time.Now(),
    })
}

func main() {
    cfg := &Config{
        Port:            ":8080",
        RedisAddr:       "localhost:6379",
        RateLimit:       100,
        RateWindow:      15 * time.Minute,
        EnableMetrics:   true,
        EnableTracing:   true,
    }

    server, err := NewServer(cfg)
    if err != nil {
        log.Fatal(err)
    }

    if err := server.router.Run(cfg.Port); err != nil {
        log.Fatal(err)
    }
}

3.2 gRPC

gRPC, Google tarafından geliştirilen yüksek performanslı RPC çerçevesidir. Protocol Buffers kullanır ve HTTP/2 üzerinde çalışır.

gRPC Mimarisi

Protocol Buffers Tanımı

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
syntax = "proto3";

package user;

import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
import "google/api/annotations.proto";

service UserService {
    rpc GetUser(GetUserRequest) returns (User) {
        option (google.api.http) = {
            get: "/v1/users/{id}"
        };
    }
    
    rpc CreateUser(CreateUserRequest) returns (User) {
        option (google.api.http) = {
            post: "/v1/users"
            body: "*"
        };
    }
    
    rpc UpdateUser(UpdateUserRequest) returns (User) {
        option (google.api.http) = {
            put: "/v1/users/{id}"
            body: "*"
        };
    }
    
    rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty) {
        option (google.api.http) = {
            delete: "/v1/users/{id}"
        };
    }
    
    rpc ListUsers(ListUsersRequest) returns (stream User) {
        option (google.api.http) = {
            get: "/v1/users"
        };
    }
}

message User {
    string id = 1;
    string name = 2;
    string email = 3;
    google.protobuf.Timestamp created_at = 4;
    google.protobuf.Timestamp updated_at = 5;
    repeated string roles = 6;
    map<string, string> metadata = 7;
}

message GetUserRequest {
    string id = 1;
}

message CreateUserRequest {
    string name = 1;
    string email = 2;
    string password = 3;
    repeated string roles = 4;
    map<string, string> metadata = 5;
}

message UpdateUserRequest {
    string id = 1;
    string name = 2;
    string email = 3;
    repeated string roles = 4;
    map<string, string> metadata = 5;
}

message DeleteUserRequest {
    string id = 1;
}

message ListUsersRequest {
    int32 page_size = 1;
    string page_token = 2;
    string filter = 3;
    string order_by = 4;
}

Go gRPC Örneği

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package main

import (
    "context"
    "fmt"
    "log"
    "net"
    "time"

    pb "path/to/generated/proto"
    "github.com/go-redis/redis/v8"
    "github.com/prometheus/client_golang/prometheus"
    "go.uber.org/zap"
    grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware/v2"
    grpclogging "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
    grpcrecovery "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
    zapprovider "github.com/grpc-ecosystem/go-grpc-middleware/providers/zap"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/reflection"
    "google.golang.org/grpc/status"
    "google.golang.org/protobuf/types/known/emptypb"
    "google.golang.org/protobuf/types/known/timestamppb"
)

type server struct {
    pb.UnimplementedUserServiceServer
    redis   *redis.Client
    logger  *zap.Logger
    metrics *prometheus.Registry
}

func NewServer(redisAddr string) (*server, error) {
    rdb := redis.NewClient(&redis.Options{Addr: redisAddr})
    logger, err := zap.NewProduction()
    if err != nil { return nil, err }
    metrics := prometheus.NewRegistry()
    return &server{redis: rdb, logger: logger, metrics: metrics}, nil
}

func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    s.logger.Info("getting user", zap.String("id", req.Id))
    user, err := s.redis.HGetAll(ctx, "user:"+req.Id).Result()
    if err != nil {
        if err == redis.Nil { return nil, status.Error(codes.NotFound, "user not found") }
        return nil, status.Error(codes.Internal, err.Error())
    }
    return &pb.User{
        Id: user["id"], Name: user["name"], Email: user["email"],
        CreatedAt: parseTime(user["created_at"]), UpdatedAt: parseTime(user["updated_at"]),
    }, nil
}

func (s *server) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.User, error) {
    s.logger.Info("creating user", zap.String("email", req.Email))
    if err := validateCreateUserRequest(req); err != nil {
        return nil, status.Error(codes.InvalidArgument, err.Error())
    }
    now := time.Now()
    user := &pb.User{
        Id: generateID(), Name: req.Name, Email: req.Email,
        CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now),
        Roles: req.Roles, Metadata: req.Metadata,
    }
    if err := s.saveUser(ctx, user); err != nil {
        return nil, status.Error(codes.Internal, err.Error())
    }
    return user, nil
}

func (s *server) UpdateUser(ctx context.Context, req *pb.UpdateUserRequest) (*pb.User, error) {
    s.logger.Info("updating user", zap.String("id", req.Id))
    existing, err := s.GetUser(ctx, &pb.GetUserRequest{Id: req.Id})
    if err != nil { return nil, err }
    if req.Name != "" { existing.Name = req.Name }
    if req.Email != "" { existing.Email = req.Email }
    if req.Roles != nil { existing.Roles = req.Roles }
    if req.Metadata != nil { existing.Metadata = req.Metadata }
    existing.UpdatedAt = timestamppb.New(time.Now())
    if err := s.saveUser(ctx, existing); err != nil {
        return nil, status.Error(codes.Internal, err.Error())
    }
    return existing, nil
}

func (s *server) DeleteUser(ctx context.Context, req *pb.DeleteUserRequest) (*emptypb.Empty, error) {
    s.logger.Info("deleting user", zap.String("id", req.Id))
    if err := s.redis.Del(ctx, "user:"+req.Id).Err(); err != nil {
        return nil, status.Error(codes.Internal, err.Error())
    }
    return &emptypb.Empty{}, nil
}

func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
    s.logger.Info("listing users", zap.Int32("page_size", req.PageSize), zap.String("page_token", req.PageToken))
    keys, err := s.redis.Keys(stream.Context(), "user:*").Result()
    if err != nil { return status.Error(codes.Internal, err.Error()) }
    for _, key := range keys {
        user, err := s.redis.HGetAll(stream.Context(), key).Result()
        if err != nil { continue }
        if err := stream.Send(&pb.User{
            Id: user["id"], Name: user["name"], Email: user["email"],
            CreatedAt: parseTime(user["created_at"]), UpdatedAt: parseTime(user["updated_at"]),
        }); err != nil {
            return status.Error(codes.Internal, err.Error())
        }
    }
    return nil
}

func (s *server) saveUser(ctx context.Context, user *pb.User) error {
    return s.redis.HSet(ctx, "user:"+user.Id, map[string]interface{}{
        "id": user.Id, "name": user.Name, "email": user.Email,
        "created_at": user.CreatedAt.AsTime().Format(time.RFC3339),
        "updated_at": user.UpdatedAt.AsTime().Format(time.RFC3339),
    }).Err()
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil { log.Fatalf("failed to listen: %v", err) }
    s, err := NewServer("localhost:6379")
    if err != nil { log.Fatalf("failed to create server: %v", err) }

    interceptorLogger := zapprovider.InterceptorLogger(s.logger)
    grpcServer := grpc.NewServer(
        grpc.UnaryInterceptor(grpcmiddleware.ChainUnaryServer(
            grpclogging.UnaryServerInterceptor(interceptorLogger),
            grpcrecovery.UnaryServerInterceptor(),
        )),
        grpc.StreamInterceptor(grpcmiddleware.ChainStreamServer(
            grpclogging.StreamServerInterceptor(interceptorLogger),
            grpcrecovery.StreamServerInterceptor(),
        )),
    )

    pb.RegisterUserServiceServer(grpcServer, s)
    reflection.Register(grpcServer)
    if err := grpcServer.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) }
}

// helpers
func generateID() string { return time.Now().UTC().Format("20060102150405.000000000") }

func validateCreateUserRequest(req *pb.CreateUserRequest) error {
    if req.GetEmail() == "" || req.GetName() == "" { return fmt.Errorf("name and email are required") }
    return nil
}

func parseTime(s string) *timestamppb.Timestamp {
    if s == "" { return timestamppb.New(time.Time{}) }
    t, err := time.Parse(time.RFC3339, s)
    if err != nil { return timestamppb.New(time.Time{}) }
    return timestamppb.New(t)
}

3.3 GraphQL

GraphQL, istemcinin tam olarak ihtiyaç duyduğu veriyi talep etmesini sağlar. REST’ten farklı olarak tüm işlemler tek endpoint’ten yürütülür.

GraphQL Mimarisi

En İyi Uygulamalar

Şema tasarımı, anlamlı alan adları, pagination, fragment ve batching, sorgu karmaşıklığı takibi, rate limiting, Playground kullanımı.

Go GraphQL Örneği

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package main

import (
    "github.com/99designs/gqlgen/graphql"
    "github.com/99designs/gqlgen/graphql/handler"
    "github.com/99designs/gqlgen/graphql/playground"
    "github.com/gin-gonic/gin"
    "github.com/vektah/gqlparser/v2"
    "github.com/vektah/gqlparser/v2/ast"
)

var schema = `
    type User {
        id: ID!
        name: String!
        email: String!
        posts: [Post!]
    }
    
    type Post {
        id: ID!
        title: String!
        content: String!
        author: User!
    }
    
    type Query {
        user(id: ID!): User
        users: [User!]!
        post(id: ID!): Post
        posts: [Post!]!
    }
    
    type Mutation {
        createUser(input: CreateUserInput!): User!
        createPost(input: CreatePostInput!): Post!
    }
    
    input CreateUserInput {
        name: String!
        email: String!
    }
    
    input CreatePostInput {
        title: String!
        content: String!
        authorId: ID!
    }
`

type Resolver struct { db *Database }

func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }

type queryResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }

func (r *queryResolver) User(ctx context.Context, id string) (*User, error) { return r.db.GetUser(id) }
func (r *queryResolver) Users(ctx context.Context) ([]*User, error)      { return r.db.GetUsers() }
func (r *mutationResolver) CreateUser(ctx context.Context, input CreateUserInput) (*User, error) {
    return r.db.CreateUser(input)
}

func main() {
    r := gin.Default()
    r.GET("/", playground.Handler("GraphQL playground", "/query"))
    r.POST("/query", func(c *gin.Context) {
        h := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &Resolver{db: NewDatabase()},}))
        h.ServeHTTP(c.Writer, c.Request)
    })
    r.Run(":4000")
}

3.3.1 GraphQL Subscriptions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
    "context"
    "log"
    "net/http"
    "time"

    "github.com/99designs/gqlgen/graphql"
    "github.com/99designs/gqlgen/graphql/handler"
    "github.com/99designs/gqlgen/graphql/playground"
    "github.com/gorilla/websocket"
)

type Message struct {
    ID        string    `json:"id"`
    Content   string    `json:"content"`
    UserID    string    `json:"userId"`
    CreatedAt time.Time `json:"createdAt"`
}

type Subscription struct { messageAdded chan *Message }

func (s *Subscription) MessageAdded(ctx context.Context) (<-chan *Message, error) { return s.messageAdded, nil }

func main() {
    srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{
        Resolvers: &Resolver{ subscription: &Subscription{ messageAdded: make(chan *Message), }, },
    }))
    http.Handle("/", playground.Handler("GraphQL playground", "/query"))
    http.Handle("/query", srv)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

3.3.2 Performans Kıyasları

Birbirine denk koşullar için bölüm 1.6’daki tabloları kullanın. Aşağıda istemci tarafı minimal bir benchmark döngüsü örneği verilmiştir:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    pb "your-project/proto"
)

func benchmarkGRPC() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    conn, err := grpc.DialContext(ctx, "localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil { log.Fatalf("Failed to connect: %v", err) }
    defer conn.Close()

    client := pb.NewYourServiceClient(conn)
    ctx = context.Background()

    start := time.Now()
    for i := 0; i < 1000; i++ {
        _, err := client.YourMethod(ctx, &pb.Request{})
        if err != nil { log.Printf("Error: %v", err) }
    }
    duration := time.Since(start)
    fmt.Printf("gRPC Benchmark avg/op: %v\n", duration/time.Duration(1000))
}

func main() { benchmarkGRPC() }

3.4 WebSocket

WebSocket, istemci ve sunucu arasında kalıcı, çift yönlü bir bağlantı sağlar.

Go WebSocket Örneği

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package main

import (
    "context"
    "encoding/json"
    "errors"
    "log"
    "net/http"
    "time"

    "github.com/go-redis/redis/v8"
    "github.com/golang-jwt/jwt"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true } }

type Client struct {
    conn *websocket.Conn; userID string; send chan []byte; redis *redis.Client
}

type Message struct {
    Type string `json:"type"`; UserID string `json:"userId"`; Content string `json:"content,omitempty"`; Title string `json:"title,omitempty"`; Message string `json:"message,omitempty"`; Status string `json:"status,omitempty"`; Timestamp int64 `json:"timestamp"`
}

var clients = make(map[*Client]struct{})

func main() {
    rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379" })
    http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Sec-WebSocket-Protocol")
        claims, err := validateToken(token)
        if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized); return }

        conn, err := upgrader.Upgrade(w, r, nil)
        if err != nil { log.Println(err); return }

        client := &Client{ conn: conn, userID: claims.UserID, send: make(chan []byte, 256), redis: rdb }
        clients[client] = struct{}{}
        client.redis.SAdd(context.Background(), "online_users", client.userID)

        go client.readPump(); go client.writePump()
    })
    log.Fatal(http.ListenAndServe(":8080", nil))
}

type tokenClaims struct { UserID string }
func validateToken(raw string) (*tokenClaims, error) { if raw == "" { return nil, errors.New("empty token") }; return &tokenClaims{UserID: raw}, nil }

func (c *Client) readPump() {
    defer func() { c.conn.Close(); delete(clients, c); c.redis.SRem(context.Background(), "online_users", c.userID) }()
    for {
        _, message, err := c.conn.ReadMessage()
        if err != nil { if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { log.Printf("error: %v", err) }; break }
        var msg Message
        if err := json.Unmarshal(message, &msg); err != nil { log.Printf("error: %v", err); continue }
        switch msg.Type { case "chat": handleChatMessage(c, msg); case "notification": handleNotification(c, msg); case "status": handleStatusUpdate(c, msg); default: log.Printf("unknown message type: %s", msg.Type) }
    }
}

func (c *Client) writePump() {
    ticker := time.NewTicker(30 * time.Second)
    defer func() { ticker.Stop(); c.conn.Close() }()
    for {
        select {
        case message, ok := <-c.send:
            if !ok { c.conn.WriteMessage(websocket.CloseMessage, []byte{}); return }
            w, err := c.conn.NextWriter(websocket.TextMessage); if err != nil { return }
            w.Write(message); if err := w.Close(); err != nil { return }
        case <-ticker.C:
            if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil { return }
        }
    }
}

3.5 gRPC‑Web

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package main

import (
    "context"
    "log"
    "net"
    "net/http"

    "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "google.golang.org/protobuf/encoding/protojson"

    pb "path/to/generated/proto"
)

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{})

    go func() {
        log.Printf("starting gRPC server on %s", lis.Addr().String())
        if err := s.Serve(lis); err != nil {
            log.Fatalf("failed to serve: %v", err)
        }
    }()

    conn, err := grpc.Dial(
        lis.Addr().String(),
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
        log.Fatalf("failed to dial server: %v", err)
    }
    defer conn.Close()

    gwmux := runtime.NewServeMux(
        runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
            MarshalOptions: protojson.MarshalOptions{
                UseProtoNames: true,
            },
        }),
    )

    if err := pb.RegisterUserServiceHandler(context.Background(), gwmux, conn); err != nil {
        log.Fatalf("failed to register gateway: %v", err)
    }

    mux := http.NewServeMux()
    mux.Handle("/", gwmux)

    log.Printf("starting HTTP server on %s", ":8080")
    if err := http.ListenAndServe(":8080", mux); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

3.6 tRPC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import (
    "context"
    "log"
    "net"
    "net/http"
    "os"

    "github.com/your-project/proto"
    "github.com/your-project/service"
    "github.com/your-project/transport"
)

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

    svc := &service.UserService{}

    go func() {
        log.Printf("starting tRPC server on %s", lis.Addr().String())
        if err := transport.Serve(lis, svc); err != nil {
            log.Fatalf("failed to serve: %v", err)
        }
    }()

    conn, err := transport.Dial(context.Background(), lis.Addr().String())
    if err != nil {
        log.Fatalf("failed to dial server: %v", err)
    }
    defer conn.Close()

    client := proto.NewUserServiceClient(conn)
    _ = client
}

4. API Güvenliği

4.1 OWASP API Security Top 10

1
2
3
4
5
6
7
8
9
// BOLA: Nesne erişim kontrolü
func (s *UserService) GetUser(ctx context.Context, userID string, requesterID string) (*User, error) {
    if userID != requesterID && !s.hasAdminRole(requesterID) {
        return nil, errors.New("unauthorized access")
    }
    user, err := s.repo.GetUser(ctx, userID)
    if err != nil { return nil, err }
    return user, nil
}
1
2
3
4
5
6
7
8
9
// Kırık kimlik doğrulama: güçlü parola, MFA, brute force koruması
func (s *AuthService) Authenticate(ctx context.Context, credentials *Credentials) (*Token, error) {
    if s.isRateLimited(credentials.Username) { return nil, errors.New("too many attempts") }
    if !s.validatePasswordPolicy(credentials.Password) { return nil, errors.New("password too weak") }
    if s.requiresMFA(credentials.Username) {
        if !s.validateMFA(credentials.Username, credentials.MFAToken) { return nil, errors.New("invalid MFA token") }
    }
    return s.generateToken(credentials)
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Aşırı veri ifşası: maskeleme/filtreleme
func (s *UserService) GetUserProfile(ctx context.Context, userID string) (*UserProfile, error) {
    user, err := s.repo.GetUser(ctx, userID)
    if err != nil { return nil, err }
    return &UserProfile{
        ID: user.ID,
        Username: user.Username,
        Email: maskEmail(user.Email),
        Phone: maskPhone(user.Phone),
    }, nil
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Kaynak ve oran sınırları (dağıtık): Redis ile
func (s *RateLimiter) CheckLimit(ctx context.Context, key string) error {
    pipe := s.redis.Pipeline()
    ipKey := fmt.Sprintf("ip:%s", key); pipe.Incr(ctx, ipKey); pipe.Expire(ctx, ipKey, time.Minute)
    userKey := fmt.Sprintf("user:%s", key); pipe.Incr(ctx, userKey); pipe.Expire(ctx, userKey, time.Hour)
    endpointKey := fmt.Sprintf("endpoint:%s", key); pipe.Incr(ctx, endpointKey); pipe.Expire(ctx, endpointKey, 30*time.Second)
    results, err := pipe.Exec(ctx); if err != nil { return err }
    if results[0].Val().(int64) > s.ipLimit || results[1].Val().(int64) > s.userLimit || results[2].Val().(int64) > s.endpointLimit {
        return errors.New("rate limit exceeded")
    }
    return nil
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// BFLA: Rol tabanlı yetkilendirme (RBAC)
func (s *AdminService) RequireRole(roles ...string) gin.HandlerFunc {
    return func(c *gin.Context) {
        user, exists := c.Get("user")
        if !exists { c.AbortWithStatus(http.StatusUnauthorized); return }
        hasRole := false
        for _, role := range roles { if s.hasRole(user.(*User), role) { hasRole = true; break } }
        if !hasRole { c.AbortWithStatus(http.StatusForbidden); return }
        c.Next()
    }
}

4.2 Güvenlik Başlıkları ve Koruma

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Güvenlik başlıkları middleware
func SecurityHeaders() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Header("Content-Security-Policy", "default-src 'self'")
        c.Header("X-XSS-Protection", "1; mode=block")
        c.Header("X-Frame-Options", "DENY")
        c.Header("X-Content-Type-Options", "nosniff")
        c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
        c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
        c.Header("Feature-Policy", "geolocation 'none'; microphone 'none'; camera 'none'")
        c.Next()
    }
}

4.3 Girdi Doğrulama ve Temizleme

1
2
3
4
5
6
7
8
func (s *UserService) CreateUser(ctx context.Context, input *CreateUserInput) (*User, error) {
    input.Username = html.EscapeString(input.Username)
    input.Email = html.EscapeString(input.Email)
    if !s.validateSQLInjection(input) { return nil, errors.New("invalid input") }
    if !s.validateCommandInjection(input) { return nil, errors.New("invalid input") }
    if input.Avatar != nil { if !s.validateFileUpload(input.Avatar) { return nil, errors.New("invalid file") } }
    return s.repo.CreateUser(ctx, input)
}

4.4 Güvenlik Testleri

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func TestSecurity(t *testing.T) {
    t.Run("SQL Injection", func(t *testing.T) {
        payloads := []string{"' OR '1'='1", "'; DROP TABLE users; --", "' UNION SELECT * FROM users; --"}
        for _, payload := range payloads {
            resp, err := http.Post("/api/users", "application/json", strings.NewReader(fmt.Sprintf(`{"username": "%s"}`, payload)))
            assert.NoError(t, err); assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
        }
    })
    t.Run("XSS", func(t *testing.T) {
        payloads := []string{"<script>alert('xss')</script>", "javascript:alert('xss')", "<img src=x onerror=alert('xss')>"}
        for _, payload := range payloads {
            resp, err := http.Post("/api/comments", "application/json", strings.NewReader(fmt.Sprintf(`{"content": "%s"}`, payload)))
            assert.NoError(t, err); assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
        }
    })
    t.Run("Rate Limiting", func(t *testing.T) {
        for i := 0; i < 101; i++ {
            resp, err := http.Get("/api/users")
            assert.NoError(t, err)
            if i < 100 { assert.Equal(t, http.StatusOK, resp.StatusCode) } else { assert.Equal(t, http.StatusTooManyRequests, resp.StatusCode) }
        }
    })
}

4.5 Güvenlik Denetim Kontrol Listesi

  • Kimlik doğrulama/Yetkilendirme, Veri güvenliği, API güvenliği, Altyapı güvenliği, Sürekli güvenlik

5. API Versiyonlama Stratejileri

5.1 URL Tabanlı Versiyonlama

1
2
3
4
5
func main() {
    r := gin.Default()
    v1 := r.Group("/api/v1"); { v1.GET("/users", getUsersV1); v1.POST("/users", createUserV1) }
    v2 := r.Group("/api/v2"); { v2.GET("/users", getUsersV2); v2.POST("/users", createUserV2) }
}

5.2 Header Tabanlı Versiyonlama

1
2
3
4
5
6
func versionMiddleware(version string) gin.HandlerFunc { return func(c *gin.Context) { c.Set("api_version", version); c.Next() } }
func main() {
    r := gin.Default()
    v1 := r.Group("/api"); v1.Use(versionMiddleware("v1")); { v1.GET("/users", getUsers); v1.POST("/users", createUser) }
    v2 := r.Group("/api"); v2.Use(versionMiddleware("v2")); { v2.GET("/users", getUsers); v2.POST("/users", createUser) }
}

5.3 Content‑Type Tabanlı Versiyonlama

1
2
3
4
5
6
7
func contentTypeVersionMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        contentType := c.GetHeader("Content-Type")
        if strings.Contains(contentType, "application/vnd.company.v2+json") { c.Set("api_version", "v2") } else { c.Set("api_version", "v1") }
        c.Next()
    }
}

6. API Kullanım ve Gelir Modellemesi

6.1 Kullanım Takibi

1
2
3
4
5
6
7
8
type UsageMetrics struct {
    UserID string `json:"user_id"`; Endpoint string `json:"endpoint"`; Method string `json:"method"`; Timestamp time.Time `json:"timestamp"`; ResponseTime int64 `json:"response_time"`; StatusCode int `json:"status_code"`
}
func trackUsage(c *gin.Context) {
    start := time.Now(); c.Next()
    metrics := UsageMetrics{ UserID: c.GetString("user_id"), Endpoint: c.Request.URL.Path, Method: c.Request.Method, Timestamp: time.Now(), ResponseTime: time.Since(start).Milliseconds(), StatusCode: c.Writer.Status() }
    sendMetrics(metrics)
}

6.2 Oran Sınırlama ve Kotalar

1
2
3
4
5
6
7
8
type RateLimiter struct { redis *redis.Client }
func (rl *RateLimiter) CheckLimit(ctx context.Context, key string, limit int, window time.Duration) error {
    current, err := rl.redis.Incr(ctx, key).Result(); if err != nil { return err }
    if current == 1 { rl.redis.Expire(ctx, key, window) }
    if current > int64(limit) { return errors.New("rate limit exceeded") }
    return nil
}
func (rl *RateLimiter) GetQuota(ctx context.Context, userID string) (int, error) { plan, err := getUserPlan(ctx, userID); if err != nil { return 0, err }; return plan.Quota, nil }

6.3 Faturalama ve Ödeme Entegrasyonu

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type BillingService struct { stripe *stripe.Client }
func (bs *BillingService) CreateSubscription(ctx context.Context, userID string, planID string) error {
    params := &stripe.SubscriptionParams{ Customer: stripe.String(userID), Items: []*stripe.SubscriptionItemsParams{{ Price: stripe.String(planID), }}, }
    _, err := bs.stripe.Subscriptions.New(params); return err
}
func (bs *BillingService) HandleWebhook(ctx context.Context, payload []byte, signature string) error {
    event, err := webhook.ConstructEvent(payload, signature, "your_webhook_secret"); if err != nil { return err }
    switch event.Type { case "invoice.paid": case "invoice.payment_failed": }
    return nil
}

7. API Test Stratejileri

7.1 Birim Testleri

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Birim testleri
func TestUserService(t *testing.T) {
    mockRepo := &MockUserRepository{}; service := NewUserService(mockRepo)
    t.Run("CreateUser", func(t *testing.T) {
        input := &CreateUserInput{ Username: "testuser", Email: "test@example.com", Password: "password123" }
        mockRepo.On("CreateUser", mock.Anything, input).Return(&User{ ID: "123", Username: input.Username, Email: input.Email, }, nil)
        user, err := service.CreateUser(context.Background(), input)
        assert.NoError(t, err); assert.Equal(t, input.Username, user.Username); assert.Equal(t, input.Email, user.Email)
        input.Email = "invalid-email"; _, err = service.CreateUser(context.Background(), input); assert.Error(t, err); assert.Contains(t, err.Error(), "invalid email")
    })
    t.Run("GetUser", func(t *testing.T) {
        mockRepo.On("GetUser", mock.Anything, "123").Return(&User{ ID: "123", Username: "testuser", Email: "test@example.com" }, nil)
        user, err := service.GetUser(context.Background(), "123"); assert.NoError(t, err); assert.Equal(t, "123", user.ID)
        mockRepo.On("GetUser", mock.Anything, "456").Return(nil, errors.New("user not found"))
        _, err = service.GetUser(context.Background(), "456"); assert.Error(t, err); assert.Contains(t, err.Error(), "user not found")
    })
}

7.2 Entegrasyon Testleri

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func TestAPI(t *testing.T) {
    server := setupTestServer(); defer server.Close()
    t.Run("User Flow", func(t *testing.T) {
        createResp, err := http.Post(server.URL+"/api/users", "application/json", strings.NewReader(`{ "username": "testuser", "email": "test@example.com", "password": "password123" }`))
        assert.NoError(t, err); assert.Equal(t, http.StatusCreated, createResp.StatusCode)
        var user User; err = json.NewDecoder(createResp.Body).Decode(&user); assert.NoError(t, err)
        getResp, err := http.Get(server.URL + "/api/users/" + user.ID); assert.NoError(t, err); assert.Equal(t, http.StatusOK, getResp.StatusCode)
        updateResp, err := http.Put(server.URL+"/api/users/"+user.ID, "application/json", strings.NewReader(`{ "email": "updated@example.com" }`)); assert.NoError(t, err); assert.Equal(t, http.StatusOK, updateResp.StatusCode)
        deleteResp, err := http.Delete(server.URL + "/api/users/" + user.ID); assert.NoError(t, err); assert.Equal(t, http.StatusNoContent, deleteResp.StatusCode)
    })
}

7.3 Yük Testleri

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func TestLoad(t *testing.T) {
    server := setupTestServer(); defer server.Close()
    concurrentUsers, requestsPerUser := 100, 10
    var totalRequests, successfulRequests, failedRequests int64; var totalDuration time.Duration
    start := time.Now(); var wg sync.WaitGroup
    for i := 0; i < concurrentUsers; i++ {
        wg.Add(1); go func() {
            defer wg.Done(); for j := 0; j < requestsPerUser; j++ {
                s := time.Now(); resp, err := http.Get(server.URL + "/api/users"); atomic.AddInt64(&totalRequests, 1)
                if err != nil { atomic.AddInt64(&failedRequests, 1); continue }
                if resp.StatusCode == http.StatusOK { atomic.AddInt64(&successfulRequests, 1) } else { atomic.AddInt64(&failedRequests, 1) }
                atomic.AddInt64((*int64)(&totalDuration), int64(time.Since(s))); time.Sleep(100 * time.Millisecond)
            }
        }()
    }
    wg.Wait(); totalTime := time.Since(start)
    avg := time.Duration(int64(totalDuration) / totalRequests); rps := float64(totalRequests) / totalTime.Seconds()
    assert.True(t, float64(successfulRequests)/float64(totalRequests) > 0.95)
    assert.True(t, avg < 200*time.Millisecond)
    assert.True(t, rps > 100)
}

7.4 Fuzz Testleri

1
2
3
4
5
6
7
8
func FuzzUserInput(f *testing.F) {
    f.Add("testuser", "test@example.com", "password123"); f.Add("", "", ""); f.Add("a", "b", "c")
    f.Fuzz(func(t *testing.T, username, email, password string) {
        input := &CreateUserInput{ Username: username, Email: email, Password: password }
        service := NewUserService(&MockUserRepository{}); user, err := service.CreateUser(context.Background(), input)
        if err == nil { assert.NotEmpty(t, user.ID); assert.Equal(t, username, user.Username); assert.Equal(t, email, user.Email) } else { assert.Contains(t, err.Error(), "validation") }
    })
}

7.5 Kaos Testleri

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func TestChaos(t *testing.T) {
    server := setupTestServer(); defer server.Close()
    t.Run("Network Issues", func(t *testing.T) {
        server.SetLatency(500 * time.Millisecond); s := time.Now(); resp, err := http.Get(server.URL + "/api/users"); d := time.Since(s)
        assert.NoError(t, err); assert.Equal(t, http.StatusOK, resp.StatusCode); assert.True(t, d >= 500*time.Millisecond)
        server.SetPacketLoss(0.1); for i := 0; i < 100; i++ { resp, err := http.Get(server.URL + "/api/users"); if err != nil { continue }; assert.Equal(t, http.StatusOK, resp.StatusCode) }
        server.SetConnectionDrop(true); _, err = http.Get(server.URL + "/api/users"); assert.Error(t, err)
    })
    t.Run("Resource Exhaustion", func(t *testing.T) {
        server.SetCPUUsage(0.9); s := time.Now(); resp, err := http.Get(server.URL + "/api/users"); d := time.Since(s)
        assert.NoError(t, err); assert.Equal(t, http.StatusOK, resp.StatusCode); assert.True(t, d < 1*time.Second)
        server.SetMemoryUsage(0.9); resp, err = http.Get(server.URL + "/api/users"); assert.NoError(t, err); assert.Equal(t, http.StatusOK, resp.StatusCode)
    })
}

7.6 CI/CD Entegrasyonu

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# .github/workflows/api-tests.yml
name: API Tests
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      redis:
        image: redis
        ports: ["6379:6379"]
      postgres:
        image: postgres
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: test
        ports: ["5432:5432"]
    steps:
    - uses: actions/checkout@v2
    - name: Set up Go
      uses: actions/setup-go@v2
      with:
        go-version: 1.19
    - name: Install dependencies
      run: go mod download
    - name: Run unit tests
      run: go test -v ./... -short
    - name: Run integration tests
      run: go test -v ./... -tags=integration
    - name: Run load tests
      run: go test -v ./... -tags=load
    - name: Run fuzzing tests
      run: go test -v ./... -fuzz=Fuzz
    - name: Run chaos tests
      run: go test -v ./... -tags=chaos
    - name: Upload test results
      uses: actions/upload-artifact@v2
      with:
        name: test-results
        path: test-results/

8. Gözlemlenebilirlik ve İzleme

8.1 Hizmet Düzeyi Hedefleri (SLO)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type SLO struct { Name, Description, Metric string; Target float64; Window time.Duration }
var SLODefinitions = []SLO{
    { Name: "Availability", Description: "API availability should be 99.9%", Target: 0.999, Window: 30*24*time.Hour, Metric: "availability" },
    { Name: "Latency", Description: "95th percentile latency should be under 200ms", Target: 0.200, Window: 1*time.Hour, Metric: "p95_latency" },
    { Name: "Error Rate", Description: "Error rate should be under 0.1%", Target: 0.001, Window: 1*time.Hour, Metric: "error_rate" },
}
func (m *Monitor) CheckSLOs(ctx context.Context) []SLOViolation {
    var violations []SLOViolation
    for _, slo := range SLODefinitions {
        value := m.getMetricValue(ctx, slo.Metric, slo.Window)
        if !m.isSLOHealthy(slo, value) { violations = append(violations, SLOViolation{ SLO: slo, Value: value }) }
    }
    return violations
}

8.2 Dağıtık İzleme (OpenTelemetry)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func setupTracing() (*sdktrace.TracerProvider, error) {
    exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
    if err != nil { return nil, err }
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithSampler(sdktrace.AlwaysSample()),
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(resource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceNameKey.String("api-service"), semconv.ServiceVersionKey.String("1.0.0"))),
    )
    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
    return tp, nil
}

func TracingMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx := otel.GetTextMapPropagator().Extract(c.Request.Context(), propagation.HeaderCarrier(c.Request.Header))
        ctx, span := otel.Tracer("api").Start(ctx, fmt.Sprintf("%s %s", c.Request.Method, c.Request.URL.Path))
        defer span.End()
        span.SetAttributes(attribute.String("http.method", c.Request.Method), attribute.String("http.url", c.Request.URL.String()), attribute.String("http.user_agent", c.Request.UserAgent()))
        c.Request = c.Request.WithContext(ctx); c.Next()
        span.SetAttributes(attribute.Int("http.status_code", c.Writer.Status()), attribute.Int64("http.response_size", c.Writer.Size()))
        if c.Writer.Status() >= 400 { span.SetStatus(codes.Error, "HTTP error"); span.RecordError(errors.New("request failed")) }
    }
}

8.3 Log Yönetimi (Zap)

1
2
3
4
5
func setupLogger() (*zap.Logger, error) {
    level := zap.NewAtomicLevel(); level.SetLevel(zap.InfoLevel)
    cfg := zap.Config{ Level: level, Development: false, Sampling: &zap.SamplingConfig{ Initial: 100, Thereafter: 100 }, Encoding: "json", EncoderConfig: zap.NewProductionEncoderConfig(), OutputPaths: []string{"stdout", "/var/log/api.log"}, ErrorOutputPaths: []string{"stderr"} }
    l, err := cfg.Build(); if err != nil { return nil, err }; zap.ReplaceGlobals(l); return l, nil
}

8.4 Metrik Toplama (Prometheus)

1
2
3
4
5
6
7
func setupMetrics() *prometheus.Registry {
    r := prometheus.NewRegistry()
    httpRequests := prometheus.NewCounterVec(prometheus.CounterOpts{Name: "http_requests_total", Help: "Total number of HTTP requests"}, []string{"method", "path", "status"})
    httpDuration := prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: "http_request_duration_seconds", Help: "HTTP request duration in seconds", Buckets: prometheus.DefBuckets}, []string{"method", "path"})
    activeConnections := prometheus.NewGauge(prometheus.GaugeOpts{Name: "http_active_connections", Help: "Number of active HTTP connections"})
    r.MustRegister(httpRequests, httpDuration, activeConnections); return r
}

8.5 Alarm Sistemi

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Alert yapılandırması
type AlertRule struct {
    Name        string
    Description string
    Condition   func(metrics map[string]float64) bool
    Severity    string
    Threshold   float64
}

var AlertRules = []AlertRule{
    {
        Name:        "High Error Rate",
        Description: "Error rate is above threshold",
        Condition: func(metrics map[string]float64) bool {
            return metrics["error_rate"] > 0.01 // %1
        },
        Severity:  "critical",
        Threshold: 0.01,
    },
    {
        Name:        "High Latency",
        Description: "95th percentile latency is above threshold",
        Condition: func(metrics map[string]float64) bool {
            return metrics["p95_latency"] > 0.5 // 500ms
        },
        Severity:  "warning",
        Threshold: 0.5,
    },
    {
        Name:        "Low Availability",
        Description: "Service availability is below threshold",
        Condition: func(metrics map[string]float64) bool {
            return metrics["availability"] < 0.99 // %99
        },
        Severity:  "critical",
        Threshold: 0.99,
    },
}

// Alert yönetimi
func (m *Monitor) CheckAlerts(ctx context.Context) []Alert {
    var alerts []Alert
    // Metrikleri topla
    metrics := m.collectMetrics(ctx)
    // Kuralları kontrol et
    for _, rule := range AlertRules {
        if rule.Condition(metrics) {
            alerts = append(alerts, Alert{
                Rule:      rule,
                Timestamp: time.Now(),
                Metrics:   metrics,
            })
        }
    }
    return alerts
}

// Alert gönderme
func (m *Monitor) SendAlert(alert Alert) error {
    // AlertManager'a gönder
    payload := map[string]interface{}{
        "alerts": []map[string]interface{}{
            {
                "labels": map[string]string{
                    "alertname": alert.Rule.Name,
                    "severity":  alert.Rule.Severity,
                },
                "annotations": map[string]string{
                    "description": alert.Rule.Description,
                    "value":       fmt.Sprintf("%.2f", alert.Metrics[alert.Rule.Name]),
                },
                "startsAt": alert.Timestamp,
            },
        },
    }

    resp, err := http.Post(
        "http://alertmanager:9093/api/v1/alerts",
        "application/json",
        bytes.NewBuffer(mustMarshal(payload)),
    )
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return fmt.Errorf("alertmanager returned status %d", resp.StatusCode)
    }
    return nil
}

8.6 Pano (Grafana)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: 1
providers:
  - name: 'API Metrics'
    orgId: 1
    folder: 'API Monitoring'
    type: file
    disableDeletion: false
    editable: true
    options:
      path: /var/lib/grafana/dashboards

9. Ölçekleme Stratejileri

9.1 Yatay Ölçekleme

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
    "context"
    "fmt"
    "time"
    "github.com/go-redis/redis/v8"
)

type Service struct {
    ID       string
    Host     string
    Port     string
    Status   string
    Load     string
}

type ServiceRegistry struct { redis *redis.Client }

func NewServiceRegistry(redisAddr string) *ServiceRegistry {
    return &ServiceRegistry{ redis: redis.NewClient(&redis.Options{ Addr: redisAddr }) }
}

func (sr *ServiceRegistry) RegisterService(ctx context.Context, service *Service) error {
    key := fmt.Sprintf("service:%s", service.ID)
    data := map[string]interface{}{
        "id": service.ID, "host": service.Host, "port": service.Port,
        "status": service.Status, "load": service.Load, "updatedAt": time.Now(),
    }
    return sr.redis.HSet(ctx, key, data).Err()
}

func (sr *ServiceRegistry) GetServices(ctx context.Context) ([]*Service, error) {
    keys, err := sr.redis.Keys(ctx, "service:*").Result()
    if err != nil { return nil, err }
    var services []*Service
    for _, key := range keys {
        data, err := sr.redis.HGetAll(ctx, key).Result(); if err != nil { continue }
        services = append(services, &Service{ ID: data["id"], Host: data["host"], Port: data["port"], Status: data["status"], Load: data["load"], })
    }
    return services, nil
}

9.2 Yük Dengeleme

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
    "context"
    "errors"
    "math"
    "math/rand"
)

type LoadBalancer struct { registry *ServiceRegistry; strategy string }

func NewLoadBalancer(registry *ServiceRegistry) *LoadBalancer { return &LoadBalancer{ registry: registry } }

func (lb *LoadBalancer) GetNextService(ctx context.Context) (*Service, error) {
    services, err := lb.registry.GetServices(ctx); if err != nil { return nil, err }
    if len(services) == 0 { return nil, errors.New("no services available") }
    switch lb.strategy { case "round-robin": return lb.roundRobin(ctx, services); case "least-connections": return lb.leastConnections(services); case "weighted": return lb.weighted(services); default: return lb.random(services) }
}

func (lb *LoadBalancer) roundRobin(ctx context.Context, services []*Service) (*Service, error) {
    key := "lb:round-robin:index"
    index, err := lb.registry.redis.Incr(ctx, key).Result(); if err != nil { return nil, err }
    i := int(index % int64(len(services)))
    return services[i], nil
}

func (lb *LoadBalancer) leastConnections(services []*Service) (*Service, error) {
    var selected *Service; min := math.MaxInt32
    for _, s := range services { var c int; fmt.Sscanf(s.Load, "%d", &c); if c < min { min = c; selected = s } }
    return selected, nil
}

func (lb *LoadBalancer) weighted(services []*Service) (*Service, error) {
    total := 0; weights := make([]int, len(services))
    for i := range services { weights[i] = 1; total += weights[i] }
    r := rand.Intn(total); cur := 0
    for i, w := range weights { cur += w; if r < cur { return services[i], nil } }
    return services[0], nil
}

func (lb *LoadBalancer) random(services []*Service) (*Service, error) { return services[rand.Intn(len(services))], nil }

9.3 Servis Keşfi

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
    "context"
    "encoding/json"
    clientv3 "go.etcd.io/etcd/client/v3"
)

type ServiceDiscovery struct { registry *ServiceRegistry; etcd *clientv3.Client }

func (sd *ServiceDiscovery) Register(ctx context.Context, service *Service) error {
    key := "/services/" + service.ID; b, err := json.Marshal(service); if err != nil { return err }
    _, err = sd.etcd.Put(ctx, key, string(b)); return err
}

func (sd *ServiceDiscovery) Discover(ctx context.Context, serviceType string) ([]*Service, error) {
    resp, err := sd.etcd.Get(ctx, "/services/"+serviceType, clientv3.WithPrefix()); if err != nil { return nil, err }
    var services []*Service
    for _, kv := range resp.Kvs { var s Service; if json.Unmarshal(kv.Value, &s) == nil { services = append(services, &s) } }
    return services, nil
}

func (sd *ServiceDiscovery) Watch(ctx context.Context, serviceType string) (<-chan []*Service, error) {
    ch := make(chan []*Service)
    go func() { watchCh := sd.etcd.Watch(ctx, "/services/"+serviceType, clientv3.WithPrefix()); for range watchCh { svcs, _ := sd.Discover(ctx, serviceType); ch <- svcs } }()
    return ch, nil
}

9.4 Oto Ölçekleme

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
    "context"
    "log"
    "time"
)

type AutoScaler struct { registry *ServiceRegistry; metrics *MetricsCollector; config *ScalingConfig }
type MetricsCollector struct{}
type ScalingConfig struct { MinInstances, MaxInstances int; TargetCPUUsage, TargetMemoryUsage, ScaleUpThreshold, ScaleDownThreshold float64; CooldownPeriod time.Duration }

func (mc *MetricsCollector) GetMetrics(ctx context.Context) (*struct{ Services []string; AverageCPUUsage float64 }, error) {
    return &struct{ Services []string; AverageCPUUsage float64 }{ Services: []string{"a","b"}, AverageCPUUsage: 0.6 }, nil
}

func (as *AutoScaler) Start(ctx context.Context) error {
    t := time.NewTicker(time.Minute); defer t.Stop()
    for { select { case <-ctx.Done(): return nil; case <-t.C: if err := as.checkAndScale(ctx); err != nil { log.Printf("Scaling error: %v", err) } } }
}

func (as *AutoScaler) checkAndScale(ctx context.Context) error {
    m, err := as.metrics.GetMetrics(ctx); if err != nil { return err }
    cur := len(m.Services); target := cur
    if m.AverageCPUUsage > as.config.ScaleUpThreshold { if cur < as.config.MaxInstances { target = cur + 1 } } else if m.AverageCPUUsage < as.config.ScaleDownThreshold { if cur > as.config.MinInstances { target = cur - 1 } }
    if target != cur { return as.scale(ctx, target) }
    return nil
}

func (as *AutoScaler) scale(ctx context.Context, target int) error { return nil }

9.5 Performans Karşılaştırması ve 9.6 Maliyet Analizi

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import "time"

type PerformanceMetrics struct { ResponseTime time.Duration; Throughput, ErrorRate float64; ResourceUsage map[string]float64 }

func (pm *PerformanceMetrics) Compare(other *PerformanceMetrics) map[string]float64 {
    return map[string]float64{
        "response_time_improvement": float64(pm.ResponseTime) / float64(other.ResponseTime),
        "throughput_improvement":    pm.Throughput / other.Throughput,
        "error_rate_improvement":    other.ErrorRate / pm.ErrorRate,
        "cpu_usage_improvement":     other.ResourceUsage["cpu"] / pm.ResourceUsage["cpu"],
        "memory_usage_improvement":  other.ResourceUsage["memory"] / pm.ResourceUsage["memory"],
    }
}

type ScalingConfig struct { MinInstances, MaxInstances int; TargetCPUUsage float64 }
var ScalingScenarios = []struct { Name, Description string; Config *ScalingConfig; Expected *PerformanceMetrics }{
    { Name: "Vertical Scaling", Description: "Tek sunucu", Config: &ScalingConfig{ MinInstances:1, MaxInstances:1, TargetCPUUsage:0.7 }, Expected: &PerformanceMetrics{ ResponseTime: 100*time.Millisecond, Throughput: 1000, ErrorRate: 0.001, ResourceUsage: map[string]float64{"cpu":0.7, "memory":0.6} } },
}

type CostAnalysis struct { InfrastructureCost, OperationalCost, MaintenanceCost, TotalCost float64 }

func (ca *CostAnalysis) CalculateCosts(config *ScalingConfig, metrics *PerformanceMetrics) *CostAnalysis {
    instanceCost := 100.0
    infrastructureCost := instanceCost * float64(config.MaxInstances)
    operationalCost := infrastructureCost * 0.2
    maintenanceCost := infrastructureCost * 0.1
    totalCost := infrastructureCost + operationalCost + maintenanceCost
    return &CostAnalysis{ InfrastructureCost: infrastructureCost, OperationalCost: operationalCost, MaintenanceCost: maintenanceCost, TotalCost: totalCost }
}

10. Dağıtım Stratejileri

10.1 Blue‑Green

1
2
3
4
5
6
7
8
9
package main

type DeploymentManager struct { redis *redis.Client }

func (dm *DeploymentManager) DeployNewVersion(version string) error {
    if err := dm.deployVersion(version); err != nil { return err }
    if err := dm.healthCheck(version); err != nil { dm.rollback(version); return err }
    return dm.switchTraffic(version)
}

10.2 Canary

1
2
3
4
5
type CanaryManager struct { redis *redis.Client }
func (cm *CanaryManager) DeployCanary(version string, percentage float64) error {
    if err := cm.deployVersion(version); err != nil { return err }
    return cm.setTrafficPercentage(version, percentage)
}

10.3 Rolling Update ve 10.4 Feature Flags

1
2
3
4
5
type RollingUpdateManager struct { redis *redis.Client }
func (rum *RollingUpdateManager) Update(version string, batchSize int) error { return nil }

type FeatureFlagManager struct { redis *redis.Client }
func (ffm *FeatureFlagManager) IsFeatureEnabled(feature, userID string) (bool, error) { return true, nil }

10.5 Kubernetes ve 10.6 Sunucusuz

1
2
3
4
5
type KubernetesManager struct { clientset *kubernetes.Clientset; config *KubernetesConfig }
type KubernetesConfig struct { Namespace, DeploymentName, ImageName string }

type ServerlessManager struct { lambda *lambda.Client; config *ServerlessConfig }
type ServerlessConfig struct { FunctionName, Handler, Runtime string }

11. API Gateway ve Service Mesh

11.1 API Gateway

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
type Gateway struct {
    routes  []Route
    metrics *prometheus.CounterVec
    config  *GatewayConfig
}

type GatewayConfig struct {
    RateLimiting    *RateLimitingConfig
    Authentication  *AuthenticationConfig
    LoadBalancing   *LoadBalancingConfig
    CircuitBreaker  *CircuitBreakerConfig
    Logging         *LoggingConfig
}

type Route struct {
    Path       string
    Methods    []string
    Service    string
    Version    string
    Middleware []gin.HandlerFunc
    Policies   []Policy
}

func (g *Gateway) AddRoute(route Route) { g.routes = append(g.routes, route) }

func (g *Gateway) Start() error {
    router := gin.Default()
    router.Use(g.metricsMiddleware())
    router.Use(g.loggingMiddleware())
    router.Use(g.recoveryMiddleware())
    for _, route := range g.routes {
        for _, method := range route.Methods {
            router.Handle(method, route.Path, g.handleRequest(route))
        }
    }
    return router.Run(":8080")
}

func (g *Gateway) handleRequest(route Route) gin.HandlerFunc {
    return func(c *gin.Context) {
        for _, mw := range route.Middleware { mw(c) }
        for _, p := range route.Policies { if err := p.Apply(c); err != nil { c.JSON(400, gin.H{"error": err.Error()}); return } }
        endpoint, err := g.getServiceEndpoint(route.Service, route.Version)
        if err != nil { c.JSON(500, gin.H{"error": "Service not available"}); return }
        resp, err := g.forwardRequest(endpoint, c.Request)
        if err != nil { c.JSON(500, gin.H{"error": "Failed to forward request"}); return }
        g.metrics.WithLabelValues(route.Service, c.Request.Method, string(resp.StatusCode)).Inc()
        c.DataFromReader(resp.StatusCode, resp.ContentLength, resp.Header.Get("Content-Type"), resp.Body, nil)
    }
}

11.2 Service Mesh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
type ServiceMesh struct {
    services map[string]*Service
    config   *MeshConfig
    metrics  *MeshMetrics
    tracer   *MeshTracer
}

type Service struct {
    Name      string
    Endpoints []string
    Health    *HealthCheck
    Policies  []Policy
}

type MeshConfig struct {
    RetryPolicy    *RetryPolicy
    CircuitBreaker *CircuitBreaker
    LoadBalancer   *LoadBalancer
    Timeout        time.Duration
    MaxRetries     int
}

func (sm *ServiceMesh) AddService(service *Service) { sm.services[service.Name] = service }

func (sm *ServiceMesh) HandleRequest(serviceName string, req *http.Request) (*http.Response, error) {
    service, ok := sm.services[serviceName]
    if !ok { return nil, fmt.Errorf("service %s not found", serviceName) }
    ctx, span := sm.tracer.StartSpan(req.Context(), "service_request"); defer span.End()
    if sm.config.CircuitBreaker.IsOpen(serviceName) { return nil, fmt.Errorf("circuit breaker open for service %s", serviceName) }
    endpoint := sm.config.LoadBalancer.GetEndpoint(service.Endpoints)
    var resp *http.Response; var err error
    for i := 0; i < sm.config.MaxRetries; i++ {
        resp, err = sm.makeRequest(ctx, endpoint, req)
        if err == nil { break }
        time.Sleep(sm.config.RetryPolicy.Backoff(i))
    }
    sm.metrics.RecordRequest(serviceName, req.Method, resp.StatusCode, time.Since(ctx.Value("start_time").(time.Time)))
    return resp, err
}

11.3–11.6 Trafik, Güvenlik Politikaları, Gözlemlenebilirlik, Servis Keşfi

1
2
3
4
type TrafficRule struct { Service, Version string; Percentage float64; Condition func(*http.Request) bool; Weight int }
type SecurityPolicy struct { Authentication *AuthenticationPolicy; Authorization *AuthorizationPolicy; RateLimiting *RateLimitingPolicy; Encryption *EncryptionPolicy }
type MeshMonitor struct { metrics *prometheus.Registry }
type ServiceInfo struct { Name, Version string; Endpoints []string }

12. Oran Sınırlama ve Throttling

12.1 Stratejiler ve 12.2 Token Bucket / 12.3 Leaky Bucket

1
2
3
type RateLimitConfig struct { RequestsPerSecond int; BurstSize int; WindowSize time.Duration }
type TokenBucket struct { redis *redis.Client; capacity int; rate float64 }
type LeakyBucket struct { redis *redis.Client; capacity int; rate float64 }

12.4 Redis ile Oran Sınırlama ve 12.5 Middleware

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type RedisRateLimiter struct { redis *redis.Client; config *RateLimitConfig; metrics *prometheus.CounterVec }
func (rl *RedisRateLimiter) CheckLimit(ctx context.Context, key string) error {
    script := `
        local key = KEYS[1]
        local limit = tonumber(ARGV[1])
        local window = tonumber(ARGV[2])
        local current = redis.call('get', key)
        if current == nil then redis.call('setex', key, window, 1); return 1 end
        if tonumber(current) >= limit then return 0 end
        redis.call('incr', key); return 1`
    result, err := rl.redis.Eval(ctx, script, []string{key}, rl.config.RequestsPerSecond, rl.config.WindowSize.Seconds()).Result()
    if err != nil { rl.metrics.WithLabelValues("error").Inc(); return err }
    if result.(int64) == 0 { rl.metrics.WithLabelValues("limited").Inc(); return errors.New("rate limit exceeded") }
    rl.metrics.WithLabelValues("allowed").Inc(); return nil
}

func RateLimitMiddleware(limiter *RateLimiter) gin.HandlerFunc {
    return func(c *gin.Context) {
        ip := c.ClientIP()
        if err := limiter.CheckLimit(c.Request.Context(), ip, "ip"); err != nil {
            c.JSON(http.StatusTooManyRequests, gin.H{ "error": "rate limit exceeded" }); c.Abort(); return
        }
        c.Next()
    }
}

13. Sonuç

13.1 Öne Çıkan Noktalar

  • REST: Basitlik ve yaygın uyumluluk; public API ve CRUD için ideal.
  • gRPC: Düşük gecikme ve yüksek throughput; mikro servis içi iletişim.
  • GraphQL: Esnek sorgu ve tek endpoint; frontend/mobil odaklı senaryolar.
  • WebSocket: Gerçek zamanlı, çift yönlü; sohbet, oyun, canlı akış, işbirliği.
  • Webhook: Olay güdümlü entegrasyon ve otomasyon.
  • gRPC‑Web: Tarayıcıdan gRPC; gateway ile tip güvenliği ve performans.
  • tRPC: TS ekosisteminde uçtan uca tip güvenliği; hızlı geliştirme.

13.2 En İyi Uygulamalar

  • API‑first yaklaşım; temiz kod ve iyi dokümantasyon.
  • Test kapsamı ve CI/CD; versiyonlama ve geriye dönük uyumluluk.
  • Performans optimizasyonu; gözlemlenebilirlik ve uyarı mekanizmaları.
  • Kod inceleme ve kalite eşikleri; maliyet ve kapasite planlama.

13.3 Gelecek Eğilimler

  • API/Service Mesh, AI/ML entegrasyonu, edge computing.
  • Güvenlik için blockchain ve sıfır güven (zero‑trust) yaklaşımları.
  • 5G/IoT etkisi; serverless ve platform mühendisliği.

13.4 Son Söz

Güvenli, ölçeklenebilir, gözlemlenebilir ve sürdürülebilir API’ler, iş değerini doğrudan etkiler. Bu rehberdeki ilkeleri uygulayarak güncel gereksinimleri karşılarken geleceğe dönük bir mimari kurabilirsiniz.