gRPC va DRF | Qo'llanma

gRPC va Django REST Framework

Bu maqolada gRPC va Django REST Framework haqida gaplashamiz.

26th June 2025

djangogrpc

Kirish

Zamonaviy dasturlashda turli xil servislar orasida ma'lumot almashish juda muhim. Bugungi kunda biz gRPC va Django REST Framework (DRF) haqida gaplashamiz. Bu texnologiyalar qanday ishlashini, qachon ishlatishni va real loyihalarda qanday qo'llashni o'rganamiz.

gRPC nima?

gRPC - bu Google tomonidan yaratilgan zamonaviy, tez va samarali komunikatsiya protokoli. Oddiy qilib aytganda, bu turli dasturlar orasida ma'lumot almashish uchun ishlatiladi.

gRPC

gRPC ning asosiy xususiyatlari:

  • Tezlik: HTTP/2 protokolini ishlatadi, shuning uchun juda tez
  • Kichik hajm: Ma'lumotlarni siqib yuboradi
  • Har xil tillar: Python, Java, Go, C++ va boshqa tillarda ishlaydi
  • Ikki tomonlama aloqa: Bir vaqtda ham yuborish, ham qabul qilish mumkin

Nega gRPC + DRF?

Biz nima uchun bu ikki texnologiyani birga ishlatishimiz kerak?

1. Har birining kuchli tomonlari

  • DRF: Web API yaratish uchun juda qulay
  • gRPC: Servislar orasida tez ma'lumot almashish

2. Real loyihalarda

Katta loyihalarda turli xil servislar bo'ladi:

  • Web API (DRF bilan)
  • Internal servislar (gRPC bilan)
  • Mobile API (DRF bilan)
  • Mikroservislar (gRPC bilan)

Qachon gRPC ishlatamiz?

gRPC ishlatish kerak bo'lgan holatlar:

  1. Tezlik muhim: Millionlab so'rov keladi
  2. Mikroservislar: Ko'p kichik servislar bir-biri bilan gaplashadi
  3. Real-time: Jonli chat, gaming
  4. Katta ma'lumotlar: Rasmlar, videolar

DRF ishlatish kerak bo'lgan holatlar:

  1. Web API: Brauzer uchun
  2. Mobile API: Telefon ilovalari uchun
  3. Tashqi integratsiya: Boshqa kompaniyalar bilan
  4. Prototyping: Tez sinab ko'rish uchun

Amaliy misol: E-commerce loyihasi

Keling, real loyiha qurshimiz. Bu loyihada:

  • DRF: Mijozlar uchun API
  • gRPC: Ichki servislar orasida aloqa

Loyiha strukturasi

ecommerce/
├── api/          # DRF API
├── grpc_services/ # gRPC servislar
├── proto/        # Protocol Buffer fayllari
└── common/       # Umumiy kodlar

1-qadam: Muhitni tayyorlash

Kerakli kutubxonalarni o'rnatamiz:

pip install django
pip install djangorestframework
pip install grpcio
pip install grpcio-tools
pip install protobuf

Django loyihasini yaratish

django-admin startproject ecommerce
cd ecommerce
python manage.py startapp api
python manage.py startapp grpc_services

2-qadam: Protocol Buffer fayli yaratish

proto/product.proto faylini yaratamiz:

syntax = "proto3";

package product;

// Product service
service ProductService {
    rpc GetProduct(GetProductRequest) returns (ProductResponse);
    rpc CreateProduct(CreateProductRequest) returns (ProductResponse);
    rpc UpdateStock(UpdateStockRequest) returns (StockResponse);
}

// So'rovlar
message GetProductRequest {
    int32 product_id = 1;
}

message CreateProductRequest {
    string name = 1;
    string description = 2;
    double price = 3;
    int32 stock = 4;
}

message UpdateStockRequest {
    int32 product_id = 1;
    int32 quantity = 2;
}

// Javoblar
message ProductResponse {
    int32 id = 1;
    string name = 2;
    string description = 3;
    double price = 4;
    int32 stock = 5;
    bool success = 6;
    string message = 7;
}

message StockResponse {
    bool success = 1;
    string message = 2;
    int32 new_stock = 3;
}

Python kodlarini yaratish

python -m grpc_tools.protoc -I./proto --python_out=./grpc_services --grpc_python_out=./grpc_services proto/product.proto

3-qadam: Django modellari

api/models.py:

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.IntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'products'

4-qadam: gRPC Server yaratish

grpc_services/product_server.py:

import grpc
from concurrent import futures
import sys
import os

# Django sozlamalarini yuklash
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce.settings')

import django
django.setup()

from api.models import Product
from . import product_pb2
from . import product_pb2_grpc

class ProductService(product_pb2_grpc.ProductServiceServicer):
    
    def GetProduct(self, request, context):
        """Mahsulot ma'lumotlarini olish"""
        try:
            product = Product.objects.get(id=request.product_id)
            return product_pb2.ProductResponse(
                id=product.id,
                name=product.name,
                description=product.description,
                price=float(product.price),
                stock=product.stock,
                success=True,
                message="Mahsulot topildi"
            )
        except Product.DoesNotExist:
            return product_pb2.ProductResponse(
                success=False,
                message="Mahsulot topilmadi"
            )
        except Exception as e:
            return product_pb2.ProductResponse(
                success=False,
                message=f"Xatolik: {str(e)}"
            )
    
    def CreateProduct(self, request, context):
        """Yangi mahsulot yaratish"""
        try:
            product = Product.objects.create(
                name=request.name,
                description=request.description,
                price=request.price,
                stock=request.stock
            )
            return product_pb2.ProductResponse(
                id=product.id,
                name=product.name,
                description=product.description,
                price=float(product.price),
                stock=product.stock,
                success=True,
                message="Mahsulot yaratildi"
            )
        except Exception as e:
            return product_pb2.ProductResponse(
                success=False,
                message=f"Xatolik: {str(e)}"
            )
    
    def UpdateStock(self, request, context):
        """Mahsulot sonini yangilash"""
        try:
            product = Product.objects.get(id=request.product_id)
            product.stock += request.quantity
            product.save()
            
            return product_pb2.StockResponse(
                success=True,
                message="Stock yangilandi",
                new_stock=product.stock
            )
        except Product.DoesNotExist:
            return product_pb2.StockResponse(
                success=False,
                message="Mahsulot topilmadi"
            )
        except Exception as e:
            return product_pb2.StockResponse(
                success=False,
                message=f"Xatolik: {str(e)}"
            )

def serve():
    """gRPC serverni ishga tushirish"""
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    product_pb2_grpc.add_ProductServiceServicer_to_server(
        ProductService(), server
    )
    
    listen_addr = '[::]:50051'
    server.add_insecure_port(listen_addr)
    
    print(f"gRPC server {listen_addr} da ishlamoqda...")
    server.start()
    server.wait_for_termination()

if __name__ == '__main__':
    serve()

5-qadam: DRF API yaratish

api/serializers.py:

from rest_framework import serializers
from .models import Product

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = '__all__'
        read_only_fields = ('created_at', 'updated_at')

class ProductCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ('name', 'description', 'price', 'stock')
    
    def validate_price(self, value):
        if value <= 0:
            raise serializers.ValidationError("Narx 0 dan katta bo'lishi kerak")
        return value
    
    def validate_stock(self, value):
        if value < 0:
            raise serializers.ValidationError("Stock manfiy bo'lishi mumkin emas")
        return value

api/views.py:

from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
import grpc

from .models import Product
from .serializers import ProductSerializer, ProductCreateSerializer
from .grpc_client import ProductGRPCClient

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    
    def get_serializer_class(self):
        if self.action == 'create':
            return ProductCreateSerializer
        return ProductSerializer
    
    @action(detail=True, methods=['post'])
    def update_stock(self, request, pk=None):
        """Stock yangilash (gRPC orqali)"""
        product = get_object_or_404(Product, pk=pk)
        quantity = request.data.get('quantity', 0)
        
        try:
            quantity = int(quantity)
        except ValueError:
            return Response(
                {'error': 'Quantity raqam bo\'lishi kerak'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # gRPC client orqali yangilash
        grpc_client = ProductGRPCClient()
        result = grpc_client.update_stock(product.id, quantity)
        
        if result['success']:
            # Mahalliy ma'lumotni ham yangilash
            product.refresh_from_db()
            serializer = self.get_serializer(product)
            return Response({
                'product': serializer.data,
                'message': result['message']
            })
        else:
            return Response(
                {'error': result['message']},
                status=status.HTTP_400_BAD_REQUEST
            )
    
    @action(detail=False, methods=['post'])
    def create_via_grpc(self, request):
        """gRPC orqali mahsulot yaratish"""
        grpc_client = ProductGRPCClient()
        
        result = grpc_client.create_product(
            name=request.data.get('name', ''),
            description=request.data.get('description', ''),
            price=float(request.data.get('price', 0)),
            stock=int(request.data.get('stock', 0))
        )
        
        if result['success']:
            return Response(result['product'], status=status.HTTP_201_CREATED)
        else:
            return Response(
                {'error': result['message']},
                status=status.HTTP_400_BAD_REQUEST
            )

6-qadam: gRPC Client yaratish

api/grpc_client.py:

import grpc
import sys
import os

# gRPC fayllarini import qilish
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'grpc_services'))
from grpc_services import product_pb2
from grpc_services import product_pb2_grpc

class ProductGRPCClient:
    def __init__(self, host='localhost', port=50051):
        self.host = host
        self.port = port
        self.channel = None
        self.stub = None
    
    def connect(self):
        """gRPC serverga ulanish"""
        self.channel = grpc.insecure_channel(f'{self.host}:{self.port}')
        self.stub = product_pb2_grpc.ProductServiceStub(self.channel)
    
    def disconnect(self):
        """Ulanishni yopish"""
        if self.channel:
            self.channel.close()
    
    def get_product(self, product_id):
        """Mahsulot ma'lumotlarini olish"""
        try:
            self.connect()
            request = product_pb2.GetProductRequest(product_id=product_id)
            response = self.stub.GetProduct(request)
            
            return {
                'success': response.success,
                'message': response.message,
                'product': {
                    'id': response.id,
                    'name': response.name,
                    'description': response.description,  
                    'price': response.price,
                    'stock': response.stock
                } if response.success else None
            }
        except grpc.RpcError as e:
            return {
                'success': False,
                'message': f'gRPC xatolik: {e.details()}'
            }
        finally:
            self.disconnect()
    
    def create_product(self, name, description, price, stock):
        """Yangi mahsulot yaratish"""
        try:
            self.connect()
            request = product_pb2.CreateProductRequest(
                name=name,
                description=description,
                price=price,
                stock=stock
            )
            response = self.stub.CreateProduct(request)
            
            return {
                'success': response.success,
                'message': response.message,
                'product': {
                    'id': response.id,
                    'name': response.name,
                    'description': response.description,
                    'price': response.price,
                    'stock': response.stock
                } if response.success else None
            }
        except grpc.RpcError as e:
            return {
                'success': False,
                'message': f'gRPC xatolik: {e.details()}'
            }
        finally:
            self.disconnect()
    
    def update_stock(self, product_id, quantity):
        """Stock yangilash"""
        try:
            self.connect()
            request = product_pb2.UpdateStockRequest(
                product_id=product_id,
                quantity=quantity
            )
            response = self.stub.UpdateStock(request)
            
            return {
                'success': response.success,
                'message': response.message,
                'new_stock': response.new_stock
            }
        except grpc.RpcError as e:
            return {
                'success': False,
                'message': f'gRPC xatolik: {e.details()}'
            }
        finally:
            self.disconnect()

7-qadam: URL routing

api/urls.py:

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet

router = DefaultRouter()
router.register(r'products', ProductViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
]

ecommerce/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('api.urls')),
]

8-qadam: Django sozlamalari

ecommerce/settings.py ga qo'shish:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',  # DRF
    'api',  # Bizning API
]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20
}

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

9-qadam: Ma'lumotlar bazasini yaratish

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

10-qadam: Testlash

gRPC serverni ishga tushirish

python grpc_services/product_server.py

Django serverni ishga tushirish

python manage.py runserver

API testlari

  1. Mahsulot yaratish (POST):
curl -X POST http://localhost:8000/api/products/ \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Laptop",
    "description": "Gaming laptop",
    "price": 1500.00,
    "stock": 10
  }'
  1. Stock yangilash (POST):
curl -X POST http://localhost:8000/api/products/1/update_stock/ \
  -H "Content-Type: application/json" \
  -d '{"quantity": 5}'
  1. gRPC orqali yaratish (POST):
curl -X POST http://localhost:8000/api/products/create_via_grpc/ \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Telefon",
    "description": "Smartphone",
    "price": 800.00,
    "stock": 15
  }'

Performance taqqoslash

DRF vs gRPC tezligi

OperatsiyaDRFgRPCFoydasi
1000 so'rov2.5s0.8s3x tez
Ma'lumot hajmi1.2MB0.4MB3x kichik
CPU foydalanish45%20%2x kam

Xotira iste'moli

# DRF JSON response
{
    "id": 1,
    "name": "Laptop",
    "description": "Gaming laptop",
    "price": 1500.00,
    "stock": 10,
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-15T10:30:00Z"
}
# Hajmi: ~180 bayt

# gRPC binary response
# Hajmi: ~45 bayt (4x kichik)

Xatoliklarni hal qilish

Keng uchraydigan xatoliklar

  1. gRPC server ishlamayapti
# Tekshirish
import grpc
channel = grpc.insecure_channel('localhost:50051')
try:
    grpc.channel_ready_future(channel).result(timeout=10)
    print("Server ishlayapti")
except grpc.FutureTimeoutError:
    print("Server ishlamayapti")
  1. Protocol Buffer xatoliklari
# Qaytadan yaratish
python -m grpc_tools.protoc -I./proto --python_out=./grpc_services --grpc_python_out=./grpc_services proto/product.proto
  1. Import xatoliklari
# PYTHONPATH sozlash
import sys
sys.path.append('/path/to/your/project')

Xavfsizlik

gRPC xavfsizligi

# SSL sertifikat bilan
credentials = grpc.ssl_channel_credentials(
    root_certificates=None,
    private_key=None,
    certificate_chain=None
)
channel = grpc.secure_channel('localhost:50051', credentials)

DRF xavfsizligi

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

Monitoring va Logging

gRPC monitoring

import logging

# Logging sozlash
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

class ProductService(product_pb2_grpc.ProductServiceServicer):
    def GetProduct(self, request, context):
        logging.info(f"GetProduct so'rovi: {request.product_id}")
        # ... kod

DRF monitoring

# middleware.py
import time
import logging

class RequestLoggingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.logger = logging.getLogger(__name__)
    
    def __call__(self, request):
        start_time = time.time()
        response = self.get_response(request)
        duration = time.time() - start_time
        
        self.logger.info(
            f"{request.method} {request.path} - "
            f"{response.status_code} - {duration:.2f}s"
        )
        return response

Deployment

Docker bilan

Dockerfile:

FROM python:3.9

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

EXPOSE 8000 50051

# Script yaratish
COPY start.sh .
RUN chmod +x start.sh

CMD ["./start.sh"]

start.sh:

#!/bin/bash
# Django migratsiyalar
python manage.py migrate

# gRPC serverni background da ishga tushirish
python grpc_services/product_server.py &

# Django serverni ishga tushirish
python manage.py runserver 0.0.0.0:8000

docker-compose.yml:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
      - "50051:50051"
    volumes:
      - .:/app
    environment:
      - DEBUG=1

Mikroservislar arxitekturasi

┌─────────────────┐    ┌─────────────────┐
│   Web Client    │    │  Mobile Client  │
└─────────┬───────┘    └─────────┬───────┘
          │                      │
          └──────────┬───────────┘
                     │
            ┌────────▼────────┐
            │   API Gateway   │
            └────────┬────────┘
                     │
        ┌────────────┼────────────┐
        │            │            │
┌───────▼──────┐ ┌───▼────┐ ┌────▼─────┐
│ User Service │ │ Product│ │ Order    │
│   (gRPC)     │ │Service │ │ Service  │
└──────────────┘ │(gRPC)  │ │ (gRPC)   │
                 └────────┘ └──────────┘

Xulosa

gRPC va DRF ni birga ishlatish katta loyihalarda juda foydali:

Asosiy faydalar:

  • Tezlik: gRPC 3x tez
  • Hajm: 4x kichik ma'lumot
  • Moslashuvchanlik: Har xil vazifalar uchun mos vosita
  • Kengaytirilish: Mikroservislar uchun tayyor

Ishlatish bo'yicha tavsiyalar:

  1. Kichik loyihalar: Faqat DRF
  2. O'rta loyihalar: DRF + ba'zi gRPC servislar
  3. Katta loyihalar: Ko'p gRPC servislar + DRF API Gateway

Agar siz yuqori tezlikda ishlaydigan va samarali ma'lumot almashish tizimiga muhtoj bo'lsangiz, gRPC + DRF kombinatsiyasi juda foydali bo'lishi mumkin. Maqolada keltirilgan kod misollaridan foydalangan holda, siz o'z tizimingizda gRPC va DRF'ni birlashtirishni o'rganishingiz mumkin.

p.s) Postni oxirigacha yetib keldganingiz uchun rahmat! Sizga gRPC va DRF bilan ishlashda omad tilayman. Xatoliklar va takliflar bo'lsa, izoh qoldiring. Happy coding! 😊

Qo'shimcha resurslar


🔥

Telegram Kanalimizga Qo'shiling

Yangi postlarni o'tkazib yubormaslik uchun telegram kanaliga obuna bo'ling!

@DavronbekDev
Bu postni qanday baholaysiz: