Ваш API не справляется с нагрузкой? Один сервер упирается в лимиты CPU/RAM? В этом руководстве мы разберем горизонтальное масштабирование API до 100K+ RPS. Вы узнаете про stateless design, load balancing (Nginx, HAProxy, AWS ALB), session management (Redis, JWT), database sharding, read replicas и Kubernetes deployment.
📋 Содержание
📊 Vertical vs Horizontal Scaling
🔼 Vertical Scaling (Scale Up)
Увеличение ресурсов одного сервера: больше CPU, RAM, SSD.
✅ Преимущества:
- Простота — не требует изменений в коде
- Нет сетевой задержки — все на одном сервере
- Проще для БД — нет проблем с распределенными транзакциями
❌ Недостатки:
- Физические лимиты — нельзя бесконечно увеличивать CPU/RAM
- Дорого — сервер с 128 CPU стоит в разы дороже чем 4 сервера по 32 CPU
- Single Point of Failure — если сервер упал, весь API недоступен
- Downtime при апгрейде — нужно останавливать сервер
➡️ Horizontal Scaling (Scale Out)
Добавление новых серверов: распределение нагрузки между множеством инстансов.
✅ Преимущества:
- Неограниченное масштабирование — просто добавляй серверы
- Высокая отказоустойчивость — если один сервер упал, остальные работают
- Дешевле — commodity hardware вместо дорогих серверов
- Zero-downtime deployment — обновляй серверы по одному
- Географическое распределение — серверы в разных регионах
❌ Недостатки:
- Сложность архитектуры — требует stateless design
- Network latency — запросы между серверами
- Сложнее для БД — нужны read replicas, sharding
- Дороже инфраструктура — load balancer, monitoring, orchestration
| Критерий | 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 средних серверов (надежно) |
🎯 Вывод: когда что использовать?
- Vertical Scaling: небольшие проекты (<10K RPS), monolithic приложения, legacy системы
- Horizontal Scaling: highload проекты (>10K RPS), microservices, cloud-native приложения
- Hybrid подход: вертикальное масштабирование каждого сервера + горизонтальное добавление серверов
🔄 Stateless API Design
Stateless API — это API, которое не хранит состояние сессии между запросами. Каждый запрос содержит всю необходимую информацию для обработки.
✅ Почему stateless критично для horizontal scaling:
- Любой сервер может обработать запрос — load balancer может отправить на любой инстанс
- Легко добавлять/удалять серверы — без потери данных сессии
- Нет проблем с sticky sessions — не нужно привязывать пользователя к одному серверу
- Автоматическое восстановление — если сервер упал, запрос идет на другой
❌ 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
Что мониторить:
- RPS per instance — сколько запросов обрабатывает каждый сервер
- CPU/Memory per instance — нагрузка на каждый сервер
- p99 latency — не ухудшается ли при добавлении серверов
- Error rate — процент ошибок (должен быть <0.1%)
- Active connections — количество активных соединений
- Database connections pool — утилизация connection pool
- Redis hit rate — эффективность кэша
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
- Любой сервер может упасть в любой момент
- Load balancer должен автоматически исключать failed instances
- Health checks на каждом сервере
- Graceful shutdown — завершить активные запросы перед выключением
2. Stateless Everything
- Храните сессии в Redis/JWT, не в памяти сервера
- Файлы загружайте в S3/Cloud Storage, не на локальный диск
- Background jobs в очередь (RabbitMQ, Redis Queue)
3. Cache Агрессивно
- Redis cache для read-heavy данных
- CDN для статики
- HTTP cache headers (ETag, Cache-Control)
4. Database Optimization
- Read Replicas для масштабирования чтения
- Connection Pooling обязателен
- Индексы на все WHERE/JOIN/ORDER BY поля
- Sharding для очень больших таблиц (>100M rows)
5. Auto-scaling
- Используйте HPA в Kubernetes
- Настройте scaling по CPU, Memory и Custom metrics (RPS)
- Min replicas должен справляться с базовой нагрузкой
- Max replicas = peak traffic * 1.5 (запас)
6. Zero-Downtime Deployment
- Rolling update в Kubernetes
- Blue-Green deployment
- Canary deployment (5% трафика на новую версию)
🔍 FAQ: Часто задаваемые вопросы
Горизонтальное масштабирование (scale out) — добавление новых серверов. Неограниченное масштабирование, высокая отказоустойчивость, но требует stateless архитектуры и load balancer.
Это критично для horizontal scaling потому что:
- Можно добавлять/удалять серверы без потери данных
- Load balancer может отправить запрос на любой сервер
- Нет проблем с sticky sessions
- Автоматическое восстановление при падении сервера
- Layer 7 (HTTP) балансировка
- SSL termination, caching
- Простая настройка
- 100K+ RPS
- 1M+ concurrent connections
- Layer 4 (TCP) и Layer 7 (HTTP)
- Лучший для highload
- Автоматический scaling
- Интеграция с AWS ECS/EKS
- Не нужно управлять инфраструктурой
- Read Replicas — master для записи, replicas для чтения (масштабирование чтения в 5-10 раз)
- Connection Pooling — переиспользование соединений, снижает overhead
- Database Sharding — разделение данных по серверам (horizontal partitioning)
- Caching — Redis/Memcached для снижения нагрузки на БД
- Query Optimization — индексы, EXPLAIN ANALYZE
- Horizontal Pod Autoscaler (HPA) — автоматическое масштабирование по CPU/Memory/Custom metrics
- Deployment с replicas — множественные поды приложения
- Service — внутренний load balancing между подами
- Ingress — внешний load balancing с SSL termination
- Rolling updates — zero-downtime deployment
- Простой 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
Начните масштабировать ваш API
LightBox API — создайте Mock API для тестирования архитектуры без нагрузки на production
Начать бесплатно →📝 Выводы
В этой статье мы рассмотрели как масштабировать API до 100K+ RPS через horizontal scaling:
- Stateless Design: JWT tokens или Redis sessions вместо in-memory storage
- Load Balancing: Nginx/HAProxy для распределения трафика между серверами
- Database Scaling: read replicas, connection pooling, sharding
- Kubernetes: HPA для автоматического масштабирования по метрикам
- Мониторинг: RPS, CPU, p99 latency, error rate
- Best Practices: design for failure, cache агрессивно, zero-downtime deployment
🎯 Главное:
- Horizontal scaling позволяет неограниченно масштабироваться
- Stateless architecture — обязательное требование
- Load balancer (Nginx/HAProxy) для распределения трафика
- Database scaling через read replicas и connection pooling
- Kubernetes HPA для автоматического scaling
- Для 100K RPS нужно 10-50 серверов в зависимости от сложности логики
Related Articles
- API Load Testing: Apache JMeter, Gatling, K6
- API p99 Latency: как достичь <100ms
- API Performance: оптимизация производительности
- API Caching: стратегии кэширования