Падает сервер от поисковых роботов

Проблема: uvicorn не выдерживает нагрузку поисковых роботов, при парсинге sitemap (3 млн урл, разбиты по 5 тыс. на один файл сайтмапки). В логах uvicorn'a ошибок нет, Nginx пишет *51966 upstream timed out (110: Operation timed out) while reading response header from upstream. В интернетах пишут об увеличении времени ожидания, при помощи директивы proxy_read_timeout, но это не решает основной проблемы (виснуть начинает весь сайт, пользователи ждут загрузки страниц очень долго)

Когда роботы начинают проходить по сайтмапу, другие пользователи тоже отваливаются. Проблема появилась только после начала использования докера.

Что пытался сделать:

  • Выделил для контейнера uvicorn 2 ядра CPU, не помогло.
  • Оптимизировал генерацию сайтмапки, вместо 4 запросов к бд, стал 1.

docker-compose.yml

version: '3.3'

services:
    db:
        ...
    redis:
        ...
    celery:
        ...
    uvicorn:
        build: .
        restart: "${DOCKER_RESTART_POLICY:-unless-stopped}"
        stop_grace_period: "${DOCKER_STOP_GRACE_PERIOD:-3s}"
        working_dir: /usr/src/app/
        command: uvicorn --host 0.0.0.0 --port 8001 --proxy-headers --log-level error --workers 5 app.asgi:asgi_application
        volumes:
            - .:/usr/src/app/
        ports:
            - :8001
        environment:
            - REDIS_HOST=redis
            - REDIS_PORT=6379
        env_file:
            - .docker/.env
        depends_on:
            - celery
        links:
            - redis
        deploy:
            resources:
                limits:
                    cpus: "2"
    nginx:
        image: nginx:1.23.3-alpine
        volumes:
            - ./media:/etc/nginx/www/app/media
            - ./static:/etc/nginx/www/app/static
            - .docker/nginx/conf:/etc/nginx/templates/
            - .docker/nginx/letsencrypt/:/etc/letsencrypt/
        ports:
            - 8000:8000
            - 443:443
        environment:
            - NGINX_HOST=site.ru
            - UVICORN_HOST=uvicorn
            - UVICORN_PORT=8001
        depends_on:
            - uvicorn

nginx.conf

server {
    server_name     site.ru;
    
    access_log      /var/log/nginx/site.access.log;
    error_log       /var/log/nginx/site.error.log;

    charset         utf-8;
    client_max_body_size 1M;

    add_header X-XSS-Protection "1; mode=block";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Permitted-Cross-Domain-Policies none;

    location /media/  {
        alias /etc/nginx/www/site.ru/media/;
    }

    location /static/ {
        alias /etc/nginx/www/site.ru/static/;
        
        gzip    on;
        expires 160d;
        add_header Cache-Control public;
    }

    location / {
        proxy_pass http://${UVICORN_HOST}:${UVICORN_PORT}/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_redirect off;
        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-Host $server_name;
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/site.ru/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/site.ru/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = site.ru) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    if ($host = site.ru) {
    return 301 https://$host$request_uri;
    }

    listen          80;
    listen      [::]:80;
    server_name     site.ru www.site.ru;
    return 404; # managed by Certbot
}

asgi.py

import os

from django.core.asgi import get_asgi_application


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')
http = get_asgi_application()

import core.routing as core

from channels.routing import ProtocolTypeRouter, URLRouter

asgi_application = ProtocolTypeRouter({
    "http": http,
    "websocket": URLRouter(core.websocket_urlpatterns)
})

sitemap.py

class ObjSitemap(sitemaps.Sitemap):
    priority = 0.5
    protocol = 'https'
    changefreq = 'monthly'
    limit = 5000

    def items(self):
        return Obj.objects.select_related("code").select_related("code__code1").select_related("code__code1__code2").filter(active=True).order_by("pk")

    def location(self, item):
        url_data = {
            'code': item.code.code1.code2,
            'code1': item.code.name,
            'code1_id': item.code.id,
        }
        return reverse('code:code_url', kwargs=url_data)

Ответы (0 шт):