← Назад к вопросам
Что делать если веб-сервис начал зависать?
1.7 Middle🔥 101 комментариев
#Django#Git и VCS#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Диагностика и устранение зависаний веб-сервиса
В production это критическая ситуация. За 10+ лет разработал структурированный подход для быстрого восстановления сервиса.
Первые 5 минут: Стабилизация
Шаг 1: Определить масштаб проблемы
# Вопросы:
problems = {
"scope": "Зависает весь сервис или конкретный endpoint?",
"users_affected": "Все пользователи или избранные?",
"duration": "Первый раз или продолжается долго?",
"traffic": "Была ли spike в трафике?",
"recent_changes": "Были ли деплои в последние часы?"
}
Шаг 2: Быстрые действия
# 1. Проверить статус сервиса
curl -s http://localhost:8000/health | jq .
# 2. Посмотреть логи (последние 100 строк)
tail -n 100 /var/log/app.log
# 3. Проверить процесс
ps aux | grep python
top -p <PID>
# 4. Проверить БД соединения
# SELECT * FROM pg_stat_activity;
# Есть ли долгие транзакции?
# 5. Если совсем плохо - graceful restart
sudo systemctl restart app-service
Вторая фаза: Диагностика (5-15 минут)
Инструменты для анализа
# Инструмент 1: Профилирование памяти
import tracemalloc
tracemalloc.start()
# ... код ...
current, peak = tracemalloc.get_traced_memory()
print(f"Current: {current / 10**6}MB; Peak: {peak / 10**6}MB")
# Инструмент 2: Профилирование CPU
import cProfile
import pstats
pr = cProfile.Profile()
pr.enable()
# ... критичный код ...
pr.disable()
ps = pstats.Stats(pr)
ps.sort_stats('cumulative')
ps.print_stats(10) # Top 10
# Инструмент 3: Проверка threads
import threading
for thread in threading.enumerate():
print(f"Thread: {thread.name}, daemon={thread.daemon}")
Частые причины зависания
# 1. Утечка памяти (Memory Leak)
problems = []
while True:
problem = {"data": list(range(1000000))}
problems.append(problem) # Никогда не очищается!
# Решение: Использовать context manager, cleanup в finally
# 2. Блокирующие операции в async коде
async def bad_endpoint():
time.sleep(10) # ПЛОХО! Блокирует весь event loop
return "ok"
# Хорошо: используй async функции
async def good_endpoint():
await asyncio.sleep(10) # OK, не блокирует
return "ok"
# 3. Deadlock в БД
# Transaction 1: SELECT * FROM users WHERE id=1 FOR UPDATE
# Transaction 2: SELECT * FROM orders WHERE user_id=1 FOR UPDATE
# Потом пытаются поменяться местами - deadlock
# 4. Бесконечный цикл или рекурсия
def bad_recursion(n):
return bad_recursion(n+1) # Бесконечная рекурсия!
# 5. Slow query в БД
# SELECT * FROM huge_table WHERE status='pending' # Нет индекса!
Диагностика систем
Для Django приложения
# 1. Проверить количество БД соединений
from django.db import connections
for conn in connections.all():
print(f"Connection: {conn.alias}, in_atomic={conn.in_atomic_block}")
# 2. Включить SQL логирование
from django.db import connection
from django.test.utils import CaptureQueriesContext
with CaptureQueriesContext(connection) as context:
# ... код ...
for query in context:
print(f"{query['time']:.2f}s: {query['sql']}")
# 3. Найти медленные queries
from django.db import connection
from django.db.backends import connection
slow_queries = [
q for q in connection.queries
if float(q['time']) > 1.0
]
Для FastAPI приложения
# 1. Проверить активные endpoints
from fastapi import Request
from datetime import datetime
active_requests = {}
@app.middleware("http")
async def track_requests(request: Request, call_next):
request_id = str(datetime.now())
active_requests[request_id] = f"{request.method} {request.url.path}"
try:
response = await call_next(request)
finally:
del active_requests[request_id]
return response
# 2. Найти медленный endpoint
import time
from starlette.middleware.timing import TimingMiddleware
@app.middleware("http")
async def log_timing(request: Request, call_next):
start = time.time()
response = await call_next(request)
elapsed = time.time() - start
if elapsed > 1.0: # Если больше секунды
print(f"SLOW: {request.method} {request.url.path} took {elapsed:.2f}s")
return response
Стратегия восстановления
Если memory leak
# 1. Немедленно: Ограничить использование памяти
# В контейнер: memory limit = 2GB (по умолчанию unlimited)
# 2. Автоматический restart при превышении
# systemd service с MemoryMax
[Service]
MemoryMax=2G
MemoryAccounting=true
# 3.롱 term: Найти leak с помощью profiler
# python -m memory_profiler script.py
# или guppy3 для heap анализа
Если slow query
-- 1. Найти медленный query
SELECT query, mean_time, stddev_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
-- 2. Проверить план
EXPLAIN ANALYZE
SELECT * FROM orders WHERE status = 'pending';
-- 3. Добавить индекс
CREATE INDEX idx_orders_status ON orders(status);
-- 4. Переписать query если нужно
-- Плохо: SELECT * (берёт ненужные колонны)
-- Хорошо: SELECT id, user_id, total (только нужное)
Если deadlock в БД
# В Django ORM:
from django.db import transaction
# Используй select_for_update
with transaction.atomic():
user = User.objects.select_for_update().get(id=1)
# Заблокируем строку, никто другой не может её менять
user.balance -= 100
user.save()
# В PostgreSQL: использовать SKIP LOCKED
SELECT * FROM tasks
WHERE status = 'pending'
FOR UPDATE SKIP LOCKED -- Пропустить заблокированные
LIMIT 1;
Prevention: Как избежать зависаний
# 1. Мониторинг
from prometheus_client import Counter, Histogram
request_duration = Histogram('request_duration_seconds', 'Request duration')
active_requests = Gauge('active_requests', 'Active requests')
# 2. Rate limiting
from slowapi import Limiter
limiter = Limiter(key_func=get_remote_address)
@app.get("/api/data")
@limiter.limit("100/minute")
async def get_data():
return {"data": "value"}
# 3. Timeouts
import signal
def timeout_handler(signum, frame):
raise TimeoutError()
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(30) # 30 секунд
try:
result = slow_function()
finally:
signal.alarm(0) # Отменить таймаут
# 4. Circuit breaker для внешних сервисов
from pybreaker import CircuitBreaker
db_breaker = CircuitBreaker(fail_max=5, reset_timeout=60)
@db_breaker
def call_external_service():
return requests.get("http://slow-service/api")
Чеклист восстановления
- Проверить последние логи
- Проверить memory и CPU usage
- Проверить активные БД connections
- Проверить recent deployments
- Проверить медленные queries
- Проверить deadlocks
- Graceful restart если нужно
- Включить мониторинг/alerting
- Post-mortem анализ
Главное
Зависание — это симптом. Нужно найти причину (memory, CPU, lock), а не просто перезагружать сервис.