字节一面:网站显示不出来,怎么排查?
网站打开一片白,或者直接 502、504、连接超时,这题我一般不先猜代码。
先定边界:到底是“网页没出来”,还是“服务没起来”,还是“请求到了但回不来”。这三种,排查顺序完全不是一回事。线上真查这种问题,我通常就是一条链路往下捋:DNS → 端口 → Nginx → Python 进程 → 依赖资源。别一上来就冲进业务代码里翻。
这题面试里最容易答飘。
“网站显示不出来”至少有 5 种现场:
浏览器直接说无法访问,连连接都建不起来 能打开,但返回 404 / 403 / 500 / 502 / 504 HTML 出来了,但 CSS、JS 没出来,页面像裸奔 首页能开,点登录或接口请求一直转圈 偶发能开,偶发打不开
我一般先问两句,不是为了拖时间,是为了把范围一下砍小:
# 先看 DNS
nslookup www.demo.com# 再看链路
ping www.demo.com
# 再看 HTTP 响应头
curl -I http://www.demo.com
# 直接看完整响应和耗时
curl -vvv -m 5 http://www.demo.com
如果 curl 都不通,那前端页面写得再漂亮也没意义,先看接入层。 如果 curl 通,浏览器不通,那就要怀疑静态资源、跨域、缓存、证书这些前端侧问题。
很多人一说网站打不开,脑子里默认是服务挂了。其实线上真不少是端口没监听、防火墙挡了、SLB 配错了。
先看机器在不在,端口有没有开:
# 机器进得去吗
ssh [email protected]# 80/443 有没有监听
ss -lntp | grep -E '80|443|8000|8080'
# 外部端口测一下
telnet www.demo.com 80
nc -vz www.demo.com 443
这里能很快分出来两种情况:
一种是 80/443 根本没人监听,那就别扯 Nginx 反向代理了,服务入口都没起来。
另一种是 443 在监听,但 curl 还是失败,这时候多半要看证书、SNI、网关策略,或者上游代理转发错了。
面试里说到这一步,其实已经比那种“先重启服务试试”强不少了。
Python 网站,尤其是 Django、Flask、FastAPI 这种,线上经常不是应用自己直接对外,而是 Nginx 在前面挡着。
所以我会先看 Nginx 配置和日志,而不是先 grep Python 代码。
# 检查配置是否合法
nginx -t# 看 error 日志
tail -n 100 /var/log/nginx/error.log
# 看 access 日志,确认请求到底进没进来
tail -n 100 /var/log/nginx/access.log
比如一眼就很典型的几种日志:
connect() failed (111: Connection refused) while connecting to upstream
upstream timed out (110: Connection timed out) while reading response header from upstream
open() "/data/www/static/app.css" failed (2: No such file or directory)
这三条基本就够定方向了:
Connection refused:Nginx 能收到请求,但后面的 Python 服务没起来,或者 upstream 写错了upstream timed out:Python 服务在处理,但太慢,Nginx 等不及static not found:页面骨架可能出来了,但静态资源路径错了
很多“网站显示不出来”,其实是第三种。HTML 在,JS 404,页面就像死了一样。
Nginx 里我会重点看这几段:
server {
listen 80;
server_name www.demo.com; location /static/ {
alias /data/app/static/;
}
location / {
proxy_pass http://127.0.0.1:8001;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
}
这里很容易踩两个坑:
一个是 root 和 alias 配混了,静态资源路径拼错。 另一个是 proxy_pass 指到了错误端口,Nginx 没问题,后端也没问题,就是转发错地方了。
这一步才轮到 Python 服务本体。
别光看 supervisor 里写着 running,就觉得服务一定正常。很多进程活着,但 worker 已经卡死了,或者启动后监听错端口。
ps -ef | grep -E 'gunicorn|uvicorn|uwsgi'
lsof -i:8001
如果是 Gunicorn,我一般会顺手看下启动参数:
ps -ef | grep gunicorn
有时候问题就出在这几项:
gunicorn app:app -w 4 -b 127.0.0.1:8001 --timeout 30
比如:
-b绑错地址,绑成了127.0.0.1,但容器外层想走别的网卡worker 数过少,请求一多就全堵住 timeout 太短,慢接口直接被砍掉
如果是 FastAPI / Flask,我还会本机打一下后端,不经过 Nginx,先隔离问题:
curl -v http://127.0.0.1:8001/health
curl -v http://127.0.0.1:8001/
如果本机直连应用能通,外部访问不通,那锅基本就在 Nginx 或网络层。 如果本机直连都超时,那就继续往应用和依赖查。
这题我不太喜欢那种泛泛一句“看日志”。
日志要看哪层,不一样的。
我会同时盯两边:
# Nginx
tail -f /var/log/nginx/access.log /var/log/nginx/error.log# Python 应用
tail -f logs/app.log
比如 Python 里如果看到这种:
django.db.utils.OperationalError: could not connect to server
redis.exceptions.ConnectionError: Error 111 connecting to 10.1.2.8:6379
requests.exceptions.ReadTimeout: HTTPConnectionPool(host='user-service', port=9000)
那页面打不开,本质可能压根不是网站本身挂了,而是网站首页依赖的 DB、Redis、下游服务没通。
这种情况线上很常见:首页接口里顺手查了数据库、顺手调了推荐服务、顺手拿了 Redis 缓存。任何一个依赖抽风,最后用户看到的都是“网站打不开”。
这个时候我一般不纠结页面显示没显示,我先把首页链路拆出来。
面试聊 Python,别只会说 curl。我一般会补一个自己常用的小探针。
import socket
import requests
from time import timetargets = [
("nginx", "http://127.0.0.1"),
("app", "http://127.0.0.1:8001/health"),
]
defcheck_tcp(host, port, timeout=2):
s = socket.socket()
s.settimeout(timeout)
start = time()
try:
s.connect((host, port))
returnTrue, round((time() - start) * 1000, 2), ""
except Exception as e:
returnFalse, 0, str(e)
finally:
s.close()
for name, url in targets:
start = time()
try:
r = requests.get(url, timeout=3)
cost = round((time() - start) * 1000, 2)
print(f"[HTTP] {name}{r.status_code}{cost}ms")
except Exception as e:
print(f"[HTTP] {name} FAIL {e}")
for host, port in [("127.0.0.1", 8001), ("127.0.0.1", 6379), ("10.1.2.5", 3306)]:
ok, cost, err = check_tcp(host, port)
print(f"[TCP] {host}:{port} ok={ok} cost={cost} err={err}")
这个脚本不花哨,但现场很好用。
因为很多时候不是“应用挂了”,而是应用依赖挂了。你把 HTTP、TCP 分开打一遍,问题会快很多。
如果是 502 / 504,我基本会优先怀疑这几类
1)Python 服务没起来
最常见,也最没技术含量。
Nginx 报:
connect() failed (111: Connection refused) while connecting to upstream
那就去看进程、端口、启动脚本。
2)Python 服务太慢,被 Nginx 干掉
日志一般长这样:
upstream timed out (110: Connection timed out) while reading response header from upstream
这时候别急着调大超时。先看慢在哪。
是首页 SQL 慢了:
rows = Article.objects.filter(status=1).order_by("-id")[:20]
还是你在首页里串行调了 5 个外部接口:
profile = requests.get(profile_api, timeout=2).json()
notice = requests.get(notice_api, timeout=2).json()
feed = requests.get(feed_api, timeout=2).json()
这种代码平时看着没什么,线上依赖一抖,首页就跟着一起跪。
3)静态资源路径配错
页面“显示不出来”,有时候不是接口挂了,是 CSS/JS 全 404。
浏览器 F12 一开,清一色红的:
GET /static/app.js 404
GET /static/app.css 404
这时候看 Django 配置就很快:
STATIC_URL = "/static/"
STATIC_ROOT = "/data/app/static/"
再看 Nginx:
location /static/ {
alias /data/app/static/;
}
这两个路径只要有一个没对上,页面基本就废了。
4)数据库、Redis、下游接口拖死首页
这种最烦,因为用户看见的是“网站打不开”,但真实故障点压根不在 Web 层。
我一般会把首页代码里所有外部依赖先画出来:
defhomepage():
banner = get_banner_from_redis()
hot = query_hot_articles()
profile = requests.get(USER_CENTER_URL, timeout=1.5).json()
return render_template("index.html", banner=banner, hot=hot, profile=profile)
这里 Redis、MySQL、用户中心任何一个超时,首页就慢。
这题真答到这一步,面试官基本知道你不是背模板的。
如果服务在容器里,再补一刀容器层
不少人答网站排查,完全不提 Docker / K8s,这就有点像只活在本机开发环境里。
如果是容器化部署,我会顺手加几步:
docker ps
docker logs app-container --tail 100
docker exec -it app-container sh
kubectl get pod -n prod
kubectl describe pod web-7f6d9c
kubectl logs web-7f6d9c -n prod --tail=100
经常会碰到这种事:
Pod 在反复重启 liveness probe 配错,把正常服务不停踢掉 容器里服务监听了 127.0.0.1,Service 根本打不进去 内存打满,进程被 OOMKilled
这些问题不看容器层,单靠应用日志有时是看不全的。
面试里我会把“排查顺序”说死,不会东一榔头西一棒子
我自己更认可这种回答方式:
先确认故障现象 再确认请求有没有到入口层 再确认入口层能不能转发到 Python 服务 再确认 Python 服务是不是正常处理 最后确认是不是依赖拖死了页面
顺着这个顺序往下走,大概就是:
1. DNS 是否正常
2. 80/443 端口是否可达
3. Nginx access/error 日志是否有请求
4. upstream 配置是否正确
5. Gunicorn/Uvicorn/uWSGI 进程是否存活、端口是否监听
6. 应用日志有无异常
7. 静态资源路径是否正确
8. MySQL/Redis/下游 HTTP 服务是否异常
9. 容器/探针/资源限制是否有问题
这种答法的好处是,不玄学。
因为网站打不开,最后无非就死在这条链路上的某一段。别一上来就“可能是前端问题,也可能是后端问题,也可能是网络问题”,这跟没答差不多。
面试官如果问我:“网站显示不出来,怎么排查?”
我会直接这么答:
我会先界定现象,是完全访问不了,还是返回 502/504,还是页面骨架在但静态资源没加载。 然后我按链路排查:先用 nslookup、curl、telnet 看 DNS、网络和端口;再看 Nginx access/error 日志,确认请求是否到达以及 upstream 是否异常;接着看 Python 进程和端口监听,确认 Gunicorn/Uvicorn 是否正常;如果 Web 层没问题,再看应用日志,排查数据库、Redis、下游接口超时;如果是页面样式丢失,再单独查静态资源路径和 Nginx 的 alias/root 配置。 如果在容器环境,我还会补查 Pod 重启、探针失败和资源限制。 这类问题我一般不先看业务代码,先把链路一层层排干净,定位会快很多。