Mikroservis Mimarisi Nedir?
Mikroservis mimarisi, büyük ve karmaşık uygulamaları daha küçük, bağımsız servislere bölerek yönetmeyi sağlayan bir yaklaşımdır. Her bir servis:
– Kendi veritabanına sahip olabilir
– Bağımsız olarak deploy edilebilir
– Farklı programlama dilleriyle yazılabilir
– Yatay olarak ölçeklendirilebilir
1. Neden Mikroservis?
Sudoku uygulamamız için mikroservis mimarisini seçmemizin nedenleri:
1. Bağımsız ölçeklendirme (örn: skor servisi)
2. Teknoloji esnekliği
3. Hata izolasyonu
4. Kolay maintenance
5. Takım bağımsızlığı
gRPC Nedir ve Neden Kullanmalıyız?
gRPC, Google tarafından geliştirilen modern bir RPC (Remote Procedure Call) framework’üdür. Öne çıkan özellikleri:
– Protocol Buffers ile yüksek performans
– Bidirectional streaming
– Language agnostic yapı
– Auto-generated client/server code
– HTTP/2 tabanlı iletişim
REST vs gRPC Karşılaştırması
Protocol Buffers ile Servis Tanımlaması
protobuf
syntax = "proto3";
package game;
service GameService {
// Oyun kaydetme
rpc SaveGame (SaveGameRequest) returns (SaveGameResponse);
// Liderlik tablosu streaming
rpc GetLeaderboard (LeaderboardRequest) returns (stream LeaderboardEntry);
// Kullanıcı istatistikleri
rpc GetUserStats (UserStatsRequest) returns (UserStatsResponse);
// Gerçek zamanlı oyun durumu
rpc StreamGameState (stream GameStateUpdate) returns (stream GameStateResponse);
}
message SaveGameRequest {
string user_id = 1;
int32 score = 2;
int32 time_spent = 3;
repeated int32 board_state = 4;
GameDifficulty difficulty = 5;
}
enum GameDifficulty {
EASY = 0;
MEDIUM = 1;
HARD = 2;
EXPERT = 3;
}
// Diğer message tanımlamaları...
Golang ile gRPC Server Implementasyonu
1. Server Yapısı
go
type gameServer struct {
pb.UnimplementedGameServiceServer
db *sql.DB
redis *redis.Client
validator *validation.Validator
}
func NewGameServer(db *sql.DB, redis *redis.Client) *gameServer {
return &gameServer{
db: db,
redis: redis,
validator: validation.New(),
}
}
2. Oyun Kaydetme İşlemi
go
func (s *gameServer) SaveGame(ctx context.Context, req *pb.SaveGameRequest) (*pb.SaveGameResponse, error) {
// Validasyon
if err := s.validator.Struct(req); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid request: %v", err)
}
// Transaction başlat
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return nil, status.Errorf(codes.Internal, "transaction error: %v", err)
}
defer tx.Rollback()
// Oyun verilerini kaydet
result, err := tx.ExecContext(ctx,
`INSERT INTO games (user_id, score, time_spent, board_state, difficulty)
VALUES ($1, $2, $3, $4, $5)
RETURNING id`,
req.UserId, req.Score, req.TimeSpent,
pq.Array(req.BoardState), req.Difficulty,
)
if err != nil {
return nil, status.Errorf(codes.Internal, "db error: %v", err)
}
// Liderlik tablosunu güncelle
err = s.updateLeaderboard(ctx, req.UserId, req.Score, req.Difficulty)
if err != nil {
return nil, status.Errorf(codes.Internal, "leaderboard error: %v", err)
}
// Transaction'ı commit et
if err := tx.Commit(); err != nil {
return nil, status.Errorf(codes.Internal, "commit error: %v", err)
}
return &pb.SaveGameResponse{Success: true}, nil
}
3. Redis ile Liderlik Tablosu
go
func (s *gameServer) updateLeaderboard(ctx context.Context, userId string, score int32, difficulty pb.GameDifficulty) error {
// Zorluk seviyesine göre leaderboard key'i
leaderboardKey := fmt.Sprintf("leaderboard:%s", difficulty.String())
// Z-Set kullanarak skor güncelleme
member := &redis.Z{
Score: float64(score),
Member: userId,
}
pipe := s.redis.Pipeline()
pipe.ZAdd(ctx, leaderboardKey, member)
pipe.ZRemRangeByRank(ctx, leaderboardKey, 0, -101) // Sadece top 100
_, err := pipe.Exec(ctx)
return err
}
Flutter İle gRPC İstemci Entegrasyonu
1. gRPC Channel Konfigürasyonu
dart
class GrpcClient {
static GameServiceClient? _client;
static Future get client async {
if (_client != null) return _client!;
final channel = ClientChannel(
'api.chipode.com',
port: 50051,
options: ChannelOptions(
credentials: await _getCredentials(),
idleTimeout: Duration(minutes: 1),
),
);
_client = GameServiceClient(channel);
return _client!;
}
static Future _getCredentials() async {
final trustStore = await rootBundle.load('assets/certificates/ca.pem');
return ChannelCredentials.secure(
certificates: trustStore.buffer.asUint8List(),
);
}
}
2. Oyun Repository İmplementasyonu
dart
class GameRepository {
final GameServiceClient _client;
GameRepository(this._client);
Future saveGame(GameState state) async {
try {
final request = SaveGameRequest()
..userId = getCurrentUserId()
..score = state.score
..timeSpent = state.timeSpent
..boardState.addAll(state.board.expand((row) => row))
..difficulty = _difficultyToProto(state.difficulty);
final response = await _client.saveGame(request);
return response.success;
} catch (e) {
log.error('Save game error', e);
rethrow;
}
}
Stream getLeaderboard(GameDifficulty difficulty) {
final request = LeaderboardRequest()
..difficulty = _difficultyToProto(difficulty);
return _client.getLeaderboard(request);
}
}
Error Handling ve Retry Mekanizması
1. Interceptor Kullanımı
dart
class RetryInterceptor extends ClientInterceptor {
@override
ResponseFuture interceptUnary(
ClientMethod method,
Q request,
CallOptions options,
ClientUnaryInvoker invoker,
) {
return _retry(
() => invoker(method, request, options),
maxAttempts: 3,
);
}
Future _retry(
Future Function() call, {
int maxAttempts = 3,
}) async {
int attempts = 0;
while (true) {
try {
attempts++;
return await call();
} catch (e) {
if (attempts >= maxAttempts) rethrow;
await Future.delayed(Duration(seconds: attempts));
}
}
}
}
Monitoring ve Logging
1. Prometheus Metrics
go
var (
requestDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "grpc_request_duration_seconds",
Help: "gRPC request duration in seconds",
},
[]string{"method", "status"},
)
activeConnections = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "grpc_active_connections",
Help: "Number of active gRPC connections",
},
)
)
2. Structured Logging
go
func (s *gameServer) SaveGame(ctx context.Context, req *pb.SaveGameRequest) (*pb.SaveGameResponse, error) {
logger := log.With().
Str("user_id", req.UserId).
Int32("score", req.Score).
Str("difficulty", req.Difficulty.String()).
Logger()
logger.Info().Msg("saving game")
// ... implementation
}
Sonuç
gRPC ve mikroservis mimarisi, Chipode’un backend altyapısının temelini oluşturuyor. Bu teknolojiler sayesinde:
1. Yüksek performanslı iletişim
2. Kolay ölçeklendirme
3. Tip güvenliği
4. Etkin monitoring
5. Güvenilir hata yönetimi
sağlayabiliyoruz. Gelecek yazılarımızda, bu altyapının diğer bileşenlerini ve optimizasyon tekniklerini de detaylıca inceleyeceğiz.





