80 lines
6.3 KiB
Markdown
80 lines
6.3 KiB
Markdown
---
|
||
id: devops.nginx.interview
|
||
title: Nginx 面试题
|
||
type: interview
|
||
domain: devops
|
||
stack: [nginx]
|
||
tags: [Nginx, 反向代理, 负载均衡, 面试]
|
||
level: basic
|
||
related: [devops.nginx.proxy-static, devops.nginx.https-stream-limit]
|
||
week: [7, 14, 16]
|
||
---
|
||
|
||
# Nginx 面试题
|
||
|
||
## 基础
|
||
|
||
### 【基础】正向代理和反向代理的区别?
|
||
|
||
**参考答案**:正向代理代表客户端访问外部服务器,服务器看到的是代理(例如公司上网代理);反向代理代表服务器接收客户端请求,客户端看到的是代理,不知道后端有哪些服务器。Nginx 常用作反向代理,做负载均衡、TLS 终止、静态资源、限流。
|
||
|
||
**追问**:反向代理除了负载均衡还能做什么?→ HTTPS 终止、静态资源服务、缓存、限流、隐藏后端地址、统一的访问日志。
|
||
|
||
### 【基础】Nginx 的负载均衡策略有哪些?
|
||
|
||
**参考答案**:轮询(默认)、加权轮询(`weight`)、最少连接(`least_conn`)、IP 哈希(`ip_hash`,同一客户端固定到同一后端)、通用哈希(`hash $key`,可配合一致性哈希)。通过 `max_fails`、`fail_timeout` 做被动健康检查。
|
||
|
||
**追问**:怎么让同一个用户总是访问同一台后端?→ `ip_hash` 或一致性哈希(`hash $cookie_xxx consistent`);更好的做法是让服务无状态,会话存到 Redis。
|
||
|
||
### 【基础】location 的匹配优先级是怎样的?
|
||
|
||
**参考答案**:先看 `=` 精确匹配;再找最长前缀匹配,如果它带 `^~` 就直接使用;否则按顺序匹配正则(`~`、`~*`),第一个匹配的生效;没有正则匹配就用最长前缀匹配。
|
||
|
||
**追问**:`location /api` 和 `location /api/` 有什么区别?→ 前者也会匹配 `/apixyz`;配合 `proxy_pass` 时,结尾斜杠还会影响路径是否被替换。
|
||
|
||
### 【基础】前端单页应用部署到 Nginx 后刷新页面 404,为什么?
|
||
|
||
**参考答案**:前端路由(history 模式)的路径在服务器上没有对应文件。用 `try_files $uri $uri/ /index.html;` 让找不到的路径都返回 index.html,交给前端路由处理。
|
||
|
||
**追问**:`try_files` 会影响 API 请求吗?→ 如果 API 也在同一个 server 中,要给 `/api/` 单独写 location,否则不存在的 API 路径会返回 index.html 而不是 404。
|
||
|
||
## 进阶
|
||
|
||
### 【进阶】502 和 504 有什么区别?分别怎么排查?
|
||
|
||
**参考答案**:502 Bad Gateway 是 Nginx 连接后端失败或后端返回了无效响应——后端没启动、端口错、进程崩溃、连接被拒绝,看 Nginx 错误日志里的 `connect() failed` 等信息。504 Gateway Timeout 是后端在超时时间内没有响应——后端处理慢(慢查询、调用外部服务卡住),或者 `proxy_read_timeout` 设置太短(例如大模型长回答)。
|
||
|
||
**追问**:怎么区分是 Nginx 的问题还是后端的问题?→ 看 Nginx 的 `error.log`(connect refused / upstream timed out)和访问日志中的 `$upstream_response_time`、`$upstream_status`。
|
||
|
||
### 【进阶】代理 SSE 和 WebSocket 分别要注意什么?
|
||
|
||
**参考答案**:SSE 要关闭代理缓冲(`proxy_buffering off` 或后端返回 `X-Accel-Buffering: no`),否则流式内容被攒成一大段;读超时要放宽或者后端发心跳。WebSocket 需要 `proxy_http_version 1.1` 并传递 `Upgrade` 和 `Connection` 头完成协议升级,读超时也要调大。
|
||
|
||
**追问**:WebSocket 连接一段时间后断开是什么原因?→ `proxy_read_timeout` 默认 60 秒,没有数据传输就会断开;调大超时或在应用层发送心跳。
|
||
|
||
### 【进阶】Nginx 怎么限流?
|
||
|
||
**参考答案**:`limit_req_zone` 定义按某个 key(如 `$binary_remote_addr`)的请求速率,基于漏桶算法;`limit_req` 应用到 location,`burst` 允许排队的突发量,`nodelay` 让突发请求不排队立即处理;超出返回 `limit_req_status`(默认 503,常改为 429)。`limit_conn` 限制并发连接数。前面还有负载均衡时要用 `real_ip` 模块取真实客户端 IP。
|
||
|
||
**追问**:`burst` 和 `nodelay` 是什么意思?→ `burst` 允许突发的排队数量;`nodelay` 让突发的请求立即处理而不是按速率排队,超过 burst 的请求直接被拒绝。
|
||
|
||
## 场景题
|
||
|
||
### 【场景题】AI 问答的流式输出经过 Nginx 后变成一次性返回,怎么解决?
|
||
|
||
**参考答案**:Nginx 默认对代理响应做缓冲。给流式接口单独 location,设置 `proxy_buffering off`、`proxy_cache off`,HTTP/1.1 长连接,`proxy_read_timeout` 调大;后端响应头加 `X-Accel-Buffering: no`;确认 gzip 没有压缩 `text/event-stream`;如果前面还有 CDN 或云负载均衡,也要确认它们支持流式。详见[AI 应用网关场景](scenarios/ai-gateway-sse-canary.md)。
|
||
|
||
**追问**:除了 Nginx,还有哪些地方可能缓冲?→ CDN、云厂商的负载均衡、应用框架的中间件(如压缩中间件)都可能缓冲,要逐层检查;后端可以设置 `X-Accel-Buffering: no` 响应头。
|
||
|
||
### 【场景题】要让 10% 的用户先用新版本,Nginx 怎么做?
|
||
|
||
**参考答案**:用 `split_clients` 根据用户标识(如用户 ID Cookie 或 IP)哈希出 10% 映射到新版本 upstream;或者 `upstream` 中用 `weight` 按比例分配;指定用户体验用 `map $cookie_xxx`。注意同一用户要稳定落在同一版本(按用户标识哈希,而不是随机)、新旧版本接口兼容、能一键回滚。
|
||
|
||
**追问**:按百分比随机分流和按用户分流哪个好?→ 按用户(如用户 ID 的哈希)更好,同一个用户的体验一致,也便于排查问题;用 `split_clients` 对用户标识做分流。
|
||
|
||
### 【场景题】怎么做到发版时用户无感知(不中断请求)?
|
||
|
||
**参考答案**:后端多实例滚动发布:先把一台实例从 upstream 摘除(或标记 `down`)并 reload Nginx,等它处理完存量请求后升级,再加回去,逐台进行;后端程序要支持优雅退出。Nginx 自身修改配置用 `nginx -s reload`,旧 worker 处理完已有连接再退出,不会中断请求。
|
||
|
||
**追问**:`nginx -s reload` 会中断连接吗?→ 不会:新 worker 使用新配置处理新请求,旧 worker 处理完已有请求后退出。但长连接(WebSocket)会在旧 worker 退出时断开(`worker_shutdown_timeout` 控制)。
|