Python技术迷

字节一面:网站显示不出来,怎么排查?

网站打开一片白,或者直接 502、504、连接超时,这题我一般不先猜代码。

先定边界:到底是“网页没出来”,还是“服务没起来”,还是“请求到了但回不来”。这三种,排查顺序完全不是一回事。线上真查这种问题,我通常就是一条链路往下捋:DNS → 端口 → Nginx → Python 进程 → 依赖资源。别一上来就冲进业务代码里翻。

这题面试里最容易答飘。

“网站显示不出来”至少有 5 种现场:

  1. 浏览器直接说无法访问,连连接都建不起来
  2. 能打开,但返回 404 / 403 / 500 / 502 / 504
  3. HTML 出来了,但 CSS、JS 没出来,页面像裸奔
  4. 首页能开,点登录或接口请求一直转圈
  5. 偶发能开,偶发打不开

我一般先问两句,不是为了拖时间,是为了把范围一下砍小:

# 先看 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 time

targets = [
    ("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 重启、探针失败和资源限制。 这类问题我一般不先看业务代码,先把链路一层层排干净,定位会快很多。