Azimjon Abdurasulov

02. Protobuf v3 va gRPC To'liq Qo'llanma

gRPC & High-Performance System Design

02. Protobuf v3 va gRPC To'liq Qo'llanma (Swift + Go)

Protocol Buffers (Protobuf) — bu Google tomonidan yaratilgan, platformadan mustaqil, binar serializatsiya mexanizmi. gRPC esa ushbu format ustida ishlaydigan, HTTP/2 asosidagi zamonaviy RPC (Remote Procedure Call) freymvorki hisoblanadi.

Ushbu darsda biz bitta .proto shartnomadan (contract) foydalanib, Go da backend va Swift 6 da iOS ilova qanday yaratilishini to'liq o'rganamiz.


1. Yagona Haqiqat Manbai: .proto Kontrakt

REST API da backend va mobile jamoalari Swagger yoki Postman hujjatlari eskirib qolishi sababli doimiy bahslashishadi. gRPC da esa yagona qonun bor — .proto fayl.

syntax = "proto3";

package masterclass.v1;
option go_package = "github.com/azimxxm/grpc-mobile-backend-masterclass/gen/go/masterclass/v1";

// 1. Foydalanuvchi xizmati
service UserService {
  rpc GetProfile (ProfileRequest) returns (UserProfile);
  rpc UpdateStatus (StatusUpdateRequest) returns (StatusResponse);
}

// 2. Modellar
message ProfileRequest {
  string user_id = 1;
}

message UserProfile {
  string user_id = 1;
  string full_name = 2;
  string email = 3;
  bool is_verified = 4;
  int64 balance_cents = 5;
}

message StatusUpdateRequest {
  string user_id = 1;
  string new_status = 2;
}

message StatusResponse {
  bool success = 1;
  int64 updated_at = 2;
}

Muhim Qoida (Schema Evolution): 1. Field raqamlarini (= 1, = 2) hech qachon o'zgartirmang yoki qayta ishlatmang! 2. Maydon kerak bo'lmay qolsa, uni o'chirish o'rniga reserved 3; deb belgilang. 3. Bu orqali eski versiyadagi iOS ilova yangi backend bilan ham, yangi iOS ilova eski backend bilan ham hech qachon "crash" bo'lmasdan ishlaydi!


2. Protobuf Kapoti Ostida: Varint Kodlash

Nega Protobuf bunchalik ixcham? Oddiy int32 raqam 4 bayt (32 bit) joy oladi. Masalan int32 value = 1; xotirada: 00000000 00000000 00000000 00000001 (4 bayt)

Protobuf esa Varint (Variable Length ZigZag Encoding) ishlatadi:

  • Har bir baytning birinchi biti (MSB) davomiylikni bildiradi.
  • 1 raqami uchun Protobuf atigi 1 bayt sarflaydi: 00000001.
  • Natijada, kichik raqamlar xotirada 4 barobar kam joy egallaydi.

3. Go Backend Server Arxitekturasi

Go da gRPC serverni ko'tarish juda sodda va toza:

package main

import (
	"context"
	"log"
	"net"

	"google.golang.org/grpc"
	pb "github.com/azimxxm/grpc-mobile-backend-masterclass/gen/go/masterclass/v1"
)

type userServer struct {
	pb.UnimplementedUserServiceServer
}

func (s *userServer) GetProfile(ctx context.Context, req *pb.ProfileRequest) (*pb.UserProfile, error) {
	log.Printf("[gRPC] Profil so'raldi: %s", req.GetUserId())
	
	// Haqiqiy hayotda bu yerda ma'lumotlar bazasidan (PostgreSQL/Redis) olinadi
	return &pb.UserProfile{
		UserId:       req.GetUserId(),
		FullName:     "Azimjon Po'latov",
		Email:        "azimjon@example.com",
		IsVerified:   true,
		BalanceCents: 45000000, // 450,000 so'm
	}, nil
}

func main() {
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("Portni tinglab bo'lmadi: %v", err)
	}

	grpcServer := grpc.NewServer()
	pb.RegisterUserServiceServer(grpcServer, &userServer{})

	log.Println("🚀 High-Performance Go gRPC Server :50051 portida ishga tushdi...")
	if err := grpcServer.Serve(lis); err != nil {
		log.Fatalf("Server xatosi: %v", err)
	}
}

4. Swift 6 iOS Arxitekturasi (Apple Native)

Apple loyihalari uchun rasmiy apple/swift-protobuf va grpc/grpc-swift kutubxonalari ishlatiladi. Swift 6 Concurrency bilan kod yozish zavqli:

import Foundation
import GRPC
import NIO
import SwiftProtobuf

@MainActor
public final class UserProfileViewModel: ObservableObject {
    @Published public private(set) var userProfile: Masterclass_V1_UserProfile?
    @Published public private(set) var isLoading: Bool = false
    @Published public private(set) var errorMessage: String?

    private let client: Masterclass_V1_UserServiceAsyncClient

    public init(channel: GRPCChannel) {
        self.client = Masterclass_V1_UserServiceAsyncClient(channel: channel)
    }

    public func fetchProfile(userId: String) async {
        self.isLoading = true
        self.errorMessage = nil

        var request = Masterclass_V1_ProfileRequest()
        request.userID = userId

        do {
            // Swift Concurrency orqali to'g'ridan-to'g'ri async/await chaqiruv
            let profile = try await client.getProfile(request, callOptions: CallOptions(
                timeLimit: .timeout(.seconds(5))
            ))
            self.userProfile = profile
        } catch {
            self.errorMessage = "Ma'lumot yuklashda xatolik: \(error.localizedDescription)"
        }

        self.isLoading = false
    }
}

5. REST vs gRPC Arxitektura Xulosasi

REST API (HTTP/1.1):
[Client] --- TCP Handshake ---> [Server]
[Client] --- TLS Handshake ---> [Server]
[Client] --- GET /profile ---> [Server]
[Client] <--- JSON Response --- [Server]
(Ulanish uziladi yoki Idle turadi)

gRPC (HTTP/2 Multiplexing):
[Client] ======== Bitta Doimiy HTTP/2 Tunnel ======== [Server]
   Stream 1: Stream.GetProfile() ------------> Response
   Stream 2: Stream.UpdateStatus() ----------> Response
   Stream 3: Stream.ChatMessages() <========== (Jonli xabarlar oqimi)

Natijada: Har bir so'rov uchun yangi TCP/TLS ochilmaydi. Tarmoq kechikishi (Latency) deyarli 0 ga tushadi.

Markdown manba

iOS Lessons