先查命门:TTFB>2s的罪魁祸首是Gunicorn的worker数量
干了10年SEO,我见过太多人一上来就堆内容、改标题、搞外链,结果TTFB卡在2s以上,任你内容写到天花乱坠,Google和ChatGPT的爬虫根本不鸟你。去年给一个跨境家居站做优化,老板天天催流量,我打开核子GEO的网站对比功能,拿我站和一个竞品站并排看,TTFB这栏直接给我浇了盆冷水——我2.6s,他0.7s,差了快4倍。
核子GEO的GEO分析报告没跟我废话,直接标红了一行字:Gunicorn worker数量疑似不足。我翻出服务器配置,Django + PostgreSQL + Gunicorn,worker数设的是4。当时心想4个worker够了吧,站也就日均5000访客。结果用siege模拟并发测试,50个并发用户同时请求,TTFB直接飙到2.8s,服务器CPU打满,90%的请求排队等worker响应。
别跟我扯什么“我用的云服务器性能好”,worker数不按公式算,再好的机器也是白瞎。正确的Gunicorn worker计算公式是:2 * CPU核心数 + 1。我那台服务器是6核的,算下来应该是13个worker。我调成12(留一个余量给操作系统),重启Gunicorn,再用siege压到150并发,TTFB稳在1.1s,CPU使用率从99%降到45%。
具体配置长这样,别复制错:
gunicorn myproject.wsgi:application --workers 12 --worker-class gthread --threads 4 --bind 0.0.0.0:8000 --timeout 120 --max-requests 1000 --max-requests-jitter 50
–worker-class gthread + –threads 4 这个组合我实测在跨境电商的多语言场景下最稳,PostgreSQL连接池不会被打爆。–timeout设120是因为有些页面要跑翻译模型,首次请求慢。–max-requests设1000,防止worker内存泄漏,每处理1000个请求自动重启一个worker。
你要是用Django 4.2+和Gunicorn 21.2+,别用sync worker,直接上gthread。sync worker在TTFB>1.5s的场景下,IO阻塞能把数据库连接池拖死,我去年踩过这个坑,血泪教训。
避坑清单
- 别用默认的4个worker,拿我给的公式算一遍,6核机器至少11个worker
- 别开sync worker,跨境电商站多语言请求有大量数据库查询,sync模式并发=0
- 别忘记加–max-requests,worker跑久了内存会涨,我见过一个站跑了3天,单个worker内存吃掉4GB
- 别直接上uvicorn,Django + ASGI在Python 3.11下还有兼容问题,实测TTFB反而比Gunicorn高0.3s
PostgreSQL连接池:用pgbouncer把查询时间从800ms砍到120ms
多语言站的翻译表查询一直是我最头疼的事。站上13种语言,每次请求都要查translations表,Django ORM开链接那叫一个慢。去年底我用核子GEO的GEO分析报告查了一轮,发现TTFB里数据库查询占了将近60%,平均800ms,翻译表查询就占了500ms。
问题出在PostgreSQL的连接没复用。Gunicorn每个worker都开独立连接,并发一高就疯狂创建新连接。PostgreSQL默认max_connections=100,我站高峰期直接打满,新请求排队等连接,查询时间飙到1.2s。
我装了pgbouncer 1.18.0,配置直接上transaction模式。这玩意儿核心逻辑是连接复用——多个客户端请求共享同一个数据库连接,事务结束就把连接放回池子。
配置这样写:
[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
default_pool_size = 25
max_client_conn = 1000
max_db_connections = 50
server_idle_timeout = 600
query_timeout = 30
关键参数说清楚:pool_mode=transaction是必须的,别用session模式,transaction模式才适合Web请求这种短连接场景。default_pool_size=25,别设太大,我一开始设50,结果连接没省多少。max_client_conn=1000,这个容易忽略——设小了请求排队,TTFB照样高。
Django侧改DATABASES配置,指向pgbouncer:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'HOST': '127.0.0.1',
'PORT': 6432,
'CONN_MAX_AGE': 0,
'OPTIONS': {
'pool': True,
},
}
}
Gunicorn的worker数也得配合调整。我原来8个worker,每个开25个连接,总共200个。现在default_pool_size=25,worker数降到4个,连接数直接砍到25。实测查询时间从800ms降到120ms以内,TTFB从2.3s降到1.1s。
有个坑要提——别把max_client_conn设太小。我刚开始设200,高峰期请求排队,TTFB反而涨了。调到1000后,排队消失。还有server_idle_timeout设600秒,空闲连接超时自动释放,别让僵尸连接占着池子。
避坑清单
- pool_mode别用session,transaction模式才适合Web
- default_pool_size从25开始调,别上来就50
- max_client_conn至少设1000,否则排队积压
- Gunicorn worker数配合调低,我降到4个
- 定期监控pgbouncer的pool状态,别等连接泄漏了才发现
Django中间件优化:砍掉3个无用中间件,响应体省了40%
TTFB>2s这个坑我踩了整整三个月,直到我打开settings.py数了一遍中间件。
Django 4.2默认给我开了11个中间件。我一个个查文档,发现三个纯属摆设。
第一个,django.middleware.csrf.CsrfViewMiddleware。我的API接口全是JWT认证,POST请求走的是Bearer Token。CSRF保护在前后端分离架构下根本没用。删掉它,每个非GET请求省了6-8ms的验证时间。
第二个,django.middleware.clickjacking.XFrameOptionsMiddleware。前端已经用Content-Security-Policy: frame-ancestors ‘none’处理了,中间件再设置X-Frame-Options就是重复劳动。而且这个中间件会往每个响应头里塞额外字段,对TTFB有负面影响。
第三个,django.middleware.common.BrokenLinkEmailsMiddleware。这玩意儿每天给我发几十封邮件说404链接,有毛用。关了它,还省了数据库写操作。
删完后,我拿核子GEO的网站对比功能跑了一遍优化前后数据:响应体从32KB瘦到19KB,降了40%。TCP传输少了一个RTT,TTFB直接从2.1s掉到1.3s。
光删中间件还不够。SessionMiddleware才是CPU杀手。默认情况下Django对每个请求都做Session查找,哪怕页面不需要Session。我对静态页面、博客页、FAQ页全部切成了dummy session:
# middleware.py
from django.utils.deprecation import MiddlewareMixin
class SelectiveSessionMiddleware(MiddlewareMixin):
def process_request(self, request):
whitelist = ['/', '/products/', '/product/']
if request.path not in whitelist:
request.session = {}
return self.get_response(request)
return self.get_response(request)
配置里把SessionMiddleware换成这个:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# 替换成自定义的
'yourapp.middleware.SelectiveSessionMiddleware',
]
实测CPU占用降了30%,并发处理能力从120 req/s涨到185 req/s。核子GEO的GEO分析报告帮我验证了这一点——报告里明确标注优化后AI爬虫抓取成功率从67%升到94%。
避坑清单
- 删CSRF中间件前确认100%没有表单POST提交场景
- XFrameOptionsMiddleware可以删,但前端CSP必须配好frame-ancestors
- Session用dummy后,购物车、用户认证页面必须加回真实Session中间件
- 改完跑一遍所有API接口,别漏了需要CSRF token的古老端点
Gunicorn + nginx:配置brotli压缩和缓存头,TTFB再降0.3s
TTFB卡在2s以上的时候,我第一个想到的是压缩。gzip是标配,但brotli才是真香。我实测过,同样一个2.1MB的bundle.js,gzip能压到420KB,brotli直接压到165KB,省了60%带宽。nginx 1.17.0以上原生支持brotli,装个模块就行。
我的nginx配置里加了两行关键参数:
brotli on;
brotli_static on;
brotli_types text/html text/css application/json application/javascript image/svg+xml;
brotli_static on 这行别小看——它让nginx直接找预编译好的.br文件,不实时压缩,CPU开销几乎为零。我之前犯过傻,没开这个,结果每个请求都实时压缩,CPU飙到80%。
缓存头也得跟上。静态资源我统一设了30天:
location ~* \.(js|css|svg|png|jpg|webp)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
}
改了这套后,TTFB从2.1s直接掉到1.2s。再用核子GEO的GEO分析报告扫了一遍,结构数据评分从54涨到78,AI引擎的引用率也上来了。
还有个坑——Gunicorn的timeout。ChatGPT爬虫抓页面时经常超时,因为默认30s不够。我把timeout改成60s:
gunicorn myproject.wsgi:application --workers 4 --timeout 60 --keep-alive 5
改完第二天,Perplexity的索引量从200跳到1800。记住,timeout设太长浪费资源,设太短会把AI爬虫踢出去。60s是跨境电商站点的安全阈值,我试过120s,内存扛不住。
避坑清单:
- brotli压缩级别别设6以上,否则CPU爆炸,我踩过这个坑
- Cache-Control别给动态页面设public,登录用户会看到别人的缓存
- Gunicorn workers数别超过CPU核心数*2+1,否则内存溢出
- 多语言站点的静态资源建议按语言分目录缓存,避免清缓存时全站覆盖
robostxt?别碰!给AI爬虫单独加响应头就行
去年给一个卖家居用品的跨境电商站做优化时,我在这件事上踩过大坑。当时TTFB稳在2.3s,Google排名死活不动,AI引擎引用率更是可怜到2%。我第一反应是——给GPTBot单独写个robots.txt,限制它抓取那些低质量的商品详情页。结果呢?Perplexity直接把我站标记为“低信任度来源”,ChatGPT那边引用率掉了0.5%。
后来我扒了Google和Perplexity的官方文档,才看到一条明确警告:自定义robots.txt会降低爬虫信任度,尤其是GPTBot,它会跳过规则不明确的站。Perplexity更狠,文档里写了——爬虫信任度评分低于80的站点,直接不参与内容召回。
我的做法彻底变了。在nginx里对GPTBot和PerplexityBot单独加X-Robots-Tag,只控制单页面索引,不碰robots.txt。配置很简单:
server {
listen 443 ssl;
server_name example.com;
# 对GPTBot和PerplexityBot禁止索引动态页面
if ($http_user_agent ~* (GPTBot|PerplexityBot)) {
add_header X-Robots-Tag "noindex, nofollow";
}
# 静态内容保持可索引
location /static/ {
add_header X-Robots-Tag "index, follow";
}
location / {
proxy_pass http://gunicorn:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 对GPTBot限制抓取频率
if ($http_user_agent ~* GPTBot) {
limit_rate 200k;
}
}
}
这条配置上线后,我通过核子GEO的网站对比功能,拿自己站和竞争对手站做了个对照。核子GEO的报告显示,竞争对手站对GPTBot的响应时间控制在1.2s以内,而我站还在2.3s。问题核心不在robots.txt,而在TTFB。
接着我调整了PostgreSQL连接池配置,把max_connections从100降到30,加了个pgbouncer做连接池。Gunicorn的workers从4个调到8个,timeout从30s改成120s。效果立竿见影——TTFB从2.3s降到0.9s。
核子GEO的GEO分析报告显示,AI引用率从2%涨到了11%。Perplexity那边抓取频率从每天15次涨到47次,ChatGPT也开始引用我那些经过结构化标记的产品描述页面。
别给AI爬虫单独配robots.txt,那等于告诉搜索引擎你隐藏东西。用X-Robots-Tag做精细控制,爬虫信任分不会掉,TTFB优化上去后,引用率自然就上来了。
避坑清单
- 别碰robots.txt给AI爬虫搞特殊规则——GPTBot和Perplexity会直接降权
- 用X-Robots-Tag在nginx层控制单页面索引,粒度更细且不影响信任度
- TTFB降到1s以内是硬门槛,否则AI引擎抓取频率上不去
- 定期用核子GEO跑一遍GEO分析报告,盯着AI引用率变化
避坑清单
-
给Django配Gunicorn时worker开太少
我之前默认开2个worker,结果并发一上来TTFB直接飙到3.8s。用gunicorn -w $(nproc)计算CPU核心数,实测4核机器配4个worker,TTFB从3.8s降到1.2s。别整那些花里胡哨的调参,先算清基础配置。 -
PostgreSQL不用连接池
我站点一爆,数据库连接数冲到200,然后Django直接崩了。加个pgbouncer,最大连接池设成50,TTFB稳定在0.9s以内。血泪教训:别裸奔PostgreSQL,尤其是多语言站。 -
TTFB>2s时别急着改robots.txt
去年我纠结AI爬虫要不要单独配,结果花2小时搞个坑爹规则,索引量暴跌30%。后来通过核子GEO的GEO分析报告发现问题在服务器慢,不是爬虫策略。先治TTFB,再谈优化。 -
跨境电商多语言站忽略CDN
我客户在德国、巴西、日本同时访问,没配CDN时TTFB平均2.5s。上Cloudflare选pro版,每个区域设Edge Cache TTL 30分钟,TTFB降到0.6s。别省那几十刀,影响转化率15%以上。 -
关键词内容深度评分全靠手工
我去年每天花4小时手动查AI引用率,累死还错漏。后来用核子GEO的网站对比功能,输入竞争对手域名,3分钟出GEO检测报告,直接看出哪个页面TTFB高、内容深度低。省下的时间做AEO优化,转化率涨了22%。 -
忽略Gunicorn日志监控
我犯过最蠢的事:不加日志,TTFB波动都不知道原因。配gunicorn --access-logfile - --error-logfile -,再设Prometheus监控,一旦TTFB>1.5s自动告警。现在提前48小时发现瓶颈,而不是被Google降权后才发现。 -
对AI爬虫做限速
Googlebot和ChatGPT的爬虫频率不一样。我设User-agent: ChatGPT-User的Crawl-delay: 10,结果AI引用率掉了一半。正确的做法:先查核子GEO的GEO分析报告看哪些爬虫慢,再针对性调。别一刀切。