grpc-ile-mikroservis-mimarisi-golang-backend-entegrasyonu-2

Microservice Architecture with gRPC: Golang Backend Integration

Table of Contents

Modern mobile applications require powerful and scalable backend services. At Chipode, we chose microservice architecture and gRPC technology to manage game data and optimize the user experience. In this article, we will explain in detail why we chose these technologies and how we implemented them.

What is Microservice Architecture?

Microservice architecture is an approach to managing large and complex applications by dividing them into smaller, independent services. Each service:

– Can have its own database
– Can be deployed independently
– Can be written in different programming languages
– Horizontally scalable

1. Why Microservice?

Reasons for choosing microservice architecture for our Sudoku application:
1. Independent scaling (e.g. score service)
2. Technology flexibility
3. Fault isolation
4. Easy maintenance
5. Team independence

What is gRPC and why should we use it?

gRPC is a modern RPC (Remote Procedure Call) framework developed by Google. Highlights:

– High performance with Protocol Buffers
– Bidirectional streaming
– Language agnostic structure
– Auto-generated client/server code
– HTTP/2 based communication

REST vs gRPC Comparison

Service Identification with Protocol Buffers

				
					protobuf
syntax = "proto3";

package game;

service GameService {
  // Game save
  rpc SaveGame (SaveGameRequest) returns (SaveGameResponse);
  
  // Leaderboard streaming
  rpc GetLeaderboard (LeaderboardRequest) returns (stream LeaderboardEntry);
  
  // User statistics
  rpc GetUserStats (UserStatsRequest) returns (UserStatsResponse);
  
  // Real-time game state
  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;
}

// Other message definitions...

				
			

gRPC Server Implementation with Golang

1. Server Structure

				
					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. Game Saving Process

				
					go
func (s *gameServer) SaveGame(ctx context.Context, req *pb.SaveGameRequest) (*pb.SaveGameResponse, error) {
    // Validation
    if err := s.validator.Struct(req); err != nil {
        return nil, status.Errorf(codes.InvalidArgument, "invalid request: %v", err)
    }

    // Transaction start
    tx, err := s.db.BeginTx(ctx, nil)
    if err != nil {
        return nil, status.Errorf(codes.Internal, "transaction error: %v", err)
    }
    defer tx.Rollback()

    // Save game data
    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)
    }

    // Update leaderboard
    err = s.updateLeaderboard(ctx, req.UserId, req.Score, req.Difficulty)
    if err != nil {
        return nil, status.Errorf(codes.Internal, "leaderboard error: %v", err)
    }

    // Commit the transaction
    if err := tx.Commit(); err != nil {
        return nil, status.Errorf(codes.Internal, "commit error: %v", err)
    }

    return &pb.SaveGameResponse{Success: true}, nil
}



				
			

3. Leaderboard with Redis

				
					go
func (s *gameServer) updateLeaderboard(ctx context.Context, userId string, score int32, difficulty pb.GameDifficulty) error {
    // Leaderboard key by difficulty level
    leaderboardKey := fmt.Sprintf("leaderboard:%s", difficulty.String())
    
    // Score update using Z-Set
    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
}

				
			

gRPC Client Integration with Flutter

1. gRPC Channel Configuration

				
					dart
class GrpcClient {
  static GameServiceClient? _client;
  
  static Future<GameServiceClient> 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<ChannelCredentials> _getCredentials() async {
    final trustStore = await rootBundle.load('assets/certificates/ca.pem');
    return ChannelCredentials.secure(
      certificates: trustStore.buffer.asUint8List(),
    );
  }
}


				
			

2. Game Repository Implementation

				
					dart
class GameRepository {
  final GameServiceClient _client;
  
  GameRepository(this._client);
  
  Future<bool> 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<LeaderboardEntry> getLeaderboard(GameDifficulty difficulty) {
    final request = LeaderboardRequest()
      ..difficulty = _difficultyToProto(difficulty);
    
    return _client.getLeaderboard(request);
  }
}



				
			

Error Handling and Retry Mechanism

1. Use of Interceptor

				
					dart
class RetryInterceptor extends ClientInterceptor {
  @override
  ResponseFuture<R> interceptUnary<Q, R>(
    ClientMethod<Q, R> method,
    Q request,
    CallOptions options,
    ClientUnaryInvoker<Q, R> invoker,
  ) {
    return _retry(
      () => invoker(method, request, options),
      maxAttempts: 3,
    );
  }
  
  Future<R> _retry<R>(
    Future<R> 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 and 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
}

				
			

Result

gRPC and microservices architecture underpin Chipode’s backend infrastructure. Thanks to these technologies:

1. High performance communication
2. Easy scaling
Type 3 safety
4. Effective monitoring
5. Reliable error handling

to optimize the infrastructure. In future articles, we will examine the other components of this infrastructure and optimization techniques in detail.

References

Twitter
LinkedIn
Related Articles
Contact Form

Chipode Apps

loading...
Google Play Store
loading...
Apple Store