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'rnigareserved 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.
1raqami 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.