API Horizontal Scaling: как масштабировать до 100K RPS

Ваш API не справляется с нагрузкой? Один сервер упирается в лимиты CPU/RAM? В этом руководстве мы разберем горизонтальное масштабирование API до 100K+ RPS. Вы узнаете про stateless design, load balancing (Nginx, HAProxy, AWS ALB), session management (Redis, JWT), database sharding, read replicas и Kubernetes deployment.

📋 Содержание

  1. Vertical vs Horizontal Scaling
  2. Stateless API Design
  3. Load Balancing стратегии
  4. Session Management
  5. Database Scaling стратегии
  6. Nginx Load Balancer настройка
  7. Kubernetes Horizontal Scaling
  8. Мониторинг масштабируемости
  9. Best Practices
  10. FAQ: Часто задаваемые вопросы

📊 Vertical vs Horizontal Scaling

🔼 Vertical Scaling (Scale Up)

Увеличение ресурсов одного сервера: больше CPU, RAM, SSD.

✅ Преимущества:

❌ Недостатки:

➡️ Horizontal Scaling (Scale Out)

Добавление новых серверов: распределение нагрузки между множеством инстансов.

✅ Преимущества:

❌ Недостатки:

Критерий Vertical Scaling Horizontal Scaling
Масштабируемость Ограничена (до 128-256 CPU) Неограничена (тысячи серверов)
Стоимость Дорого (exponential рост) Линейный рост
Отказоустойчивость Single Point of Failure Высокая (redundancy)
Сложность Низкая Высокая
Deployment Downtime требуется Zero-downtime
RPS на 1 сервер 10K-50K RPS 5K-20K RPS
Для 100K RPS 1 огромный сервер (рискованно) 10-20 средних серверов (надежно)

🎯 Вывод: когда что использовать?

🔄 Stateless API Design

Stateless API — это API, которое не хранит состояние сессии между запросами. Каждый запрос содержит всю необходимую информацию для обработки.

✅ Почему stateless критично для horizontal scaling:

❌ Stateful vs Stateless

Плохо: Stateful (хранение сессии в памяти сервера)

// ❌ ПЛОХО: In-memory session store
const express = require('express');
const session = require('express-session');

const app = express();

// Session хранится в памяти Node.js процесса
app.use(session({
  secret: 'my-secret',
  resave: false,
  saveUninitialized: false,
  // По умолчанию MemoryStore — НЕ для production!
}));

app.get('/api/cart', (req, res) => {
  // Сессия привязана к этому серверу
  // Load balancer должен всегда отправлять пользователя сюда (sticky session)
  const cart = req.session.cart || [];
  res.json({ cart });
});

// Проблемы:
// 1. Sticky sessions сложны в настройке
// 2. Если сервер упал — сессия потеряна
// 3. Нельзя масштабировать (каждый сервер имеет свою память)

✅ Хорошо: Stateless (JWT tokens)

// ✅ ХОРОШО: JWT для stateless аутентификации
const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();

// Login endpoint — возвращает JWT
app.post('/api/auth/login', async (req, res) => {
  const { email, password } = req.body;

  // Проверка credentials
  const user = await User.findByCredentials(email, password);

  // Генерируем JWT token (все данные в токене)
  const token = jwt.sign(
    {
      userId: user.id,
      email: user.email,
      role: user.role
    },
    process.env.JWT_SECRET,
    { expiresIn: '7d' }
  );

  res.json({ token });
});

// Middleware для проверки JWT
function authenticate(req, res, next) {
  const token = req.headers.authorization?.replace('Bearer ', '');

  try {
    // Декодируем JWT — никаких запросов к БД или Redis!
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

// Protected endpoint
app.get('/api/profile', authenticate, (req, res) => {
  // req.user содержит данные из JWT
  // Запрос может обработать ЛЮБОЙ сервер
  res.json({
    userId: req.user.userId,
    email: req.user.email
  });
});

// Преимущества:
// 1. Stateless — любой сервер может проверить JWT
// 2. Нет sticky sessions
// 3. Нет запросов к БД/Redis для проверки auth (быстрее!)
// 4. Легко масштабировать

Stateless Session Management с Redis

Для случаев когда JWT недостаточно (нужно отозвать сессию), используйте Redis для централизованного хранения:

// Stateless с Redis session store
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');

const app = express();

// Redis client
const redisClient = createClient({
  url: 'redis://localhost:6379'
});
redisClient.connect();

// Session в Redis (не в памяти сервера!)
app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: 'my-secret',
  resave: false,
  saveUninitialized: false,
  cookie: {
    maxAge: 7 * 24 * 60 * 60 * 1000,  // 7 дней
    httpOnly: true,
    secure: true  // только HTTPS
  }
}));

// Теперь сессия доступна на ВСЕХ серверах через Redis
app.get('/api/cart', (req, res) => {
  // Сессия читается из Redis
  // Любой сервер может обработать запрос
  const cart = req.session.cart || [];
  res.json({ cart });
});

// Преимущества:
// 1. Централизованное хранение в Redis
// 2. Любой сервер может прочитать сессию
// 3. Можно отозвать сессию (удалить из Redis)
// 4. Persistence (сессия не теряется при перезапуске сервера)

⚖️ Load Balancing стратегии

Load Balancer распределяет входящие запросы между несколькими серверами API.

Алгоритмы балансировки

Алгоритм Как работает Когда использовать
Round Robin Запросы по очереди: 1→2→3→1→2→3... Одинаковые серверы, одинаковая нагрузка
Weighted Round Robin Учитывает вес сервера (мощность) Серверы разной мощности
Least Connections Отправляет на сервер с меньшим числом активных соединений Long-polling, WebSocket
IP Hash Хэш IP клиента определяет сервер (sticky) Stateful приложения (не рекомендуется)
Least Response Time Отправляет на сервер с наименьшим временем ответа Серверы в разных регионах

Layer 4 vs Layer 7 Load Balancing

Тип Layer 4 (Transport) Layer 7 (Application)
Уровень TCP/UDP HTTP/HTTPS
Производительность Очень высокая (1M+ conn/s) Высокая (100K+ RPS)
Видит IP, Port URL, Headers, Cookies
Возможности Простая балансировка URL routing, SSL termination, WAF
Пример HAProxy (TCP mode), AWS NLB Nginx, HAProxy (HTTP mode), AWS ALB

🔐 Session Management для Horizontal Scaling

Стратегия 1: JWT Tokens (рекомендуется)

Полный пример JWT Authentication

// auth.js
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

const JWT_SECRET = process.env.JWT_SECRET;
const JWT_EXPIRES_IN = '7d';

// Регистрация пользователя
async function register(req, res) {
  const { email, password, name } = req.body;

  // Хэшируем пароль
  const hashedPassword = await bcrypt.hash(password, 10);

  // Сохраняем в БД
  const user = await User.create({
    email,
    password: hashedPassword,
    name
  });

  // Генерируем JWT
  const token = jwt.sign(
    {
      userId: user.id,
      email: user.email,
      role: user.role
    },
    JWT_SECRET,
    { expiresIn: JWT_EXPIRES_IN }
  );

  res.status(201).json({ token, user: { id: user.id, email, name } });
}

// Login
async function login(req, res) {
  const { email, password } = req.body;

  // Находим пользователя
  const user = await User.findOne({ where: { email } });
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Проверяем пароль
  const isValid = await bcrypt.compare(password, user.password);
  if (!isValid) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Генерируем JWT
  const token = jwt.sign(
    {
      userId: user.id,
      email: user.email,
      role: user.role
    },
    JWT_SECRET,
    { expiresIn: JWT_EXPIRES_IN }
  );

  res.json({ token, user: { id: user.id, email: user.email, name: user.name } });
}

// Middleware для проверки JWT
function authenticate(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'No token provided' });
  }

  const token = authHeader.replace('Bearer ', '');

  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'Token expired' });
    }
    return res.status(401).json({ error: 'Invalid token' });
  }
}

// Middleware для проверки ролей
function authorize(...roles) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Not authenticated' });
    }

    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }

    next();
  };
}

module.exports = { register, login, authenticate, authorize };

Стратегия 2: Redis Session Store

# Python Flask + Redis sessions
from flask import Flask, session
from flask_session import Session
import redis

app = Flask(__name__)

# Redis configuration
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True
app.config['SESSION_KEY_PREFIX'] = 'session:'
app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:6379')

Session(app)

@app.route('/api/login', methods=['POST'])
def login():
    # Authenticate user
    user = authenticate_user(request.json)

    # Store в Redis session
    session['user_id'] = user.id
    session['email'] = user.email
    session['role'] = user.role

    return jsonify({'message': 'Logged in'})

@app.route('/api/profile')
def profile():
    # Читаем из Redis (любой сервер может обработать)
    user_id = session.get('user_id')
    if not user_id:
        return jsonify({'error': 'Not authenticated'}), 401

    user = User.query.get(user_id)
    return jsonify(user.to_dict())

🗄️ Database Scaling стратегии

БД часто становится bottleneck при horizontal scaling API.

1. Read Replicas (Master-Slave)

Архитектура Read Replicas

┌──────────────┐
│  API Servers │
│  (10 nodes)  │
└───────┬──────┘
        │
   ┌────┴─────┐
   │          │
   ↓          ↓
┌─────────┐  ┌──────────────┐
│ Master  │  │  Read Replica│
│  (Write)│→ │  (Read only) │
└─────────┘  │   Sync       │
             └──────────────┘

Master: Все записи (INSERT, UPDATE, DELETE)
Replicas: Все чтения (SELECT)
Репликация: async или sync

Node.js: Read/Write Split

// db.js
const { Sequelize } = require('sequelize');

// Master для записи
const masterDB = new Sequelize(process.env.DATABASE_URL_MASTER, {
  logging: false,
  pool: { max: 20, min: 5 }
});

// Read Replica для чтения
const replicaDB = new Sequelize(process.env.DATABASE_URL_REPLICA, {
  logging: false,
  pool: { max: 50, min: 10 }  // Больше соединений для чтения
});

// Helper функции
async function write(query, params) {
  return masterDB.query(query, {
    bind: params,
    type: Sequelize.QueryTypes.INSERT
  });
}

async function read(query, params) {
  return replicaDB.query(query, {
    bind: params,
    type: Sequelize.QueryTypes.SELECT
  });
}

// Использование
async function getUsers() {
  // Читаем из replica
  return read('SELECT * FROM users WHERE active = true');
}

async function createUser(data) {
  // Пишем в master
  return write(
    'INSERT INTO users (email, name) VALUES (?, ?)',
    [data.email, data.name]
  );
}

module.exports = { masterDB, replicaDB, read, write };

2. Connection Pooling

// Go: pgx connection pool
package main

import (
    "context"
    "github.com/jackc/pgx/v5/pgxpool"
    "time"
)

var dbPool *pgxpool.Pool

func InitDB() error {
    config, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
    if err != nil {
        return err
    }

    // Pool configuration для highload
    config.MaxConns = 100                          // Максимум 100 соединений
    config.MinConns = 20                           // Минимум 20 всегда активны
    config.MaxConnLifetime = time.Hour             // Пересоздавать каждый час
    config.MaxConnIdleTime = 30 * time.Minute      // Закрывать idle через 30 минут
    config.HealthCheckPeriod = time.Minute         // Health check каждую минуту

    dbPool, err = pgxpool.NewWithConfig(context.Background(), config)
    return err
}

func GetUsers(ctx context.Context) ([]User, error) {
    rows, err := dbPool.Query(ctx, "SELECT id, email, name FROM users")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var users []User
    for rows.Next() {
        var user User
        err := rows.Scan(&user.ID, &user.Email, &user.Name)
        if err != nil {
            return nil, err
        }
        users = append(users, user)
    }

    return users, nil
}

3. Database Sharding

Sharding — это горизонтальное разделение данных по нескольким БД серверам.

Архитектура Sharding

┌──────────────┐
│  API Servers │
└───────┬──────┘
        │
   ┌────┴─────┬─────────┬─────────┐
   │          │         │         │
   ↓          ↓         ↓         ↓
┌─────────┐┌─────────┐┌─────────┐┌─────────┐
│ Shard 1 ││ Shard 2 ││ Shard 3 ││ Shard 4 │
│ Users   ││ Users   ││ Users   ││ Users   │
│ 0-25%   ││ 26-50%  ││ 51-75%  ││ 76-100% │
└─────────┘└─────────┘└─────────┘└─────────┘

Sharding Key: user_id % 4
user_id=1 → Shard 1
user_id=2 → Shard 2
user_id=5 → Shard 1
// Sharding по user_id
class ShardManager {
  constructor() {
    this.shards = [
      new Sequelize(process.env.DATABASE_SHARD_1),
      new Sequelize(process.env.DATABASE_SHARD_2),
      new Sequelize(process.env.DATABASE_SHARD_3),
      new Sequelize(process.env.DATABASE_SHARD_4)
    ];
  }

  // Определяем shard по user_id
  getShard(userId) {
    const shardIndex = userId % this.shards.length;
    return this.shards[shardIndex];
  }

  // Получить пользователя
  async getUser(userId) {
    const shard = this.getShard(userId);
    const [user] = await shard.query(
      'SELECT * FROM users WHERE id = ?',
      { bind: [userId], type: Sequelize.QueryTypes.SELECT }
    );
    return user;
  }

  // Создать пользователя
  async createUser(userData) {
    // Generate user_id сначала
    const userId = await this.generateUserId();

    // Определяем shard
    const shard = this.getShard(userId);

    // Вставляем в соответствующий shard
    await shard.query(
      'INSERT INTO users (id, email, name) VALUES (?, ?, ?)',
      { bind: [userId, userData.email, userData.name] }
    );

    return userId;
  }
}

const shardManager = new ShardManager();
module.exports = shardManager;

⚙️ Nginx Load Balancer настройка

Базовая конфигурация

# /etc/nginx/nginx.conf

http {
    # Upstream servers (ваши API инстансы)
    upstream api_backend {
        # Алгоритм: Round Robin (по умолчанию)
        server 10.0.1.10:3000;
        server 10.0.1.11:3000;
        server 10.0.1.12:3000;
        server 10.0.1.13:3000;

        # Keepalive соединения для производительности
        keepalive 32;
    }

    server {
        listen 80;
        server_name api.example.com;

        # SSL redirect
        return 301 https://$server_name$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name api.example.com;

        # SSL certificates
        ssl_certificate /etc/nginx/ssl/cert.pem;
        ssl_certificate_key /etc/nginx/ssl/key.pem;

        # SSL optimization
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;

        # Gzip compression
        gzip on;
        gzip_vary on;
        gzip_min_length 1024;
        gzip_types text/plain text/css application/json application/javascript;

        # Rate limiting (защита от DDoS)
        limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
        limit_req zone=api_limit burst=200 nodelay;

        location / {
            # Proxy к backend
            proxy_pass http://api_backend;

            # Headers
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            # Timeouts
            proxy_connect_timeout 5s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;

            # HTTP/1.1 для keepalive
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }

        # Health check endpoint
        location /health {
            access_log off;
            return 200 "healthy\n";
        }
    }
}

Продвинутая конфигурация с весами и health checks

upstream api_backend {
    # Weighted Round Robin (сервер 1 мощнее)
    server 10.0.1.10:3000 weight=3;
    server 10.0.1.11:3000 weight=2;
    server 10.0.1.12:3000 weight=2;
    server 10.0.1.13:3000 weight=1;

    # Backup server (используется только если остальные упали)
    server 10.0.1.99:3000 backup;

    # Health check parameters
    # max_fails=3 — после 3 неудачных попыток считается down
    # fail_timeout=30s — считается down на 30 секунд
    server 10.0.1.10:3000 max_fails=3 fail_timeout=30s;

    # Least connections algorithm
    least_conn;

    keepalive 64;
}

# Upstream для WebSocket
upstream websocket_backend {
    ip_hash;  # Sticky sessions для WebSocket
    server 10.0.1.10:3001;
    server 10.0.1.11:3001;
    server 10.0.1.12:3001;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    # Regular HTTP API
    location /api/ {
        proxy_pass http://api_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }

    # WebSocket endpoint
    location /ws/ {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;  # 24 hours для long-lived connections
    }
}

☸️ Kubernetes Horizontal Scaling

Deployment с Multiple Replicas

# api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
  labels:
    app: api
spec:
  # Количество реплик (pods)
  replicas: 10

  selector:
    matchLabels:
      app: api

  # Rolling update strategy (zero-downtime deployment)
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 2          # Максимум 2 дополнительных pod'а при обновлении
      maxUnavailable: 1    # Максимум 1 pod может быть недоступен

  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
      - name: api
        image: mycompany/api:v1.2.3
        ports:
        - containerPort: 3000

        # Resource limits
        resources:
          requests:
            cpu: "500m"      # 0.5 CPU
            memory: "512Mi"
          limits:
            cpu: "1000m"     # 1 CPU
            memory: "1Gi"

        # Liveness probe (перезапускать если не отвечает)
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3

        # Readiness probe (не слать трафик пока не готов)
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3

        # Environment variables
        env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: api-secrets
              key: database-url
        - name: REDIS_URL
          valueFrom:
            secretKeyRef:
              name: api-secrets
              key: redis-url

---
# Service (внутренний load balancer)
apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  selector:
    app: api
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: ClusterIP  # Внутренний сервис

---
# Ingress (внешний load balancer с SSL)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    secretName: api-tls
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80

Horizontal Pod Autoscaler (HPA)

# api-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-deployment

  # Min и max количество реплик
  minReplicas: 5
  maxReplicas: 50

  # Метрики для автоскейлинга
  metrics:
  # CPU: если средний CPU > 70%, добавить pod'ы
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

  # Memory: если средняя память > 80%, добавить pod'ы
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

  # Custom metric: RPS (requests per second)
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "1000"  # 1000 RPS на pod

  # Поведение скейлинга
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300  # Ждать 5 минут перед scale down
      policies:
      - type: Percent
        value: 10              # Уменьшать максимум на 10% за раз
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0    # Немедленный scale up
      policies:
      - type: Percent
        value: 50              # Увеличивать до 50% за раз
        periodSeconds: 60
      - type: Pods
        value: 5               # Или добавлять по 5 pod'ов
        periodSeconds: 60

Применение конфигурации

# Создать Deployment, Service, Ingress
kubectl apply -f api-deployment.yaml

# Создать HPA
kubectl apply -f api-hpa.yaml

# Проверить статус
kubectl get deployment api-deployment
kubectl get pods -l app=api
kubectl get hpa api-hpa

# Мониторинг автоскейлинга
kubectl get hpa api-hpa --watch

# Logs
kubectl logs -f deployment/api-deployment

# Scale вручную (если нужно)
kubectl scale deployment api-deployment --replicas=20

📊 Мониторинг масштабируемости

Ключевые метрики для horizontal scaling

Что мониторить:

Prometheus Queries для мониторинга

# RPS per instance
sum(rate(http_requests_total[5m])) by (instance)

# CPU utilization по инстансам
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# p99 latency
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

# Error rate
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

# Database connection pool utilization
pg_stat_database_numbackends / pg_settings_max_connections * 100

# Total RPS across all instances
sum(rate(http_requests_total[5m]))

✅ Best Practices для Horizontal Scaling

1. Design for Failure

2. Stateless Everything

3. Cache Агрессивно

4. Database Optimization

5. Auto-scaling

6. Zero-Downtime Deployment

🔍 FAQ: Часто задаваемые вопросы

❓ В чем разница между вертикальным и горизонтальным масштабированием?
Вертикальное масштабирование (scale up) — увеличение ресурсов одного сервера (больше CPU, RAM, SSD). Имеет физические пределы и единую точку отказа.

Горизонтальное масштабирование (scale out) — добавление новых серверов. Неограниченное масштабирование, высокая отказоустойчивость, но требует stateless архитектуры и load balancer.
❓ Что такое stateless API и почему это важно для масштабирования?
Stateless API не хранит состояние сессии между запросами — каждый запрос содержит всю необходимую информацию (JWT token, параметры).

Это критично для horizontal scaling потому что:
  • Можно добавлять/удалять серверы без потери данных
  • Load balancer может отправить запрос на любой сервер
  • Нет проблем с sticky sessions
  • Автоматическое восстановление при падении сервера
❓ Какой load balancer выбрать: Nginx, HAProxy или AWS ALB?
Nginx — универсальный выбор:
  • Layer 7 (HTTP) балансировка
  • SSL termination, caching
  • Простая настройка
  • 100K+ RPS
HAProxy — максимальная производительность:
  • 1M+ concurrent connections
  • Layer 4 (TCP) и Layer 7 (HTTP)
  • Лучший для highload
AWS ALB — managed решение:
  • Автоматический scaling
  • Интеграция с AWS ECS/EKS
  • Не нужно управлять инфраструктурой
❓ Как масштабировать базу данных при horizontal scaling?
Ключевые стратегии:
  • Read Replicas — master для записи, replicas для чтения (масштабирование чтения в 5-10 раз)
  • Connection Pooling — переиспользование соединений, снижает overhead
  • Database Sharding — разделение данных по серверам (horizontal partitioning)
  • Caching — Redis/Memcached для снижения нагрузки на БД
  • Query Optimization — индексы, EXPLAIN ANALYZE
❓ Как масштабировать API в Kubernetes?
Kubernetes предоставляет:
  • Horizontal Pod Autoscaler (HPA) — автоматическое масштабирование по CPU/Memory/Custom metrics
  • Deployment с replicas — множественные поды приложения
  • Service — внутренний load balancing между подами
  • Ingress — внешний load balancing с SSL termination
  • Rolling updates — zero-downtime deployment
Пример: HPA автоматически масштабирует от 5 до 50 подов при росте CPU >70%.
❓ Сколько RPS может обработать один сервер?
Зависит от сложности логики:
  • Простой API (возврат JSON из cache): 10,000-50,000 RPS на 4 CPU cores
  • API с БД запросами (simple SELECT): 1,000-5,000 RPS
  • API с complex queries (JOIN, aggregation): 500-2,000 RPS
  • API с heavy вычислениями: 100-500 RPS
Для достижения 100K+ RPS нужен horizontal scaling с 10-50 серверами + Redis cache + read replicas.

Начните масштабировать ваш API

LightBox API — создайте Mock API для тестирования архитектуры без нагрузки на production

Начать бесплатно →

📝 Выводы

В этой статье мы рассмотрели как масштабировать API до 100K+ RPS через horizontal scaling:

  1. Stateless Design: JWT tokens или Redis sessions вместо in-memory storage
  2. Load Balancing: Nginx/HAProxy для распределения трафика между серверами
  3. Database Scaling: read replicas, connection pooling, sharding
  4. Kubernetes: HPA для автоматического масштабирования по метрикам
  5. Мониторинг: RPS, CPU, p99 latency, error rate
  6. Best Practices: design for failure, cache агрессивно, zero-downtime deployment

🎯 Главное:

Related Articles

← Вернуться к статьям