Q86. 什么是"惊群"问题?Nginx 是如何解决的?
1. 是什么 :惊群(thundering herd)指多个进程/线程同时阻塞在同一个事件上(如对同一监听 socket 执行 accept),当事件到达时内核把所有等待者全部唤醒,但最终只有一个能成功处理,其余被唤醒后发现无事可做又重新睡眠,造成大量无谓的上下文切换和 CPU 消耗。
2. 为什么用它:Nginx 是典型的"master + 多 worker"模型,多个 worker 进程默认都监听同一个端口。若无保护机制,一个连接到达会唤醒所有 worker,大部分白忙一场。解决惊群能显著降低高并发下的 CPU 开销。
3. 怎么用它(Nginx 的解决手段,按演进顺序):
- accept_mutex 互斥锁 (早期方案):多个 worker 共享一把 accept 锁,只有抢到锁的 worker 才能 accept 新连接,其余 worker 在锁上等待,避免同时被唤醒。旧版本默认
accept_mutex on;。 - listen 的 reuseport(Linux 3.9+,Nginx 1.9.1+ 支持):为每个 worker 建立独立的监听 socket,由内核按 4 元组 hash 把新连接直接分发到某个 worker 的独立队列,无需抢锁,从源头消除惊群,吞吐更高:
nginx
server {
listen 8080 reuseport; # 每个 worker 独立队列,内核负载均衡
}
- EPOLLEXCLUSIVE (内核 4.5+):epoll 事件只唤醒一个等待者。从 Nginx 1.11.3 起,若内核支持 EPOLLEXCLUSIVE 或使用了 reuseport,
accept_mutex默认关闭,避免额外锁开销。 - 另外 worker 进程通过
events块的非阻塞事件驱动(epoll)模型并发处理海量连接,master 进程只负责信号与 worker 管理,不参与 accept。
4. 使用场景:面试考察对 Nginx 多进程模型的深层理解。答题关键:先说清"多个等待者被同时唤醒、只有一者能干活"的惊群现象,再按 accept_mutex → reuseport → EPOLLEXCLUSIVE 的演进讲 Nginx 的三种解法,并点出 reuseport 在高并发下的取舍(连接分布不均时可能倾斜)。
Q87. 如何配置 Nginx 记录 upstream 服务器的真实响应时间?
1. 是什么 :Nginx 内置了多个 upstream 相关变量,可在日志中记录"后端到底处理了多久"。核心变量有 $upstream_response_time(后端返回响应所花时间,秒,精确到毫秒)、$upstream_connect_time(与后端建立连接耗时,1.9.1+)、$upstream_addr(实际处理请求的后端地址)、$upstream_status(后端返回的状态码)。
2. 为什么用它 :$request_time 是 Nginx 收到请求到返回响应的完整耗时(含与客户端传输、等待后端的全部时间),无法区分瓶颈在前端、网络还是后端。记录 upstream 时间才能量化"后端慢"还是"网络慢/前端慢"。
3. 怎么用它 :在 http 块定义自定义日志格式,并在 access_log 中启用:
nginx
http {
log_format upstream '$remote_addr [$time_local] "$request" '
'status=$status '
'request_time=$request_time '
'upstream_connect=$upstream_connect_time '
'upstream_resp=$upstream_response_time '
'upstream_addr=$upstream_addr '
'upstream_status=$upstream_status';
server {
access_log /var/log/nginx/access.log upstream;
}
}
解读要点:
$request_time与$upstream_response_time相差大 → 慢在网络/客户端(用户带宽小、上传慢、长连接等待);- 两者几乎相等且都大 → 后端应用本身慢,去优化 SQL/接口/缓存;
$upstream_connect_time大 → 后端建连慢,查后端半连接队列溢出、负载过高;- 一个请求内部多次访问 upstream(如内部重定向)时,upstream 相关变量可能是逗号分隔的多个值(如
0.010, 0.024),按顺序对应每次后端调用; $upstream_response_time在未访问后端(如命中 Nginx 缓存)时为-。
4. 使用场景 :线上慢请求排查------用日志里 $request_time 与 $upstream_response_time 的差值定位是"后端慢"还是"网络/客户端慢";配合 $upstream_addr 还能看出负载是否倾斜到某台后端。面试要点:分清 request_time 与 upstream_response_time 的语义差异,这是核心考察点。
Q88. 如何配置 Nginx 将 HTTP 请求重定向到 HTTPS?
1. 是什么 :让所有通过 http://(80 端口)访问的请求自动跳转到 https://(443 端口),通常用 HTTP 301/302 重定向实现。
2. 为什么用它:HTTPS 已加密流量,强制跳转可避免明文访问;对正式站点用 301 永久跳转还能保证 SEO 权重集中到 https 地址。
3. 怎么用它 :推荐用 return 指令(比 rewrite 高效,不经过正则匹配):
nginx
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri; # 保留原路径与查询参数
}
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# 其余业务配置...
}
备选 rewrite 写法:
nginx
server {
listen 80;
server_name example.com;
rewrite ^(.*)$ https://$host$1 permanent;
}
要点:
$host保留用户访问的域名,$request_uri保留原路径和?查询串,避免跳转后丢失参数;301 permanent(永久)适合正式站点(利于 SEO、浏览器缓存跳转);临时活动页可用302;- 只监听 80 且只做跳转的 server 块里不要再写业务 location,避免重复处理;
- 可配合
add_header Strict-Transport-Security "max-age=31536000" always;(HSTS)强制浏览器后续直接用 https 访问。
4. 使用场景 :上线 HTTPS 时的统一跳转、迁移 https 后清理 http 入口、合规要求禁止明文传输。面试要点:首选 return 301 https://$host$request_uri;,并解释 $host/$request_uri 的作用和 301/302 的区别。
Q89. Nginx 返回 502 Bad Gateway 错误,可能的原因有哪些?如何排查?
1. 是什么:502 Bad Gateway 表示 Nginx 作为网关/反向代理,向上游(upstream 后端)发出请求后,没有收到后端的有效响应(或后端不可达)。
2. 为什么用它 :502 是反代架构下最高频的故障,多数情况问题不在 Nginx 本身而在后端。掌握原因分类与排查顺序,能快速定位是"后端挂了"还是"配置/网络/超时"问题。
3. 怎么用它(先看 error.log,再逐项排查):
bash
tail -f /var/log/nginx/error.log # 第一步:看具体错误,决定排查方向
按 error.log 报错分类定位:
connect() failed (111: Connection refused)→ 后端没启动或端口不对 :systemctl status <svc>、curl http://127.0.0.1:<端口>直连验证;检查proxy_pass的地址与端口是否写错;connect() failed (110: Connection timed out)→ 后端网络不通 :检查防火墙/安全组、路由;SELinux 拦截可用getenforce/setenforce 0临时验证;upstream timed out→ 后端处理太慢 :后端负载高、慢 SQL、线程池打满;调大proxy_connect_timeout/proxy_read_timeout/proxy_send_timeout;no live upstreams while connecting to upstream→ 后端全部不可用:健康检查全挂、后端全部宕机或 weight/backup 配置有问题;- FastCGI 场景(php-fpm 等)502:
php-fpm未启动、fastcgi_pass的 socket 路径不对或权限不足(unix:/run/php-fpm/www.sock属主不匹配); - 后端连接数打满:tomcat 线程池、MySQL 连接池耗尽,看后端自身日志与
ss -ant连接数。
4. 使用场景 :反代后端故障、发布后服务未拉起、后端接口偶发超时的线上排查。面试要点:先背结论"502 是 Nginx 没拿到后端的有效响应,先看 error.log",再按"直连后端 → 网络/防火墙 → 超时参数 → 后端资源"的顺序展开;能说出 111 Connection refused、110 Timeout、no live upstreams 三种典型报错对应的原因。
Q90. Nginx 的 access_log 和 error_log 主要关注哪些信息?
1. 是什么 :access_log 按请求记录每一次访问(流量/访问行为),error_log 记录运行过程中的错误与异常(排障入口)。两者的文件、格式、关注点完全不同。
2. 为什么用它:access_log 用于流量统计、慢请求分析、安全审计;error_log 是 Nginx 问题排查的第一现场。会读这两个日志是 Nginx 运维基本功。
3. 怎么用它:
access_log 关注的信息(核心变量):
nginx
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'$request_time $upstream_addr $upstream_response_time $http_x_forwarded_for';
access_log /var/log/nginx/access.log main;
- 来源信息 :
$remote_addr(客户端 IP)、$http_x_forwarded_for(真实客户端 IP,需上游代理透传)、$http_user_agent、$http_referer; - 请求信息 :
$request(方法 + URI)、$request_time(总耗时)、$request_length; - 响应信息 :
$status(状态码)、$body_bytes_sent(响应体大小); - 后端信息 :
$upstream_addr、$upstream_response_time、$upstream_status; - 分析用途:PV/UV、TOP IP/TOP URL、状态码分布、慢请求(按
$request_time排序)、带宽与流量估算。注意默认格式只含时间、IP、请求、状态、大小,不含耗时与 upstream 信息。
error_log 关注的信息:
nginx
error_log /var/log/nginx/error.log warn; # 级别:debug < info < notice < warn < error < crit < alert < emerg
- 日志级别 :生产通常
error或warn,调试用debug(会记录海量信息); - 常见错误 :
connect() failed(后端连不上)、upstream timed out(上游超时)、no live upstreams(后端全挂)、worker_connections are not enough(连接数不够,调大worker_connections)、too many open files(句柄耗尽,查 ulimit)、open() "..." failed (13: Permission denied)(权限问题)、recv() failed(客户端提前断开); - 每条记录含:时间、级别、worker 连接号(
*123,可与 access_log 关联)、具体错误、相关文件/行号。
4. 使用场景 :用户报"网站慢/打不开"先看 error.log 有没有报错,再做 access_log 的慢请求与状态码分析;安全事件用 access_log 排查恶意 IP/异常路径。面试要点:一句话区分------access_log 面向"请求"(流量分析),error_log 面向"错误"(故障定位),排障顺序永远是先 error_log。
Q91. 如何用 OpenResty 实现更复杂的业务逻辑?
1. 是什么 :OpenResty 是"带 LuaJIT 的 Nginx",把 Nginx 从纯转发层升级成能跑业务逻辑的应用平台。它通过 ngx_lua 模块在请求处理的各个阶段(rewrite/access/content/log)注入 Lua 脚本,直接读取/修改请求、访问 Redis/MySQL、并发请求后端,并把结果拼装返回。
2. 为什么用它:传统架构中鉴权、限流、聚合等逻辑都要跑到后端应用做,每次都要经历一次网络往返。OpenResty 把这些逻辑下沉到 Nginx 层就地执行(LuaJIT 编译执行极快),大幅降低延迟和下游压力,且支持热更新(reload 即生效)。
3. 怎么用它:
nginx
# 共享内存字典(跨 worker 共享,用于计数/缓存)
lua_shared_dict mycache 10m;
server {
location /api/order {
access_by_lua_block {
-- 1) 鉴权:校验 Token
local token = ngx.var.http_authorization
if not token or token ~= "secret" then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
-- 2) 限流:基于共享字典做简单计数(生产用 lua-resty-limit-traffic)
local d = ngx.shared.mycache
local n = d:incr(ngx.var.remote_addr, 1, 1)
if n > 100 then ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS) end
}
content_by_lua_block {
-- 3) 聚合:并发请求两个后端再拼装返回
local res1 = ngx.location.capture("/upstream/inventory")
local res2 = ngx.location.capture("/upstream/user")
ngx.say(ngx.encode_json({
inventory = cjson.decode(res1.body),
user = cjson.decode(res2.body)
}))
}
}
}
常用能力:
- 阶段钩子 :
rewrite_by_lua_block(改写请求)、access_by_lua_block(鉴权限流)、content_by_lua_block(生成响应)、log_by_lua_block(异步日志上报); - 核心 API :
ngx.var.*(读写 Nginx 变量)、ngx.req(读/改请求)、ngx.say/ngx.print(输出)、ngx.redirect(跳转)、ngx.location.capture(子请求访问内部 location)、ngx.timer.at(定时任务)、ngx.shared.DICT(worker 间共享存储); - 生态库 :
lua-resty-redis/lua-resty-mysql(直连缓存/数据库)、lua-resty-limit-traffic(限流)、lua-resty-jwt(JWT)、resty.http(对外发起 HTTP); - 典型业务:动态黑白名单、基于 Redis 的灰度/AB 分流、URL 级限流与熔断、请求聚合网关、边缘鉴权。
注意点:
- 禁止在 Lua 里做阻塞调用 (如同步
socket:connect),会卡住整个 worker;阻塞操作用 cosocket 异步模型; - 业务逻辑保持精简,复杂计算仍建议回落到后端应用;
- 排查 Lua 问题用
error_log调debug级别 +ngx.log输出。
4. 使用场景:网关型业务(鉴权/限流/灰度在边缘统一做)、API 聚合、缓存穿透防护、业务规则频繁变更又不愿重启后端的场景。面试要点:讲清"OpenResty = Nginx + LuaJIT,把逻辑放到各阶段钩子里就地执行",举一个可落地的例子(如限流或鉴权),并强调"不能阻塞 worker"这个关键约束。
Q92. 如何配置 Nginx 的缓存来加速后端动态应用?
1. 是什么 :Nginx 的 proxy_cache 反向代理缓存可以把后端返回的响应缓存到本地磁盘,下次相同请求直接由 Nginx 返回,不再打到后端,从而显著降低后端压力与响应延迟。
2. 为什么用它:动态应用(如 Java/PHP/Node 后端)处理请求开销大,而很多"动态"接口实际上内容变化不频繁。用 Nginx 做一层缓存,把重复请求拦在 Nginx 层,后端只处理真实变化的数据和缓存未命中的请求。
3. 怎么用它:
nginx
# http 块:定义缓存区(路径、目录分层、共享内存、磁盘上限、清理策略)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m
max_size=1g inactive=60m use_temp_path=off;
server {
location / {
proxy_pass http://backend;
# 开启缓存
proxy_cache mycache;
proxy_cache_key $scheme$request_method$host$request_uri; # 缓存 key
proxy_cache_valid 200 302 10m; # 200/302 缓存 10 分钟
proxy_cache_valid 404 1m; # 404 只缓存 1 分钟
proxy_cache_min_uses 1; # 至少被访问 1 次才缓存
# 精细控制:用户态/带会话 Cookie 的请求绕过缓存,避免串数据
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
add_header X-Cache-Status $upstream_cache_status; # 观察命中情况
}
}
参数与调优要点:
proxy_cache_path:levels=1:2生成两级子目录避免单目录文件过多;keys_zone是共享内存,存 key 与缓存元数据(10m约可存几万 key);max_size为磁盘上限,超限按 LRU 淘汰;inactive指定时间内没被访问则清理;proxy_cache_key:默认$scheme$proxy_host$request_uri,业务一般要加上$host、方法,按需加版本号参数避免命中旧缓存;- 缓存命中判断 :
$upstream_cache_status取值HIT(命中)/MISS(未命中)/EXPIRED(过期已回源)/BYPASS(被绕过); - 动态应用缓存策略 :只缓存 GET/HEAD;带登录 Cookie 或用户态的请求用
proxy_cache_bypass/proxy_no_cache绕过;敏感接口不缓存; - 清除缓存 :
rm -rf /var/cache/nginx/*(或按 key 删除),线上常配合"加版本号参数"实现即时失效; - 后端主动配合 :让后端返回
Cache-Control/Expires头,Nginx 按业务语义缓存,比统一写死时长更灵活。
4. 使用场景 :热点接口缓存加速、静态化页面缓存、突发流量(如秒杀)时保护后端。面试要点:能默写核心指令(proxy_cache_path、proxy_cache、proxy_cache_key、proxy_cache_valid),说清 $upstream_cache_status 的 HIT/MISS 判断方法,并强调"用户态/带 Cookie 请求要绕过缓存,避免串数据"。
Q93. 描述 DNS 递归查询和迭代查询的过程。
1. 是什么 :DNS 查询分两种交互方式:递归查询 (recursive)指 DNS 服务器替客户端"查到底"并返回最终答案;迭代查询(iterative)指 DNS 服务器只返回"下一站该问谁"的指引,由查询方自己去逐级追问。
2. 为什么用它 :理解两类查询才能看懂 dig +trace 的输出、理解根服务器/顶级域/权威服务器的分工,也才能解释"为什么改完 DNS 记录要等生效"。
3. 怎么用它 (以解析 www.example.com 为例):
递归查询(客户端 → 本地 DNS):
- 客户端把查询交给本地 DNS(如运营商
114.114.114.114),要求"你替我把最终结果查出来"; - 本地 DNS 必须返回最终的 A 记录(或 NXDOMAIN 等失败结果),不能只给个半成品;
- 优点:客户端实现简单;缺点:查询压力集中在 DNS 服务器,所以服务器会做缓存。
迭代查询(本地 DNS → 各级服务器):
本地DNS --(迭代)--> 根服务器(.) → 返回 .com 顶级域服务器地址
本地DNS --(迭代)--> .com 顶级域服务器 → 返回 example.com 权威服务器地址
本地DNS --(迭代)--> example.com 权威服务器 → 返回 www.example.com 的 A 记录
- 根服务器(全球 13 组根服务器)只回答"
.com/.cn该问谁"; - 顶级域服务器(
.com、.cn)只回答"example.com 该问谁"; - 权威服务器(NS 记录指向的服务器)才给出最终的记录;
- 每一级都支持缓存,命中即无需继续往下问。
4. 使用场景 :dig +trace www.example.com 可直观看到"根 → 顶级域 → 权威"的迭代过程;排查"域名解析慢"时用 dig @8.8.8.8 与 dig @本地 对比,判断慢在本地 DNS 还是权威侧。面试要点:一句话区分------递归是"帮你查到底",迭代是"指路让你自己去查";典型拓扑是"客户端→本地 DNS 递归,本地 DNS→根→TLD→权威 迭代"。
Q94. A记录, CNAME, MX记录, TXT记录, SRV记录 分别是什么?
1. 是什么:DNS 不只是"域名→IP",它还支持多种记录类型,每种记录承载一类映射信息。
2. 为什么用它 :配置域名解析、企业邮箱、微信/企业认证、服务发现都依赖不同记录类型。会区分它们才能正确建解析,也能看懂 dig 返回的各类记录。
3. 怎么用它:
| 记录类型 | 含义 | 格式示例 | 典型用途 |
|---|---|---|---|
| A 记录 | 域名 → IPv4 地址 | example.com A 1.2.3.4 |
最基本的网站解析(IPv4);IPv6 用 AAAA |
| CNAME 记录 | 别名 → 指向另一个域名 | www.example.com CNAME example.com |
CDN/别名接入;注意同一主机名不能同时有 A 和 CNAME |
| MX 记录 | 邮件交换记录,指定收件服务器及优先级 | example.com MX 10 mail.example.com |
邮件路由;数字越小优先级越高,多个 MX 用于冗余/负载 |
| TXT 记录 | 任意文本信息 | example.com TXT "v=spf1 include:... -all" |
SPF/DKIM/DMARC 邮件安全验证、域名所有权验证 |
| SRV 记录 | 服务定位,指明特定服务与协议的主机 + 端口 | _sip._tcp.example.com SRV 10 60 5060 sip.example.com |
服务发现(SIP、LDAP、XMPP 等);格式为 _service._proto.name + 优先级、权重、端口、目标主机 |
补充:
- 优先级/权重:MX 用"数字小优先";SRV 先按优先级(priority 小者优先),同优先级内按权重(weight 越大分配越多)负载均衡;
- 常见混淆:CNAME 的别名链最终仍解析到 A 记录;TXT 记录与邮件头无直接关系,只是认证/验证用的文本;
- 主机记录(@)表示根域名本身;
@ A与@ MX可共存,但www CNAME与www A不可共存。
4. 使用场景:新域名上线(A/CNAME)、配置企业邮箱(MX + TXT/SPF)、服务内网发现(SRV)、域名归属验证(TXT)。面试要点:每个记录说"一个词定义 + 一个示例"------A 指 IP、CNAME 指别名、MX 管邮件、TXT 放文本认证、SRV 定位服务端口,并点出 CNAME 与 A 不能共存、MX 数字小优先这两个易错点。
Q95. 如何用 dig 和 nslookup 命令查询各种记录并分析输出?
1. 是什么 :dig(Linux/BIND 自带的 DNS 查询工具)和 nslookup(Windows/Linux 均内置)都能发 DNS 查询;dig 功能更强、输出更详细,适合分析,nslookup 简单易用,适合快速查询。
2. 为什么用它 :查解析结果、验证修改是否生效、分析解析慢/失败都是高频运维操作,熟练使用这两个命令(尤其是 dig 的 +trace)是 DNS 排错的基础。
3. 怎么用它:
dig 常用用法:
bash
dig example.com # 默认查询 A 记录
dig example.com A / MX / TXT / CNAME / SRV / NS # 查指定类型
dig @8.8.8.8 example.com # 指定 DNS 服务器
dig +short example.com # 只输出答案,简洁
dig +trace example.com # 从根服务器开始迭代追踪全链路
dig -x 1.2.3.4 # 反向解析 PTR
dig example.com +noall +answer # 只显示答案区
dig 输出分析(关键区块):
text
;; QUESTION SECTION: # 查询的问题(域名 + 类型)
;example.com. IN A
;; ANSWER SECTION: # 答案:记录值 + TTL(缓存秒数)+ 类型
example.com. 300 IN A 1.2.3.4
;; AUTHORITY SECTION: # 权威服务器(NS 记录,找谁确认)
;; ADDITIONAL SECTION: # 附加信息(如 NS 对应的 A 记录)
;; Query time: 12 msec # 本次查询耗时
;; status: NOERROR # NOERROR 正常 / NXDOMAIN 域名不存在 / SERVFAIL 服务器故障 / REFUSED 拒绝
nslookup 常用用法:
bash
nslookup example.com # 默认查 A
nslookup -type=mx example.com # 查 MX
nslookup -type=ns example.com # 查 NS(权威服务器)
nslookup -type=txt example.com # 查 TXT
nslookup -type=srv _sip._tcp.example.com # 查 SRV
nslookup -type=ptr 4.3.2.1 # 反查 PTR
nslookup example.com 8.8.8.8 # 指定 DNS 服务器
# 交互模式:输入 nslookup 后 set type=mx 切换类型,再输入域名查询
分析思路:
- 对比
dig @8.8.8.8与dig @本地DNS的结果是否一致 → 判断是本地缓存/污染问题还是上游问题; status: NXDOMAIN→ 域名确实不存在;SERVFAIL→ 权威服务器有问题;NOERROR但无答案 → 该类型记录未配置;- 看 TTL 判断缓存多久生效------修改 DNS 记录后,旧记录在 TTL 内可能仍被缓存;
Query time大 → 排查权威服务器慢或本地 DNS 到上游的网络质量;- Windows 下
nslookup够用;深入分析(+trace、多服务器对比)建议用dig。
4. 使用场景 :上线前验证解析、域名到期/迁移后检查、排查"域名解析慢/解析失败"、配置邮件后验证 MX/TXT 是否生效。面试要点:会查指定类型记录、会指定 @服务器、能看懂 +trace 的链路和 NOERROR/NXDOMAIN/SERVFAIL 三者的含义。