Падает сервер от поисковых роботов
Проблема: 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)