# 运维常见题-网站维护


## 🤔 简述 HTTP/1.0、1.1、2、3 区别？	
- **`HTTP` 协议经历了四代演进，每一代解决前一代的核心痛点：连接复用、并发能力、传输效率。**
    - **`HTTP/1.0（1996，RFC 1945）`**
        - **特点**：每个请求/响应使用一个独立的 `TCP` 连接，请求完成后连接立即关闭（短连接）
        - **问题**：一次页面加载需要多个资源（`HTML`、`CSS`、`JS`、图片），每个资源都要新建 `TCP` 连接，`TCP` 握手（尤其 `HTTPS` 的 `TLS` 握手）开销巨大
        - **其他**：无 `Host` 头（同一 `IP` 上无法在 `HTTP` 层区分多个域名，即虚拟主机）；缓存控制简陋（仅有 `Expires`/`Pragma`/`Last-Modified`，无 `Cache-Control`/`ETag`）

    - **`HTTP/1.1`（`1997` 首版 `RFC 2068`，`1999` 定稿 `RFC 2616`，现为 `RFC 9110`/`9111`/`9112`）**
        - **持久连接（`Keep-Alive`）** ：默认复用同一个 `TCP` 连接发送多个请求，解决频繁握手问题
        - **`Host` 头**：支持一个 `IP` 上部署多个域名（虚拟主机）
        - **管线化（Pipelining）**：客户端可以连续发送多个请求而不必等前一个响应——但由于响应必须按请求顺序返回（`FIFO`），一个慢响应会阻塞后面的所有响应（队头阻塞），实际很少启用
        - **其他**：`chunked` 传输编码、更完善的缓存控制（`Cache-Control`、`ETag`）
        - **核心问题**：队头阻塞（`Head-of-Line Blocking`） ——同一连接上的请求必须串行处理，一个响应慢，后续全等

    - **`HTTP/2`（`2015`，`RFC 7540`，现为 `9113`）**
        - **二进制分帧**：把 `HTTP` 消息拆成二进制帧（`HEADERS`、`DATA` 等），替代 `HTTP/1.1` 的纯文本格式
        - **多路复用（`Multiplexing`）** ：多个请求/响应可以在同一个 `TCP` 连接上并行交错传输，解决了应用层的队头阻塞
        - **头部压缩（`HPACK`）** ：压缩首部（请求和响应两侧），减少重复头部传输
        - **服务器推送（`Server Push`）** ：规范层面仍保留（`RFC 9113 §8.4`），但 `Chrome 106（2022-09）`、`Firefox（2022）`已移除实现、`Safari` 从未实现，实际未普及；`HTTP/3` 无此功能，业界替代是 `103 Early Hints`（`RFC 8297`）
        - **流优先级**：`RFC 7540` 的依赖-权重方案已被 `RFC 9218`（`Extensible Prioritization`，`2022`）取代（原方案"并不成功"）
        - **局限**：虽然解决了应用层队头阻塞，但 `TCP` 层队头阻塞仍然存在——`TCP` 保证数据有序，一个 `TCP` 包丢失会导致后续所有包等待重传，即使它们属于不同的请求流

    - **`HTTP/3（2022，RFC 9114）`**
        - 传输层改用 `QUIC` 协议（`RFC 9000`，承载于 `UDP` 之上）替代 `TCP`
        - 解决 `TCP` 层队头阻塞：`QUIC` 在用户空间实现可靠传输和有序交付，但丢失的包只影响它所在的流，其他流不受影响
        - `0-RTT` 连接恢复：基于 `TLS 1.3` 会话恢复票据（`PSK`），第二次连接可免握手直接发送数据（注意 `0-RTT` 有重放风险，`RFC 9001`）
        - 连接迁移：`QUIC` 用连接 `ID` 标识连接，`IP` 地址变化（如 `Wi-Fi` 切到移动网络）连接不中断
        - 内建 `TLS 1.3`：加密是 `QUIC` 的强制部分，没有明文 `HTTP/3`
        - 头部压缩用 `QPACK`（`RFC 9204`）而非 `HPACK`（应对乱序到达的首部）
        - 部署现状：`W3Techs 2026-08` 数据显示约 `40%` 的网站已支持 `HTTP/3`

    - **四代对比速览**
        - **`1.0`**：短连接，一请求一连接
        - **`1.1`**：持久连接，但有队头阻塞
        - **`2`**：多路复用（解决应用层队头阻塞），基于 `TCP`
        - **`3`**：多路复用 + 解决传输层队头阻塞，基于 `UDP/QUIC`

- **协助记忆**
    - 一代一痛点：`1.0` 连接费（短连接）→ `1.1` 连接省（持久连接）但排队（队头阻塞）→ `2` 并行（多路复用）但堵在 `TCP` → `3` 换路（`QUIC/UDP`）彻底不堵
    - 队头阻塞有两个层面：应用层（`HTTP/1.1` 的 `FIFO`）和传输层（`TCP` 包丢失重传） ——— `HTTP/2` 解决前者，`HTTP/3` 解决后者

- **进阶思考**
    - **为什么 `HTTP/2` 多路复用后还要升级到 `HTTP/3`？**
        - `HTTP/2` 解决的是应用层队头阻塞——多个请求可以在一个 `TCP` 连接上并行，不需要按序等待。但 `TCP` 本身有传输层队头阻塞：`TCP` 保证数据按序交付，如果某一个 `TCP` 段丢失，接收方必须等待该段重传才能继续处理后续数据，即使这些数据属于完全不同的请求流。在丢包率高的弱网环境（移动网络）这个问题尤其严重。`HTTP/3` 用 `QUIC`（`UDP`）解决了这个问题——每个流独立处理，一个流的丢包不影响其他流

    - **`HTTP/3` 用了 `UDP`，可靠性和顺序性怎么保证？**
        - `QUIC` 在 `UDP` 之上自己实现了可靠传输和有序交付——它有类似 `TCP` 的序号、确认、重传机制，但作用域是"流"而不是整个连接。每个流独立编号、独立确认、独立重传，所以一个流的丢包只重传该流的数据，不影响其他流。这也是 `QUIC` 解决队头阻塞的本质：把 `TCP` 的"全局有序"变成 `QUIC` 的"流内有序"

## 🤔 HTTP 有哪些常见的状态码？
- **[`HTTP` 状态码](/posts/linux/http响应码/)是服务器在响应中返回的三位数字，用于告知客户端请求的处理结果。按首位数字分为五类：`1xx`（信息）、`2xx`（成功）、`3xx`（重定向）、`4xx`（客户端错误）、`5xx`（服务器错误）。**

    - **`1xx`**：信息性响应
        - **`100 Continue`**：客户端可以继续发送请求体
        - **`101 Switching Protocols`**：协议切换（如升级到 `WebSocket`）
        - **`103 Early Hints`**：提前发送提示（如预加载资源链接），配合 `301/308` 重定向场景做性能优化
        - 日常运维很少直接见到 `1xx`，多由协议层内部处理

    - **`2xx`**：成功
        - **`200 OK`**：请求成功，最常见
        - **`201 Created`**：资源创建成功（常用于 `POST` 创建资源）
        - **`202 Accepted`**：请求已接受但尚未处理完成（异步任务）
        - **`204 No Content`**：请求成功但无内容返回（常用于 `DELETE`、以及 `POST/PUT` 的"成功无内容"场景）

    - **`3xx`**：重定向
        - **`301 Moved Permanently`**：永久重定向，后续请求应使用新 `URL`（默认允许缓存，受 `Cache-Control` 控制）
        - **`302 Found`**：临时重定向，后续请求仍用原 `URL`
        - **`303 See Other`**：方法强制改为 `GET`（`SHOULD use GET`，`RFC 9110 §15.4.4`）——常用于表单提交后重定向到结果页
        - **`304 Not Modified`**：资源未修改，可使用缓存（配合 `If-Modified-Since` / `ETag`）
        - **`307 Temporary Redirect`**：临时重定向，且保留请求方法和请求体
        - **`308 Permanent Redirect`**：永久重定向，且保留请求方法和请求体
        - **方法语义完整图谱**：`301/302`（`MAY` 改方法）→ `303`（`SHOULD` 用 `GET`）→ `307/308`（必须保留方法）

    - **`4xx`**：客户端错误
        - **`400 Bad Request`**：请求语法错误，服务器无法或不愿处理
        - **`401 Unauthorized`**：未认证（未登录或凭据无效）。注意：`401` 是"没证明你是谁"，`403` 是"不让进"
        - **`403 Forbidden`**：服务器理解请求但拒绝处理/授权——常因权限不足，但也可能是未认证、`IP` 封禁、`WAF` 拦截；未认证时服务器也可能返回 `403` 而非 `401`（如不想暴露资源存在性）
        - **`404 Not Found`**：资源不存在，最常见
        - **`405 Method Not Allowed`**：请求方法不被允许（如对只读接口发 `POST`）
        - **`408 Request Timeout`**：请求超时
        - **`409 Conflict`**：请求与当前资源状态冲突（如版本冲突）
        - **`410 Gone`**：资源已永久删除（区别于 `404` 的"不存在"，`410` 是"曾经存在但没了"）
        - **`413 Content Too Large`**：请求体过大（`RFC 9110` 新名，旧名 `Payload Too Large` 仍广泛使用）
        - **`429 Too Many Requests`**：请求过多，触发限流（`RFC 6585` 定义，常配合 `Retry-After` 头）

    - **`5xx`**：服务器错误
        - **`500 Internal Server Error`**：服务器内部错误，通用错误码
        - **`501 Not Implemented`**：服务器不支持请求的方法
        - **`502 Bad Gateway`**：网关/代理收到上游服务器的无效响应（`Nginx` 作为反向代理时常见）
        - **`503 Service Unavailable`**：服务暂时不可用（过载、维护中），常配合 `Retry-After` 头，通常过一段时间会恢复
        - **`504 Gateway Timeout`**：网关/代理等待上游响应超时

- **协助记忆**
    - **五位分类**：`1` 信、`2` 成、`3` 转、`4` 客户错、`5` 服务器错
    - **易混三对**：
        - `301` vs `308`：都是永久，但 `301` 方法可能变（POST→GET），`308` 保留方法
        - `302` vs `307`：都是临时，`302` 方法可能变，`307` 保留方法
        - `401` vs `403``：401` 是"你是谁"，`403` 是"不让进"（注：`403` 不要求已认证，是"拒绝授权"）

    - `502` vs `503` vs `504`：`502` 是上游答了但答错（无效响应）、`503` 是自己没空（过载）、`504` 是上游没答（超时）

- **进阶思考**
    - **`301` 和 `308` 到底有什么区别？什么时候用哪个？**
        - 核心区别是请求方法是否保留。`301`（和 `302`）在重定向时，很多浏览器和客户端会把 `POST` 请求改为 `GET` 请求（`RFC` 使用 `MAY`，历史行为普遍存在）；`303` 明确要求改用 `GET`；`308`（和 `307`）明确要求保留原方法和请求体。实践中：永久迁移且希望客户端用新 `URL` 重新发起 `GET` 请求，用 `301`；`API` 场景希望 `POST/PUT` 的请求体和方法原样转发到新地址，用 `308`。现代 `Web` 的 `API` 重定向推荐 `308/307` 以保证语义一致

    - **反向代理场景，`502`、`503`、`504` 分别对应什么排查方向？**
        - **`502（Bad Gateway）`**：上游服务器响应了但响应无效——如上游进程崩溃返回空响应、返回了非法响应格式。排查上游应用是否挂了、端口是否监听。
        - **`503（Service Unavailable）`**：本机（代理）或上游过载/维护——如后端连接数打满、限流触发。排查后端负载、连接池。
        - **`504（Gateway Timeout）`**：上游没有及时响应——如后端处理超时、慢查询。排查后端应用延迟、数据库慢查询。
        - **一句话**：`502` 是上游答了但答错，`503` 是没空， `504` 是没答
    


## 🤔 浏览器访问域名经历了哪些流程？	
- **从在地址栏输入域名到页面显示，浏览器经历了一条完整的链路：`URL` 解析 → `DNS` 解析 → `TCP` 连接 → `TLS` 握手（`HTTPS`）→ `HTTP` 请求 → 服务器处理 → `HTTP` 响应 → 浏览器渲染。每一步都有对应的可排查点。**

    - **第一步**：`URL` 解析
        - 浏览器解析输入的 `URL`，拆分成协议（`https`）、域名（`www.example.com`）、端口（默认 `443`）、路径（`/index.html`）、查询参数等
        - 判断协议是否合法、域名格式是否正确

    - **第二步**：`DNS` 解析（把域名变成 `IP`）
        - 按缓存层级逐级查找：浏览器缓存 → 系统解析器（`hosts` 文件、系统缓存、`DNS` 服务器的相对顺序由操作系统决定 ——— `Windows` 通常是 `hosts` → `DNS Client` 缓存 → `DNS` 服务器，`Linux` 按 `nsswitch.conf` 默认 `files dns` 通常 `hosts` 优先）
        - 客户端向递归 `DNS` 服务器发起递归查询；递归解析器对根 `DNS` → 顶级域 `DNS`（`.com`）→ 权威 `DNS`（`example.com` 的 `NS`）的逐级查询称为迭代查询（浏览器本身不逐级问根服务器）
        - 细节：现代浏览器和系统可能启用 `DNS-over-HTTPS`（`DoH`，`RFC 8484`），`DNS` 查询走 `HTTPS` 加密通道

    - **第三步**：`TCP` 连接（三次握手）
        - 拿到 `IP` 后，浏览器与服务器建立 `TCP` 连接：`SYN` → `SYN-ACK` → `ACK`（`RFC 9293`）
        - 涉及 `TCP` 相关优化：`Fast Open`（`TFO`）、连接复用（`keep-alive`）；若启用 `HTTP/3`，`TCP+TLS` 合并为 `QUIC` 一次握手

    - **第四步**：`TLS` 握手（`HTTPS` 专属）
        - **`HTTPS` 下进行 `TLS` 握手（以 `TLS 1.3` 为主，现行规范 `RFC 9846`）：**
            - 客户端发送 `ClientHello`（支持的加密套件、`TLS` 版本、`key_share` 密钥共享）
            - 服务器返回 `ServerHello`（含密钥共享）、证书；证书在加密消息中发送（`EncryptedExtensions` → `Certificate` → `CertificateVerify` → `Finished`）
            - 客户端验证证书（信任链、域名匹配、有效期）
            - 双方确认，握手完成，开始加密通信

        - 注：`TLS 1.2` 流程是"独立的密钥交换回合（`ECDHE` 等）"，`TLS 1.3` 把密钥共享直接放进 `ClientHello`/`ServerHello`，更快更简洁
        - `TLS 1.3` 首次握手 `1-RTT`、会话恢复 `0-RTT`（注意 `0-RTT` 有重放攻击风险，规范要求默认禁用，浏览器默认不开 `early data`）

    - **第五步**：发送 `HTTP` 请求
        - 浏览器构造 `HTTP` 请求（方法、`URL`、头部、`cookie`），通过已建立的加密通道发送
        - 经过 `CDN` 时，请求先到最近的 `CDN` 节点，`CDN` 命中缓存则直接返回，未命中则回源

    - **第六步**：服务器处理
        - 请求到达服务器（或反向代理 `Nginx`），经过：负载均衡 → `Web` 服务器 → 应用代码 → 数据库/缓存查询
        - 服务器生成响应（状态码、响应头、响应体）

    - **第七步**：`HTTP` 响应返回
        - **服务器返回响应**：状态码（`200`/`404`/`500` 等）、响应头（`Content-Type`、`Cache-Control`、`Set-Cookie` 等）、响应体（`HTML`）
        - **浏览器判断状态码**：`2xx` 正常渲染，`3xx` 跟随重定向（重新走流程），`4xx/5xx` 显示错误页

    - **第八步**：浏览器渲染
        - 解析 `HTML` 构建 `DOM` 树
        - 解析 `CSS` 构建 `CSSOM`
        - `DOM` + `CSSOM` 合并生成渲染树（`Render Tree`） （注：`Render Tree` 是 `WebKit/Blink` 的实现术语，非统一 `Web` 标准定义，`Gecko` 叫 `Frame tree`）
        - **布局（Layout/Reflow）** ：计算每个元素的几何位置
        - **绘制（Paint）** ：绘制到屏幕
        - **合成（Composite）** ：把各层合并呈现 ——— `transform/opacity` 动画只触发合成，不触发重排重绘
        - **脚本与样式对渲染的影响**：`<script>` 是 `parser-blocking`（阻塞 `DOM` 解析进而阻塞渲染，除非 `async/defer`；`<script type="module">` 默认 `defer`），但现代浏览器有预加载扫描器提前并行下载脚本（下载并行、执行阻塞）；`CSS` 是 `render-blocking` 但不阻塞 `DOM` 构建
        - `JavaScript` 执行修改 `DOM/CSSOM` 会触发重排（`Reflow`）和重绘（`Repaint`）

- **协助记忆**
    - **八步链路**：拆 `URL` → 查 `DNS` → 连 `TCP` → 握 `TLS` → 发请求 → 服务器处理 → 收响应 → 渲染页面
    - **"三握一握"**：`TCP` 三次握手 + `TLS` 一次握手（`HTTPS` 专属）
    - **渲染五步**：`DOM`（结构）→ `CSSOM`（样式）→ `Render Tree`（合并）→ `Layout` + `Paint`（画出来）→ `Composite`（合成呈现）
    - **排查口诀**：访问慢，先看 `DNS` 快不快，再看 `TCP/TLS` 握多久，再看服务器响应时间，最后看渲染卡不卡

- **进阶思考**
    - **为什么 `HTTPS` 站点首次访问比 `HTTP` 慢？慢在哪？**
        - `HTTP` 只需 `TCP` 三次握手（`1` 个 `RTT`）；`HTTPS` 除了 `TCP` 还要 `TLS` 握手 ——— `TLS 1.2` 需要 `2` 个 `RTT`、`TLS 1.3` 需要 `1` 个 `RTT`（首次），加上 `TCP` 总共 `2~3` 个 `RTT` 才能发出第一个请求。`RTT` 越大（物理距离越远），差距越明显。优化手段：`TLS 1.3` 会话恢复（`0-RTT`，需权衡重放风险）、`HTTP/3`（`QUIC` 把 `TCP+TLS` 合并为一次握手）、连接复用与 `TFO`。在 `HTTP/3` 或连接复用场景下 `HTTPS` 与 `HTTP` 差距已不明显，但全新连接下 `HTTPS` 仍多约 `1` 个 `RTT`

    - **输入域名后浏览器直接显示"无法访问此网站"，最可能是哪一步出了问题？**
        - 按经验上最常见的顺序：一是 `DNS` 解析失败（域名拼错、`DNS` 服务器故障、本地 `hosts` 被改）——浏览器提示"找不到服务器 `IP` 地址"；二是 `TCP` 连接失败（服务器宕机、端口不通、防火墙拦截）——— 提示"连接超时"或"连接被拒绝"；三是 `TLS` 握手失败（证书过期、不受信任）——提示"您的连接不是私密连接"；四是服务器返回 `5xx` 错误（有响应但处理失败）。浏览器通常给出具体错误提示，根据提示即可定位到是哪一步


## 🤔 HTTP 请求头和响应头有哪些内容？
- **`HTTP` 头部（`Header`）是请求/响应中"键值对"形式的元数据，用于传递请求上下文、内容描述、缓存策略、认证信息等。现代分类（`RFC 9110`）不再使用传统的"通用头/实体头"分类，头部按用途分为请求头、响应头、表示头、内容头等；本文按常见场景组织，仍会说明新旧分类对应关系。**
    - **请求行 / 状态行结构（头部之前）**
        - **请求行**：`GET /index.html HTTP/1.1`（方法 + 路径 + 版本）
        - **状态行**：`HTTP/1.1 200 OK`（版本 + 状态码 + 原因短语）

    - **请求头（客户端 → 服务器）**
        - **`Host`**：目标主机和端口（`HTTP/1.1` 必需，虚拟主机依据）
        - **`User-Agent`**：客户端标识（浏览器/版本/操作系统）
        - **`Accept`**：客户端可接受的内容类型（如 `text/html`、`application/json`）
        - **`Accept-Encoding`**：客户端支持的压缩算法（`gzip`、`br`、`zstd` —— `2024-2025` 主流浏览器已支持）
        - **`Accept-Language`**：客户端语言偏好
        - **`Content-Type / Content-Length`**：请求体类型和长度（`POST`/`PUT` 时；注意这属于"表示头/内容头"，同一字段在请求和响应中含义一致）
        - **`Authorization`**：认证凭据（`Bearer token`、`Basic`）
        - **`Cookie`**：客户端携带的 `cookie`（`RFC 6265` 定义，注意不是 `RFC 9110`）
        - **`Referer`**：来源页面 `URL`（注意拼写是 `Referer` 而非 `Referrer`）
        - **`Origin`**：跨域请求的来源（协议+域名+端口，`CORS` 用）
        - **`X-Forwarded-For`**：经过代理时的原始客户端 `IP`（非标准头，事实标准；标准替代为 `Forwarded`，`RFC 7239`）

    - **响应头（服务器 → 客户端）**
        - **`Content-Type / Content-Length`**：响应体类型和长度
        - **`Set-Cookie`**：服务器设置 cookie（RFC 6265 定义，含 Secure、HttpOnly、SameSite 等属性）
        - **`Location`**：重定向目标地址（3xx 响应；也用于 201 Created 返回新资源地址）
        - **`Cache-Control`**：响应缓存策略（RFC 9111 定义）
        - **`ETag`**：资源版本标识（配合 If-None-Match 缓存验证；也配合 If-Match 做乐观并发控制）
        - **`Last-Modified`**：资源最后修改时间（配合 If-Modified-Since）
        - **`Expires`**：缓存过期时间（HTTP/1.0 遗留，被 Cache-Control 取代）
        - **`Server`**：服务器软件信息
        - **`Date`**：响应时间
        - **`Access-Control-Allow-Origin`**：CORS 允许的跨域来源
        - **`Retry-After`**：多久后重试（配合 429/503）
        - **`Strict-Transport-Security`**：强制 HTTPS（HSTS，RFC 6797）

    - **传统分类与新分类的对应（旧文档常见，帮助理解）**
        - 传统四分类：通用头、请求头、响应头、实体头
        - `RFC 9110` 已同时废弃"通用头"和"实体头"概念，重构为：请求头、响应头、表示头（Representation，描述资源表示，如 Content-Type、ETag、Last-Modified）、内容头（Content，描述消息内容，如 Content-Length）、验证器字段（Validator，ETag/Last-Modified 单独归为验证器）
        - 旧教材把 ETag 归入实体头是简化说法，严格按 RFC 2616 ETag 是响应头

    - **常见安全响应头（现代 Web 标配）**
        - **`CSP（Content-Security-Policy）`** ：内容安全策略，限制资源加载来源
        - **`X-Content-Type-Options: nosniff`**：禁止 MIME 嗅探
        - **`X-Frame-Options / frame-ancestors`**：点击劫持防护
        - **`Referrer-Policy`**：控制 Referer 发送策略
        - **`Permissions-Policy`**：限制浏览器功能权限
        - **`COOP/COEP/CORP`**：跨源隔离相关头

- **协助记忆**
    - **请求头记一组**：`Host`、`UA`、`Accept` 家族、`Cookie`、`Authorization`——"你是谁、要什么、带什么"
    - **响应头记一组**：`Content-Type`、`Set-Cookie`、`Location`、`Cache-Control`、`ETag` ——— "给你什么、让你存什么、跳去哪"
    - **排查缓存问题看这四个**：`Cache-Control`、`ETag`、`Last-Modified`、`Expires`
    - **区分两个"内容"**：`Content-Type`（媒体类型）≠ `Content-Length`（字节长度）

- **进阶思考**
    - **`Cookie` 和 `Authorization` 都能传递身份信息，有什么区别？**
        - 机制不同。`Authorization` 是请求头，每次请求由客户端代码显式携带（如 `Bearer token`），无状态、常用于 `API`。`Cookie` 是浏览器自动管理的状态机制——服务器 `Set-Cookie` 后，浏览器自动在后续请求中带上对应域名的 `cookie`，有状态、常用于 `Web` 会话。`Cookie` 会自动附加到同域请求（不受代码控制），`Authorization` 需要代码显式添加。安全上：`Authorization` 适合 `API token`，`Cookie` 需配合 `Secure`/`HttpOnly`/`SameSite` 防窃取

    - **`Nginx` 反代场景，如何保证后端能看到真实客户端 `IP`？**
        - `Nginx` 在转发请求时把客户端真实 `IP` 写入 `X-Forwarded-For` 或 `X-Real-IP` 头，后端应用从这些头读取。但 `X-Forwarded-For` 是可伪造的——客户端可以直接构造这个头。安全做法：在 `Nginx` 层用 `set_real_ip_from` + `real_ip_header` 处理，或在信任的代理边界重写 `X-Forwarded-For`（不要直接信任客户端传入的值）。这是反代场景的常见安全坑


## 🤔 网站显示中文乱码会是什么原因？	
- **中文乱码的根本原因是编码不一致——网页的字节是按 `A` 编码写入的，但浏览器用 `B` 编码解读，导致字节序列被错误解码。排查方向就是找出"写入编码"和"读取编码"哪里对不上。**

- **编码机制速览（先理解再排查）**
    - **UTF-8**：现代标准，兼容 `ASCII`，中文字符占 `3` 字节，全栈默认。`2025-2026` 年 `UTF-8` 已占全网超 `98%`，且 `Encoding` 标准已把 `UTF-8` 定为新协议的强制编码
    - **`GBK / GB2312`**：中文传统编码，中文字符占 `2` 字节，旧系统常见（注意 `GBK` 是 `GB2312` 的超集，现行国标是 `GB18030`）
    - **乱码的本质**：同一串字节，用不同编码解读得到不同字符

- **常见原因一**：`HTML` 文件内部声明与实际编码不符
    - `HTML` 文件实际以 `UTF-8` 保存，但 `<meta charset="GBK">` 声明为 `GBK`（或反过来）——浏览器按声明解码，乱码
    - 注意 `file` 命令的局限：`file` 能可靠识别 `UTF-8`/`UTF-16`（靠 `BOM` 和字节特征），但对 `GBK` 等东亚双字节编码通常只报含糊的 "`ISO-8859 text`" 或 "`data`"。排查 `GBK` 更可靠的方式：用 `iconv -f GBK -t UTF-8` 文件 和 `iconv -f UTF-8 -t GBK` 文件 双向试转，看哪个方向能转出正常文字；或用 `chardet` / `uchardet` 检测
    - **修复**：统一文件保存编码和 `meta` 声明

- **常见原因二**：`HTTP` 响应头 `charset` 与 `meta` 声明冲突
    - `HTTP` 响应头 `Content-Type: text/html; charset=UTF-8` 与 `HTML` 内 `meta` 声明不一致时，`HTTP` 头的优先级更高
    - **排查**：`curl -I <url>` 查看响应头 `charset`；`curl -s <url> | head -20` 查看 `meta` 声明
    - **修复**：让两者一致（改 `Nginx`/应用配置或改 `meta`）
    - **补充**：还有更高优先级的 `BOM` ——— 文件开头的 `UTF-8 BOM` 优先级高于包括 `HTTP` 头在内的一切声明。这是"改了 `meta` 还是乱码"的另一个隐蔽原因
    - `meta` 声明必须位于文件前 `1024` 字节内才生效

- **常见原因三**：数据库存储编码与连接编码不一致
    - 数据库表是 `UTF-8`，但应用连接数据库时用了错误的连接字符集（如 `latin1`），导致"存进去是乱码"或"读出来是乱码"
    - **机制（`MySQL`）**：服务器按 `character_set_client` 解释客户端发来的字节，再转 `character_set_connection`，最后转目标列字符集；读出的结果由 `character_set_results` 决定
    - **排查**：`SHOW VARIABLES LIKE 'character_set%'` 查看各字符集配置
    - **修复**：连接时 `SET NAMES utf8mb4`（同时设置 `client`/`connection`/`results` 三个变量）；建库时 `CREATE DATABASE ... DEFAULT CHARACTER SET utf8mb4`
    - 注：`MySQL 8.0+` 默认即 `utf8mb4`，旧的 `utf8`（`utf8mb3`）已废弃、未来大版本移除

- **常见原因四**：编码被二次转换
    - 数据经过多个环节时，某个环节做了一次错误的编码转换
    - 是否可逆取决于原始字节是否被改写：纯显示层乱码（字节仍是原始 `UTF-8`，只是被按 `GBK` 显示）可逆 ——— 著名例证是"锟斤拷" （`UTF-8` 替换符被按 `GBK` 显示）；只有经真实转码（如 `iconv` 转错后落库）才不可逆

- **常见原因五**：页面完全没有编码声明
    - `HTTP` 头无 `charset`、`HTML` 内也无 `meta` 声明时，浏览器按默认（现代浏览器默认 `UTF-8`）尝试解码，非 `UTF-8` 字节就会乱码
    - 这比"用户改了浏览器设置"常见得多——排查时先确认页面是否有编码声明，而不是去查浏览器设置

- **协助记忆**
    - 一句话：乱码 = 写的时候用 `A` 编码，读的时候用 `B` 编码，对不上
    - 读取侧优先级（从高到低） ：`HTTP` 头 `charset` → `BOM` → `meta` 声明 → 默认 `UTF-8`
    - 写入侧检查：文件实际编码（iconv 双向试转）、数据库/连接字符集
    - 排查命令：`curl -I` 看响应头、`iconv -f X -t Y` 文件 试转换、`chardet` 检测
    - `utf8mb4` 记住：`MySQL` 存 `emoji` 必须用 `utf8mb4` 而非 `utf8`

- **进阶思考**
    - **`HTTP` 响应头 `charset` 和 `HTML meta charset` 哪个生效？**
        - `HTTP` 响应头的 `Content-Type: text/html; charset=xxx` 优先级更高——浏览器先看 `HTTP` 头，如果 `HTTP` 头没有明确 `charset`，才看 `HTML` 内的 `meta` 声明。但注意 `BOM` 优先级最高：文件开头的 `UTF-8 BOM` 会覆盖包括 `HTTP` 头在内的一切声明。所以"改了 `meta` 还是乱码"要查两个地方：`HTTP` 头里的旧 `charset`、文件是否带 `BOM`

    - **数据库已经是 `UTF-8`，为什么页面还乱码？**
        - 存储编码和连接编码是两回事。表字符集是 `UTF-8` 只保证数据以 `UTF-8` 存，但写入时服务器按 `character_set_client`（连接时声明的客户端字符集）解释应用发来的字节——如果连接用的还是 `latin1` 或 `GBK`，写入时就错了。`MySQL` 排查：`character_set_server`（服务器默认）、`character_set_database`（当前库）、`character_set_client`/`connection`/`results`（连接链路）。统一 `SET NAMES utf8mb4` + 建库指定 `utf8mb4` 通常能解决

## 🤔 网站访问很慢，该如何排查？
- **网站访问慢的排查要遵循"分层定位"思路：把一次访问拆成多个阶段，先定位慢在哪一层（DNS？网络？服务器？数据库？），再针对该层深入。不要一上来就盯着服务器 CPU——很多"网站慢"其实是网络或前端问题。**
    - **第一步**：先量化"慢"在哪——用 `curl` 计时
        - `curl -o /dev/null -s -w 'DNS: %{time_namelookup}s\nTCP: %{time_connect}s\nTLS: %{time_appconnect}s\nTTFB:%{time_starttransfer}s\nTotal: %{time_total}s\n' https://example.com`
        - **重要**：这些变量是"从 `curl` 启动到该阶段完成的累计时间"，不是各阶段本身耗时。要算阶段耗时需做差：
            - `DNS` 解析耗时 = `time_namelookup`
            - `TCP` 握手耗时 = `time_connect` − `time_namelookup`
            - `TLS` 握手耗时 = `time_appconnect` − `time_connect`
            - 服务器处理时间 = `time_starttransfer` − `time_appconnect`（近似）

        - **`TTFB（time_starttransfer）`** ：从 `curl` 启动到收到第一个字节的累计时间，包含 `DNS` 查询 + `TCP` 握手 + `TLS` 握手 + 服务器处理（`MDN` 定义）
        - **判断方法**：`DNS` 差值长 → `DNS` 问题；`TCP` 差值长 → 网络问题；`DNS`/`TCP`/`TLS` 各阶段差值都正常但 `TTFB` 仍长 → 服务器处理慢；`Total` 长但 `TTFB` 正常 → 响应体传输慢（带宽/大文件）

    - **第二步**：按阶段定位
        - **`DNS` 慢**：`dig + time = 2 example.com` 看解析耗时，`dig @8.8.8.8 example.com` 对比公共 `DNS`；检查本地 `DNS` 服务器、递归解析器、上游权威、`DNSSEC`；考虑 `DoH/DoT`。注意：智能 `DNS/CDN` 解决的是"解析到哪个节点"的调度准确性，不直接加速解析耗时本身
        - **网络慢**：`ping` 看 `RTT`，`mtr` 看中间链路是否有丢包或高延迟节点；跨地域访问考虑 `CDN`。`2025` 新坑：`HTTP/3` 走 `UDP`，部分网络对 `UDP` 做 `QoS` 限速/防火墙拦截——如果 `time_connect` 长但 `ping` 正常，可怀疑 `UDP` 被限速
        - **服务器响应慢（`TTFB` 高）** ：进入服务器端排查（第三步）
        - **传输慢（`Total` − `TTFB` 长）** ：响应体太大、带宽不足、压缩未开（`gzip`/`brotli`）、大图片未优化

    - **第三步**：服务器端深入
        - **Nginx 层**：
            - 先确认日志格式包含耗时字段：默认 `combined` 格式没有 `$request_time`，需要在 `log_format` 中显式配置，如 `log_format main '$remote_addr $request_time $upstream_response_time';，然后 awk '{print $2}' access.log | sort -n | tail  -20` 看最慢的请求（注意取的是配置里的耗时字段列）
            - 检查反向代理超时配置（`proxy_read_timeout` 等）、`upstream` 是否有慢节点

        - **应用层**：
            - 看应用日志的请求处理时间，定位是哪个接口慢
            - 检查应用是否有死锁、线程池打满、`GC` 频繁（`Java`）、`GIL` 阻塞（`Python`）

        - **数据库层**：
            - 开慢查询日志，看是否有全表扫描、缺索引的 `SQL`
            - `SHOW PROCESSLIST` 看是否有长时间运行的查询
            - 检查数据库连接池是否打满（连接等待导致 `TTFB` 升高）

        - **缓存层**：
            - Redis/缓存是否生效（缓存命中率低会导致每次都打数据库）
            - 缓存穿透/击穿/雪崩

        - **资源层**：
            - `top/free` 看 `CPU`、内存是否打满
            - `iostat` 看磁盘 `IO` 是否瓶颈
            - 连接堆积用 `ss -s`（汇总统计）或 `ss -ant state established | wc -l` 看连接数（注意 `ss -lntp` 只显示监听中的 `socket`，看不到 `ESTABLISHED` 堆积；`-lntp` 中 `Send-Q` 列可看 `backlog` 溢出）

    - **第四步**：前端/浏览器侧（如果服务器很快但用户觉得慢）
        - **`Chrome DevTools` 的 `Network` 面板**：看每个资源的加载时间，`Waterfall`（瀑布图）能看出哪些资源是瓶颈
        - **`Core Web Vitals`（`2025` 年核心指标）** ：`LCP`（最大内容绘制）、`INP`（交互延迟，`2024` 年取代 `FID`）、`CLS`（布局偏移）——用 `Lighthouse` 或 `RUM`（`CrUX` 字段数据）审计
        - **常见前端问题**：页面引用了太多大资源、图片未优化、未用 `CDN`、缓存策略未配置
        - **注意**：`HTTP/2` 时代"`JS/CSS` 合并文件"已不必要（多路复用下合并反而有害），现代实践是 `bundling` + `code splitting` + `tree-shaking` + 按需加载
        - **补充**：`103 Early Hints` 可提前 `TTFB`、`Server-Timing` 响应头可暴露服务端耗时

- **协助记忆**
    - **分层定位口诀**：先 `curl` 计时做差分层，再按层深挖 ——— `DNS` 慢查解析，`TCP` 慢查网络，`TTFB` 慢查服务器，`Total` 慢查传输
    - **`curl` 变量是累计值，算阶段要"做差"**：`TCP` = `connect` − `namelookup`，`TLS` = `appconnect` − `connect`，服务器 = `starttransfer` − `appconnect`
    - 排查顺序：网络层 → 服务器层 → 数据库层 → 缓存层 → 前端层，别跳过

- **进阶思考**
    - **`TTFB` 高但服务器 `CPU`/内存都正常，可能是什么原因？**
        - `TTFB` 包含网络 `RTT` + 服务器处理。如果 `CPU`/内存正常，可能的隐藏原因：一是数据库慢查询——请求卡在等数据库返回，但数据库 `CPU` 不高（如缺索引全表扫描、锁等待）；二是外部依赖慢——应用调第三方 `API`，等外部响应；三是连接池/线程池打满——请求排队等待可用连接；四是磁盘 `IO`——日志或缓存写盘慢。排查：看应用日志确认请求在等什么，必要时用 `APM`（链路追踪）定位耗时分布

    - **用户分布广（全国/全球），如何区分是"网站慢"还是"用户网络慢"？**
        - 用多点拨测——在不同地域的机器上执行同样的 `curl` 计时（云厂商都有多地拨测工具）。如果所有地域 `TTFB` 都高 → 服务器端问题；如果只有某些地域高 → 网络/`CDN` 节点问题（该地域到服务器链路差，或 `CDN` 在该地域节点少）。这是"网站慢"与"网络慢"的经典区分方法


## 🤔 QPS、TPS、PV、UV 是什么？怎么统计？	
- **`QPS`/`TPS` 是实时性能指标（衡量系统处理能力），`PV`/`UV` 是业务流量指标（衡量用户访问量）。两者维度不同，但常放在一起讨论。**

- **`QPS`（`Queries Per Second`，每秒查询数）**
    - **含义**：系统每秒处理的请求/查询数量，衡量系统吞吐能力
    - **注意**：`QPS` 在不同语境含义不同 ——— `Web` 场景指每秒 `HTTP` 请求数（严格说这是 `RPS`，`Requests Per Second`），数据库场景指每秒 `SQL` 查询数。中文语境常混用，面试和讨论中要明确语境
    - **统计方法**：
        - **监控系统**：`Prometheus` + `nginx-prometheus-exporter`（从 `nginx stub_status` 取数，`nginx_http_requests_total` 速率为 `QPS`）或应用埋点
        - **`Nginx access log`**：按时间窗口统计日志条数
        - **压测工具**：`ab`、`wrk`、`JMeter` 直接报 `QPS`

    - **相关概念**：峰值 `QPS`（扩容依据，行业更常用最大 `5` 分钟窗口均值或 `TP99`，而非秒级瞬时毛刺值）vs 平均 `QPS`（容量规划参考）

- **`TPS`（`Transactions Per Second`，每秒事务数）**
    - **含义**：系统每秒处理的事务数量。事务是业务层面的完整操作，一个事务可能包含多个请求/查询
    - **典型**：一次下单 = 一个事务，但可能包含"查库存 + 扣款 + 生成订单 + 通知"等多个请求
    - 通常 `TPS` ≤ `QPS`（一个事务由多个请求组成时），但取决于事务定义与接口粒度——批量/批处理接口（一个 `HTTP` 请求处理 `N` 笔转账）会出现 `TPS` > `QPS`
    - **统计方法**：应用埋点（在事务完成点打点）、`APM` 工具
    - **适用**：电商下单、支付、银行转账等有明确"事务边界"的业务

- **`PV`（`Page View`，页面浏览量）**
    - **含义**：页面被浏览的次数。每次刷新/打开页面都算一次 `PV`，同一用户重复浏览累加
    - **统计方法**：
        - **`Nginx access log`**：统计 `HTML` 页面请求数（需过滤静态资源、排除非 `2xx`、过滤爬虫）
        - 前端埋点 / RUM（真实用户监测）

    - **爬虫污染**：`2025` 年 `AI` 爬虫（`GPTBot`、`ClaudeBot` 等）激增，严重污染 `PV`/`UV` 统计，行业标准做法是 `UA` 白名单过滤
    - 注意 `PV` ↔ `QPS` 不能直接换算：`1` 次 `PV`（页面加载）通常产生 `1` 个 `HTML` + 数个静态资源/接口请求（`1 PV` ≈ `5~20` 个请求）；容量规划估算：峰值 `QPS` ≈（日均 `PV` × 页面平均请求数 × 峰值系数）/ `86400`

- **`UV`（`Unique Visitor`，独立访客数）**
    - **含义**：去重后的独立访客数，同一用户只算一次
    - **统计方法（去重口径，不同口径结果不同）**：
        - **基于 `Cookie` / 第一方标识**：给访客分配唯一 `ID` 去重。`2025` 年隐私环境下的主流已演变为"第一方 `Cookie` + 登录 `ID` + 设备指纹"的混合识别（`GA4` 等基于 `first-party` 标识去重）——— 因为第三方 `Cookie` 在 `Safari`/`Firefox` 默认拦截、`GDPR`/个保法限制下受限（`Chrome 2024` 年放弃强制淘汰第三方 `Cookie`、`2025` 年终止 `Privacy Sandbox` 计划）
        - **基于 `IP`**：按 `IP` 去重（不准确 —— `NAT` 后多人同 `IP` 算 `1` 个；同一人换 `IP` 算多个）
        - **基于用户登录 `ID`**：最准确，但只能统计登录用户

    - 注意：`UV` 是"人"的维度，`PV` 是"次"的维度。`PV/UV` 比值 = 人均浏览页数 ——— 不能简单解读为"内容吸引"，比值高也可能因导航差反复点击、长文分页；需结合跳出率、停留时长解读

- **四个指标的维度对比**
    - **`QPS`**：系统处理能力（实时、按请求数）
    - **`TPS`**：业务事务处理能力（实时、按事务数）
    - **`PV`**：页面访问量（累计、按次数）
    - **`UV`**：独立访客数（累计、按人去重，与 `QPS` 无换算关系）

- **协助记忆**
    - `QPS` 是"每秒能扛多少请求"，`TPS` 是"每秒能完成多少业务"，`PV` 是"被看了多少次"，`UV` 是"有多少人来看"
    - `QPS/TPS` 看系统强不强，`PV/UV` 看业务火不火
    - `PV/UV` 比值：人均看几页，需结合跳出率解读

- **进阶思考**
    - **如何从 `Nginx access log` 统计 `QPS` 和 `PV`？**
        - `combined` 格式时间戳如 `10/Feb/2025:14:23:45`，`$4` 是完整时间戳。按分钟统计：`awk '{print $4}' access.log | cut -d: -f2-3 | sort | uniq -c`（必须 `sort` 再 `uniq`，多 `worker` 写日志时同分钟行不连续，不 `sort` 会重复计数）；按秒看峰值：`cut -d: -f2-4 | sort | uniq -c | sort -rn | head`。统计 `PV`：过滤静态资源 + 非 `2xx` + 爬虫 ——— `grep -E '\.(html|htm)'` 在 `URL` 带 `query string` 或动态路由站点会失效，更可靠的是按 `URL` 类型/路径前缀区分或用 `GoAccess`、`ELK` 等分析工具。`QPS` 更推荐用监控系统（`nginx-prometheus-exporter`）而非日志

    - **`UV` 统计中"基于 `Cookie`"和"基于 `IP`"哪个更准确？为什么行业多用 `Cookie`？**
        - 基于 `Cookie` 更接近真实人数，是行业主流——因为 `IP` 在 `NAT` 场景下多人共享一个 `IP`（会被算成 `1` 个 `UV`），而同一用户在不同网络下 `IP` 会变（会被算成多个 `UV`），`IP` 口径误差大。`Cookie` 的问题是用户清浏览器缓存会重新计数（略微高估）、禁 `Cookie` 则无法统计。`2025` 年隐私环境下的主流是"第一方 `Cookie` + 登录 `ID` + 设备指纹"混合识别（`GA4` 基于 `first-party` 标识去重），第三方 `Cookie` 因 `Safari/Firefox` 拦截和法规限制已不可依赖


- 扩展信息
    - **·**
        - `Apache HTTP Server` 自带的基准测试工具，单进程模型（`-c` 靠 `fork` 子进程，非线程），无 `HTTP/2`
        - 官方自认"未完整实现 `HTTP/1.x`"、解析脆弱——定位是快速冒烟测试（"给你当前 `Apache` 安装表现的一个印象"），不适合严谨压测
        - 适用：随手测一下吞吐量。`2025` 年已属过时，正式压测选下面这些

    - **`wrk` / `wrk2`**
        - `wrk`：多线程 + `epoll/kqueue` 事件驱动，单颗多核 `CPU` 即可产生高负载，`LuaJIT` 脚本定制请求
        - 维护状态：长期未实质更新（`wrk` 停留在 `2014` 年前后）；`wrk2` 是事实上的标准改进 `fork` ——— 恒定吞吐（`-R` 参数）+ `HdrHistogram` 精确延迟记录，修复"协调遗漏（`Coordinated Omission`）"，可报 `99.9999%` 分位延迟
        - 适用：命令行快速吞吐/延迟测试；要正确的高分位延迟用 `wrk2`

    - **`JMeter`**
        - `Apache` 开源、纯 `Java` 的全功能负载测试工具，从 `Web` 扩展到 `JDBC`/`JMS`/`FTP`/`LDAP`/`TCP` 等十余种协议
        - 图形化 `Test IDE` + `CLI` 无头模式 + 动态 `HTML` 报告，`Groovy/JSR223` 脚本化，高度可扩展
        - 注意：官方明确"`JMeter is not a browser`" ——— 协议层工作，不执行 `JS`
        - 适用：功能/负载测试一体化、多协议、团队 `GUI` 协作（重量级）

    - **现代压测工具（2025 年主流）**
        - **`k6`（最活跃）**：`Go` 内核 + `JS` 脚本"`tests as code`"，`HTTP/WebSocket/gRPC/Browser`，阈值/`SLO`、`CI` 集成，`2025` 年事实上的新一代主流
        - **`Locust`（活跃，`Microsoft` 赞助）**：纯 `Python` 写场景，`gevent` 协程 + `Web UI` + 分布式，适合高并发用户模拟
        - **`Vegeta`**：`Go`，恒定速率 + `UNIX` 组合式 `CLI` + `Go` 库，内置 `Prometheus exporter`
        - **`hey`**：`Go` 单文件，自称 "`ApacheBench (ab) replacement`"，`HTTP/2`、限速、`CSV` 输出，小而够用
        - **`oha`（活跃）**：`Rust` + `tokio`，实时 `TUI`，`HTTP/2/3`（实验）、`burst`、`--latency-correction`（同样修复协调遗漏）
        - **高分位延迟的正确性**：`wrk2` / `Vegeta` / `oha` 都处理协调遗漏，`ab` 和 `wrk` 不处理

    - **`APM`（`Application Performance Monitoring`，应用性能监控）**
      - **核心能力四件套**：链路追踪（跨服务请求流、瓶颈、根因、依赖分析）、性能剖析（方法级 `CPU`/内存 `profiling`）、错误监控（异常捕获、聚合、告警）、指标 + 仪表盘 + 告警
      - **`Jaeger`**：`CNCF` 毕业项目，纯分布式追踪平台，已发 `v2`、原生拥抱 `OpenTelemetry`（`OTLP`、`ClickHouse` 存储后端） ——— 轻量自托管选它
      - **`SkyWalking` / `Pinpoint`**：`Java agent` 无侵入字节码注入，覆盖 `trace` + 指标 + 日志 + `profiling` 的全栈 `APM` ——— 全栈自托管选它们
      - **`Datadog APM` / `New Relic`**：商业 `SaaS`，自动埋点 + 分布式追踪 + 错误监控 + 持续剖析 + `SLO`/告警的托管全栈——省事省运维选它们


    一句话选型：快速冒烟测试用 `ab`；命令行脚本化压测用 `wrk`/`wrk2`、`hey`；`CI` 常态化压测首选 `k6`；`Python` 团队用 `Locust`；多协议/团队协作用 `JMeter`；自托管链路追踪用 `Jaeger`；全栈 `APM` 自托管用 `SkyWalking`。

## 🤔 简述 HTTP 和 HTTPS 区别？
- **`HTTP` 和 `HTTPS` 的核心区别：`HTTPS` 是在 `HTTP` 与 `TCP` 之间插入了一层 `TLS`（旧称 `SSL`）加密层，保证传输安全。`HTTP` 是明文传输，`HTTPS` 是加密传输。**

    - **`HTTP（HyperText Transfer Protocol）`**
        - **明文传输**：请求和响应内容（`URL`、请求头、`Cookie`、表单数据、响应体）在网络中以明文传输，可被中间人抓包直接读取
        - **默认端口 `80`**
        - **无身份验证**：无法确认你连的服务器就是目标服务器（可能被 `DNS` 劫持/中间人冒充）
        - **无认证性完整性保护**：数据可能被中间人篡改而不被发现（`TCP` 校验和只能发现传输错误，防不了恶意篡改）
        - **适用**：非敏感场景。如今内网也推荐 `HTTPS`（零信任趋势）

    - **`HTTPS（HTTP over TLS）`**
        - **加密传输**：在 `HTTP` 和 `TCP` 之间加了一层 `TLS`（`Transport Layer Security`，旧称 `SSL`） ，数据加密后传输
        - **默认端口 `443`**
        - **提供三方面安全能力**：
            - **机密性**：内容加密，中间人抓包只能看到密文（但 `SNI` 域名与流量元数据仍可见，这正是 `ECH` 要解决的——见下文）
            - **完整性**：`AEAD` 认证加密（`TLS 1.2` 及以前用 `MAC`）保证数据未被篡改
            - **身份认证**：通过服务器证书验证服务器身份，防止中间人冒充

        - **证书机制**：服务器需要部署 `CA`（证书颁发机构）签发的证书，包含公钥和服务器身份信息，客户端验证证书的信任链

    - **`HTTPS` 的建立流程（简版）**
        - `TCP` 三次握手 → `TLS` 握手（协商加密套件、交换密钥、验证证书）→ 加密的 `HTTP` 通信
        - `TLS 1.3` 首次完整握手 `1-RTT`；`PSK` 会话恢复通常仍为 `1-RTT`；开启 `early data`（`0-RTT`）可零往返发送幂等请求，但有重放风险、无前向保密，规范要求默认不启用（部分 `CDN` 对 `QUIC 0-RTT` 会开启）

    - **性能差异**
        - `HTTPS` 比 `HTTP` 多一次 `TLS` 握手开销（首次连接多 `1~2` 个 `RTT`：`TLS 1.3` 为 `1-RTT`、`TLS 1.2` 为 `2-RTT`）
        - 现代优化（`TLS 1.3`、会话恢复、`HTTP/3` + `QUIC 0-RTT`）已让差异很小

    - **应用现状（截至 2026 年）**
        - `HTTPS` 已是绝对主流：主流浏览器对 `HTTP` 站点显示"不安全"警告，搜索引擎降权 `HTTP` 站点；`TLS 1.3` 自 `2018` 发布以来已广泛部署，`TLS 1.2` 是主要回退版本
        - 免费证书普及：`Let's Encrypt` 提供免费自动化证书（`certbot`），`HTTPS` 部署成本几乎为零
        - `HSTS` 强制浏览器只走 `HTTPS` ——— 注意其生效前提是首次请求已走 `HTTPS`（或加入 `preload list`），否则第一个请求仍可能被 `SSL-strip` 降级
        - `ECH`（`Encrypted Client Hello`，`2026` 年 `3` 月标准化为 `RFC 9849`） ：加密整个 `ClientHello`（含 `SNI` 域名），配合 `DoH` 防域名泄露——解决"抓包只见密文但域名仍可见"的最后一块短板。`Chrome/Firefox` 已默认启用，`OpenSSL 4.0`、`nginx` 已支持
        - `HTTP/3` 基于 `QUIC`，本身就要求 `TLS 1.3` 加密 ——— 现代 `Web` 已无"明文 `HTTP/3`"这回事

- **协助记忆**
    - **一句话**：`HTTPS` = `HTTP` + `TLS` 加密层，明文变密文
    - **三个安全能力**：机密性（加密）、完整性（防篡改）、身份认证（防冒充）
    - **端口记忆**：`80` 明文，`443` 加密
    - **类比**：`HTTP` 是明信片（谁都能看内容），`HTTPS` 是密封信（只有收发双方能看）

- **进阶思考**
    - **`HTTPS` 能防止所有攻击吗？**
        - 不能。`HTTPS` 保护的是传输过程，不保护端点本身 ——— `Web` 应用漏洞（`SQL` 注入、`XSS`）发生在应用层，`HTTPS` 管不了；钓鱼网站本身就用 `HTTPS`（有合法证书），`HTTPS` 只证明"你是连到了证书对应的服务器"，不证明"这个服务器是可信的"。`HTTPS` 解决的只是"传输中不被偷看、篡改、冒充"

    - **为什么 `HTTP/3` 没有明文版本？**
      - `HTTP/3` 基于 `QUIC`，`QUIC` 在设计上强制内置 `TLS 1.3`（加密是协议的一部分，不是可选项）。这和 `HTTP/2` 不同 ——— `HTTP/2` 虽然规范支持 `h2c`（明文），但浏览器从未实现明文 `HTTP/2`。所以到了 `HTTP/3` 时代，明文 `HTTP` 事实上只剩 `HTTP/1.1` 还在用

## 🤔 Session 共享是什么？有哪些实现方式？	
- **`Session` 是服务器端保存的用户会话状态（登录状态、购物车、临时数据）。单机部署时 `Session` 存在本机内存即可；多实例部署时，用户的请求可能落到不同的服务器——如果 `Session` 只存在某台服务器上，落到其他服务器的请求就"不认识"这个用户了。`Session` 共享就是把 `Session` 从单台服务器内存中拿出来，让集群中所有服务器都能访问同一份会话状态。**

    - **为什么需要 `Session` 共享**
        - **单机时代**：`Session` 存在本机内存，一个进程处理所有请求，天然可用
        - **集群时代**：多台服务器 + 负载均衡，请求会分散到不同机器，`Session` 存在 `A` 机器，请求落到 `B` 机器就丢失登录状态
        - **水平扩展**：缩容掉持有 `Session` 的那台机器，用户就被登出

    - **实现方式一**：`Session` 复制（已过时，不推荐）
        - 各服务器之间互相复制 `Session`。`Tomcat` 集群两种实现：`DeltaManager`（`all-to-all`，每台机器保存全量 `Session`）和 `BackupManager`（只复制到一台备份节点，主备模式）
        - **细节**：成员发现/心跳走 `multicast`（默认 `228.0.0.4:45564`），会话数据复制走 `TCP` ——— 大集群下 `all-to-all TCP` 复制 + `multicast` 心跳的网络开销剧增
        - 已基本被淘汰，适合小集群

    - **实现方式二**：`Session Sticky`（粘性会话，治标不治本）
        - 负载均衡器把同一个用户的请求始终转发到同一台服务器（基于 `IP hash`、`Cookie` 或 `URL` 参数）
        - 优点：实现简单，`Session` 仍可存在单台机器内存
        - 缺点：只是"绕开"了问题而不是解决——某台服务器宕机，粘在上面的用户全部掉线；扩容/缩容会破坏 `hash` 映射（用一致性哈希可大幅减少重映射，如 `Nginx hash ... consistent`）；无法做到真正的负载均衡（热点用户集中在一台机器）
        - 定位：不推荐作为唯一会话方案（云原生场景如 `K8s ingress`/`ALB` 中 `sticky` 仍是常规实践，但通常与集中共享叠加使用）

    - **实现方式三**：集中式 `Session` 存储（主流方案）
        - 把 `Session` 从各服务器内存中抽出来，存到一个集中存储（`Redis` / `Memcached`）
        - 所有服务器从同一个地方读写 `Session`，天然共享
        - `Redis` 方案（最常用）：支持 `TTL` 过期，正好匹配 `Session` 的过期机制；读写快、支持集群
        - 框架支持：`Java` 的 `Spring Session`（支持 `Redis`/`JDBC`，切换不改应用代码）、`Hazelcast`/`Infinispan`（社区扩展）、云托管 `Redis`
        - 优点：服务器无状态、支持水平扩展、单台服务器宕机不影响其他机器
        - 注意点：`Redis` 是新的单点——本身需要高可用（主从、哨兵、集群）；`Session` 是热数据，`Redis` 内存开销需要考虑

    - **实现方式四**：客户端存储 / 无状态 `Token`（趋势方案）
        - **不把会话状态存在服务器，而是放到客户端**：
            - **`JWT（JSON Web Token）`** ：签名后的 `token` 存在客户端（`localStorage`/`Cookie`），服务器验证签名即可，无需存储 `Session`。注意"无法主动失效"是简化说法——严格可用黑名单/`jti`/版本号实现服务端撤销，但代价是重新引入状态
            - 签名/加密 `Cookie` 直接存会话数据（如 `Rails cookie_store`、`Flask`、`Django signed_cookies`）：注意这是"消除共享需求"而非"共享"——数据在 `Cookie` 里服务端已无状态，不应再叫 `Session`；受 `Cookie` 约 `4KB` 大小限制

        - 优点：服务器完全无状态、天然支持分布式、不需要额外 `Session` 存储
        - 缺点：`JWT` 主动失效难、`token` 泄露后有攻击窗口、体积比 `Session ID` 大
        - 适用：`API`/前后端分离场景主流

    - **`2026` 年演进补充**
        - `OIDC/SSO`（企业认证标准，基于 `JWT`）成为多应用统一登录的主流方案
        - `Passkeys`/`WebAuthn`（无密码认证）兴起，减少对传统 `Session`/`Cookie` 的依赖
        - 第三方 `Cookie` 逐步淘汰对依赖 `Cookie` 的 `Session`/`Sticky` 方案有冲击；`SameSite` 默认 `Lax` 影响跨站 `Cookie` 会话

    - **方案对比速览**
        - **`Session` 复制**：已淘汰（同步开销大）
        - **`Sticky`**：临时过渡（宕机掉线、不均衡）
        - **`Redis` 集中存储**：主流（服务器无状态、支持扩展）
        - **`JWT` 无状态**：趋势（完全无状态、但难失效）

- **协助记忆**
    - **`Session` 共享的本质**：把"存在单台机器内存里的会话"搬到"所有机器都能访问的地方"
    - **一条主线**：`Session` 存储位置从"服务器内存"→"集中存储（`Redis`）"→"客户端（`JWT`）"，越来越无状态
    - **选型**：传统 `Web` 应用用 `Redis` 集中存储（`Spring Session`）；前后端分离/`API` 用 `JWT`

- **进阶思考**
    - **`Redis` 存 `Session` 和 `JWT` 怎么选？**
        - 看业务需求。需要主动失效（登出、踢人、封禁）、需要服务端可控（管理在线用户）、传统服务端渲染应用 → `Redis` 存 `Session`（服务端可随时删 `Session`）；需要跨域/多端无缝（小程序、`App`、`Web` 共享登录）、追求服务端零存储、`API` 场景 → `JWT`。混合方案也常见：`JWT` 做认证 + `Redis` 做登出黑名单

    - **`Session Sticky` 和 `Session` 共享是二选一吗？**
        - 不是，`Sticky` 是"让请求总去同一台机器"来规避共享问题，`Session` 共享是"让所有机器共享同一份状态"。两者可以叠加：生产常见做法是 `Sticky` 保性能 + 集中共享存储保故障转移——正常时请求粘在本地减少跨节点访问，节点宕机时共享存储兜底。但只依赖 `Sticky` 的方案（宕机全掉线）不可接受

## 🤔 Tomcat 8005、8009、8080 端口分别作用是什么？
- **`Tomcat` 的三个经典端口各有分工：`8005` 是 `Shutdown`（关闭）端口、`8009` 是 `AJP` 端口（与 `Apache httpd` 集成）、`8080` 是 `HTTP` 端口（对外服务）。都在 `conf/server.xml` 中配置。**

    - **`8005`**：`Shutdown`（关闭）端口
        - **作用**：用于关闭 `Tomcat` 的端口。向该端口发送特定的 `SHUTDOWN` 命令字符串，`Tomcat` 会优雅关闭。它只接受一个固定的 `SHUTDOWN` 字符串，没有其他管理能力（真正的管理机制是 `JMX`，默认随机端口）
        - **配置**：`<Server port="8005" shutdown="SHUTDOWN">`，关闭命令字符串默认为 `SHUTDOWN`
        - **现状**：`8005` 在 `Tomcat 9/10/11` 的默认 `server.xml` 中始终启用，且默认只监听 `localhost（127.0.0.1）`。"禁用"是社区加固建议，不是版本默认行为
        - **安全注意**：如果配置不当绑定到 `0.0.0.0` 或防火墙未限制，任何人连接 `8005` 发 `SHUTDOWN` 就能关掉 `Tomcat`（`DoS`）
        - **加固方式（注意别把 `Tomcat` 停不掉）**：
            - 确保监听 `127.0.0.1`、修改 `shutdown` 字符串
            - 用 `port="-1"` 完全禁用该端口 ——— 但注意 `catalina.sh stop` 默认正是通过 `8005` 发 `SHUTDOWN`，禁用后必须：设置 ·（此时 `catalina.sh` 走 `kill` 路径）、或用 `jsvc` / `Apache Commons Daemon` 停止，否则 `Tomcat` 无法优雅停止

    - **`8009`**：`AJP` 端口（与 `Apache httpd` 集成）
        - **作用**：`AJP`（`Apache JServ Protocol`）连接器端口，用于 `Tomcat` 与前端 `Apache httpd` 之间的通信（通过 `mod_jk` 或 `mod_proxy_ajp`）。注意 `Nginx` 不支持 `AJP` ——— `Nginx` 与 `Tomcat` 集成应走 `HTTP（proxy_pass）`
        - **场景**：`Apache httpd` 作为前端静态服务器 + `Tomcat` 处理动态请求，两者通过 `AJP` 通信
        - **配置**：`<Connector port="8009" protocol="AJP/1.3" />` ——— `8.5+` 默认 `server.xml` 中 `8009` 已注释（需手动启用，默认示例 `address="::1"`）
        - **`Ghostcat` 漏洞（`CVE-2020-1938`）** ：`AJP` 端口未限制时可读取 `webapps` 下任意文件甚至执行代码。修复版（`9.0.31`/`8.5.51+`）起默认要求 `secret`（`secretRequired` 默认 `true`）且默认只监听 `loopback`
        - **现状**：`AJP` 未被官方废弃（`Tomcat 10/11` 仍支持），但主要存在于历史遗留架构（`Apache httpd` + `Tomcat` 集成）；与 `HTTP` 相比对 `HTTP/2` 等现代特性支持不足

    - **`8080`**：`HTTP` 连接器端口（对外服务）
        - **作用**：`Tomcat` 对外提供 `HTTP` 服务的默认端口
        - **配置**：`<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />`
        - **`redirectPort="8443"`**：当应用配置了 `SSL` 要求（`web.xml` 中 `security-constraint` 且 `transport-guarantee=CONFIDENTIAL`）时，`HTTP` 请求自动重定向到 `8443`（`HTTPS` 端口）
        - **默认 `8080` 而非 `80`**：`Unix` 下 `1024` 以下端口需 `root` 权限而 `Tomcat` 默认非 `root` 运行（`Windows` 无此限制），兼为避免与既有 `Web` 服务冲突

    - **`Tomcat 10/11` 的变化（`2020` 年后）**
        - **最大变化**：`javax.servlet` → `jakarta.servlet` 包名迁移（`Tomcat 10` 起），老应用需改包名才能运行
        - **三个端口的机制与默认配置不变**：`8005`/`8080` 默认启用、`8009` 默认注释

- **协助记忆**
    - 三个端口一句话：`8005` 关 `Tomcat`（管理）、`8009` 连 `Apache`（`AJP` 集成）、`8080` 给用户（`HTTP` 服务）
    - `8080` → `8443` 重定向：`HTTP` 强制跳 `HTTPS` 时用（`redirectPort`）
    - 安全三查：`8005` 别绑外网（禁用前先配 `CATALINA_PID`）、`8009` 别裸奔（`Ghostcat`）、`8080` 按需开放

- 进阶思考
    - **`8005` 端口关闭命令的安全性如何保障？**
        - `8005` 的 `SHUTDOWN` 默认字符串是固定的 `SHUTDOWN`，一旦端口可达任何人都能关掉 `Tomcat`。保障手段：确保监听 `127.0.0.1`（默认）；防火墙限制本机访问；修改 `shutdown` 字符串；最彻底是 `port="-1"` 禁用 ——— 但禁用后必须设置 `CATALINA_PID` 或改用 `jsvc`，否则 `catalina.sh stop` 无法优雅停止（它默认走 `8005` 发 `SHUTDOWN`）

    - **为什么现在很多架构不用 `8009 AJP` 了？**
        - 一是 `Ghostcat` 漏洞（`CVE-2020-1938`）暴露了安全风险；二是现代前端架构变了 ——— `Nginx` 已成为主流反向代理，直接用 `proxy_pass` 走 `HTTP（8080）`就能完成转发，而且 `Nginx` 根本不支持 `AJP`；三是 `AJP` 对 `HTTP/2` 等现代特性支持不足。`AJP` 的优势（减少解析开销）在现代硬件上可忽略。所以 `AJP` 主要存在于 `Apache httpd` + `Tomcat` 的历史架构中

## 🤔 Tomcat 如何性能优化？	
- **`Tomcat` 性能优化要分层进行：`JVM` 参数 → 连接器（`Connector`）→ 应用层 → 操作系统层。核心原则是"先找准瓶颈再优化"，不要盲目调大参数。**

    - **第一层**：`JVM` 参数优化
        - **堆内存**：设置 `-Xms`（初始堆）和 `-Xmx`（最大堆）为相同值，避免运行时动态扩缩堆造成性能抖动。参考值：`-Xms2g` `-Xmx2g`（按实际内存调整；容器场景下 `JDK 10+` 默认启用 `UseContainerSupport`，`-Xmx` 受容器配额约束）
        - **元空间**：`-XX:MaxMetaspaceSize` 设置上限，防止元空间无限增长
        - **`GC` 选择**：`JDK 9+` 默认 `G1`，适合大堆（`4GB+`）；超大堆（几十 `GB`）追求低延迟用 `ZGC`（`JDK 11` 实验、`15` 生产可用）
        - **`GC` 日志**：`-Xlog:gc*` 启用 `GC` 日志，用于排查 `GC` 停顿
        - **常见误区**：盲目调大 `-Xmx` 不一定提升性能，堆过大反而增加 `GC` 停顿

    - **第二层**：连接器（`Connector`）优化
        - **连接器模式**：`Tomcat 8.5/9` 默认 `NIO`（装了 `tomcat-native` 时自动用 `APR`）；`Tomcat 11` 起默认纯 `Java NIO`。`BIO` 在 `8.5` 已移除
        - **线程池参数（`Executor`）**：
            - **`maxThreads`**：最大工作线程数，默认 `200`。不是越大越好——线程过多导致上下文切换开销增大；建议结合压测确定，一般 `200~500`
            - **`minSpareThreads`**：保底存活线程数（`Connector` 属性默认 `10`，`Executor` 上默认 `25`）；注意是"保底水位"，线程按需增长到该水位，并非启动即全部创建
            - **`acceptCount`**：等待队列长度，默认 `100`。高并发时调大（如 `500~1000`）
            - **`maxConnections`**：最大连接数，`NIO` 默认 `8192`

        - **连接超时**：`connectionTimeout` 文档默认 `60000ms`，但随 `Tomcat` 发行的默认 `server.xml` 显式配置为 `20000ms` ——— 如果自定义 `server.xml` 未写该属性，实际生效的是 `60000ms`；`keepAliveTimeout` 控制长连接存活时间
        - **压缩**：`compression="on"` 开启 `gzip` 压缩，减少传输量（对文本类资源效果明显）
        - **静态资源**：配置资源缓存或交给前端 `Nginx` 处理（推荐）

    - **第三层**：应用层优化
        - **数据库连接池**：配置合理的连接池参数（初始/最大连接数），避免频繁创建销毁连接
        - **缓存**：热点数据用 `Redis`/本地缓存，减少数据库压力
        - **异步处理**：长耗时操作用 `Servlet 3.1` 异步处理，释放线程
        - **避免阻塞**：`IO` 操作异步化，减少线程占用

    - **第四层**：操作系统层
        - **文件描述符**：调大 `ulimit -n`（默认 `1024` 太小，`Tomcat` 高并发会报 `too many open files`）
        - **内核参数**：`net.core.somaxconn`（配合 `acceptCount`）、`net.ipv4.tcp_*` 相关优化

- **协助记忆**
    - **优化四层**：`JVM`（堆 + `GC`）→ 连接器（线程池 + 超时）→ 应用（连接池 + 缓存）→ 系统（fd + 内核）
    - **核心原则**：先压测找瓶颈，再对症下药；盲目调大参数反而更糟
    - **三个默认值**：`maxThreads 200`、`acceptCount 100`、`maxConnections 8192（NIO）`

- **进阶思考**
    - **`Tomcat` 线程数（`maxThreads`）是不是越大越好？**
        - 不是。线程是"资源"而非"能力"——每个线程占用栈内存，线程过多导致内存占用大、`CPU` 上下文切换开销大（频繁切换反而降低吞吐）。正确做法：用压测工具（`JMeter`/`k6`）逐步加压，观察吞吐量拐点，在吞吐不再增长的点设置 `maxThreads`。通常 `200~500` 是常见范围，但具体要压测确定。还要配合 `maxConnections` 和 `acceptCount` 一起看——三者共同决定了并发处理能力

    - **`Tomcat 10/11` 有哪些值得关注的性能相关变化？**
        - `Tomcat 10` 最大变化是 `jakarta` 命名空间迁移（`javax→jakarta`）；`Tomcat 11` 对应 `Jakarta EE 11`。真正值得关注的是虚拟线程（`Virtual Threads`） ——— `Tomcat 10.1` 和 `11` 都支持（通过 `StandardVirtualThreadExecutor`，需 `JDK 21+`，且默认都不启用、需在 `server.xml` 显式配置）。注意概念澄清：`Tomcat NIO` 自 `6.0` 起就不是"`1` 线程 `1` 连接" ——— `acceptor` + `poller` 线程负责连接，工作线程池处理的是请求而非连接；虚拟线程改变的是"处理请求的工作线程"的实现（平台线程→虚拟线程），让每个请求跑在 `KB` 级栈的虚拟线程上，突破 `maxThreads=200` 的平台线程上限。仅对 `IO` 密集型（阻塞占比高）应用收益明显；`CPU` 密集型无增益；注意 `JDK 21` 上 `synchronized` 会固定（`pin`）虚拟线程（`JDK 24` 的 `JEP 491` 才解决）、`ThreadLocal` 规模放大问题


## 🤔 简述 LVS 的三种模式及其工作原理？
- **`LVS`（`Linux Virtual Server`）是 `Linux` 内核内置的负载均衡方案，工作在内核空间（第四层，基于 `IP` + 端口），性能高。`LVS` 有三种工作模式：`NAT`、`DR`（`Direct Routing`）、`TUN`（`IP Tunneling`），核心区别在于数据报文如何流转、响应流量是否经过调度器。**

    - **模式一**：`NAT` 模式（`VS`/`NAT`）
        - **原理**：调度器同时改写请求和响应的 `IP` 地址——请求进来做 `DNAT`（目标地址改为 `RS`(`Real Server`; 真实服务器)），响应方向做逆 `NAT`（源地址从 `RS` 改回 `VIP`，基于连接跟踪的双向改写）
        - **报文流转**：请求 → 调度器 → `RS`；响应 → 调度器 → 客户端。请求和响应都经过调度器
        - **特点**：
            - **优点**：`RS` 可以使用任意操作系统和私网 `IP`，配置简单；是唯一支持端口映射的模式（`VIP` 端口可与 `RS` 端口不同）
            - **缺点**：调度器是瓶颈——所有响应都经过它，吞吐受限于网卡带宽 + 连接表/`CPU`（每个 `NAT` 连接在哈希表占两个节点）
            - `RS` 的网关必须指向调度器

        - **适用**：`RS` 数量少、流量不是特别大的场景

    - **模式二**：`DR` 模式（`VS`/`DR`，直接路由）
        - **原理**：调度器只改写数据帧的目标 `MAC` 地址（源 `MAC` 也会改写）把请求转发给 `RS`；`RS` 处理完直接把响应绕过调度器返回客户端
        - **报文流转**：请求 → 调度器 → `RS`；响应 → `RS` → 客户端（不经过调度器）
        - **关键配置**：`VIP` 同时配置在调度器和所有 `RS` 上 ——— `RS` 的 `VIP` 配置在 `lo` 接口上，并抑制 `ARP` 响应（`arp_ignore=1`、`arp_announce=2`）
        - **特点**：
            - **优点**：响应不经过调度器，调度器只处理请求流量，性能高，生产环境最常用
            - **缺点**：`RS` 和调度器必须在同一二层网络（同网段）；`RS` 端口必须与 `VIP` 端口相同；`RS` 需额外配置 `VIP` 和 `ARP` 抑制
            - **适用**：大规模、高流量场景（`Web` 集群主流）


    - **模式三**：`TUN` 模式（`VS`/`TUN`，`IP` 隧道）
        - **原理**：调度器把请求封装在 `IP` 隧道（`IP-in-IP`，也支持 `GRE`/`SIT`/`GUE`）中转发给 `RS`；`RS` 解封装后处理，响应直接返回客户端
        - **报文流转**：请求 → 调度器（封装）→ `RS`（解封装）→ 处理 → 响应 → 客户端（不经过调度器）
        - **关键配置（和 `DR` 一样）** ：`VIP` 配置在 `RS` 的非 `ARP` 设备（`tunl0`/`dummy`/`lo`）上，同样要抑制 `ARP` 响应 ——— 否则 `RS` 会抢答 `ARP`
        - **`MTU` 坑（`TUN` 实战第一坑）** ：`IPIP` 每包 `+20` 字节头，`MTU 1500` 网络中 `DF`（不分片）大包会被拒（`PMTU` 问题）；内核可配置 `pmtu_disc` 关闭让 `TUN` 方法分片
        - **特点**：
        - **优点**：`RS` 可以跨地域（不在同一网络），响应不经过调度器
        - **缺点**：需 `RS` 支持隧道协议（`modprobe ipip`、`tunl0 up`）；封装带来开销和 `MTU` 问题；配置复杂度高
        - **适用**：异地多活、`RS` 分布在多个机房的场景


    - **三种模式对比速览**
        - **`NAT`**：请求和响应都过调度器（调度器瓶颈），唯一支持端口映射
        - **`DR`**：请求过调度器，响应直接回（同二层网络，最常用），`RS` 端口必须等于 `VIP` 端口
        - **`TUN`**：请求隧道封装，响应直接回（可跨地域），`RS` 端口必须等于 `VIP` 端口

    - **2026 年生态现状**
        - `ipvs` 至今在内核 `6.x` 中活跃维护；`kube-proxy` 的 `IPVS` 模式是 `K8s` 大规模集群的常用方案
        - 演进方向：`DPVS`（`DPDK` 版 `ipvs`）、`Katran`/`Cilium`（`XDP`/`eBPF` 负载均衡）、`ECMP+BGP` 替代 `Keepalived/VRRP`

- **协助记忆**
    - **一句话区分**：`NAT` 是"快递员两头跑"（进出发都经调度器），`DR` 是"只送件不取件"（请求经调度器、响应直达），`TUN` 是"打包寄送"（隧道封装跨地域）
    - **`DR` 两个关键**：`VIP` 放 `lo` 接口 + 抑制 `ARP`（`TUN` 同样要）
    - **选型口诀**：同网段高流量用 `DR`，跨地域用 `TUN`，简单场景用 `NAT`

- **进阶思考**
    - **`DR` 模式为什么要抑制 `RS` 的 `ARP` 响应？不抑制会怎样？**
        - `VIP` 同时配置在调度器和所有 `RS` 上。如果 `RS` 不抑制 `ARP`，局域网设备对 `VIP` 发起 `ARP` 请求时所有 `RS` 都会响应（`ARP` 是广播的），导致 `MAC` 地址混乱——有的请求被转发到 `RS`，有的被 `RS` 直接抢答。抑制 `ARP`（`arp_ignore=1`、`arp_announce=2`）让只有调度器响应 `VIP` 的 `ARP`，`RS` 的 `VIP` 只用于接收转发请求，不对外宣告

    - **`LVS` 和 `Nginx` 负载均衡怎么选？**
        - `LVS` 工作在四层（`IP` + 端口），内核态，性能高（并发连接数可达百万级，注意是并发连接数而非 `QPS`）；但只做转发，本身不带健康检查和故障摘除，需 `Keepalived` 等外部组件配合。`Nginx` 工作在七层（`HTTP`），能做 `URL` 路由、重写、限流、缓存，但含 `TLS` 终止/`HTTP` 解析，开销天然更大（可比对象是 `Nginx stream` 四层模块）。生产架构常见组合：`LVS`（四层入口）→ `Nginx`（七层反向代理）→ 应用服务器，高可用由 `Keepalived` 实现 ——— `VRRP` 做 `VIP` 漂移 + 对 `RS` 健康检查并动态增删 `ipvs` 规则

## 🤔 LVS 支持哪些调度算法？	
- **`LVS`（`ipvs`）支持的调度算法分为静态和动态两大类：静态算法不考虑后端实时负载，动态算法根据后端当前负载/连接数做决策。截至 `2026` 年，`ipvs` 共有 `14` 个调度算法（`RR`/`WRR`/`DH`/`SH`/`MH` + `LC`/`WLC`/`SED`/`NQ`/`LBLC`/`LBLCR`/`FO`/`OVF`/`TWOS`）。**

- **静态调度算法**（不考虑后端实时状态）
    - **`RR`（`Round Robin`，轮询）** ：请求依次分发到每个 `RS`，轮流来
    - **`WRR`（`Weighted Round Robin`，加权轮询）** ：按权重比例分发，权重高的 `RS` 分到更多请求
    - **`DH`（`Destination Hashing`，目标地址哈希）** ：按请求的目标 `IP` 哈希，相同目标 `IP` 的请求始终分发到同一台 `RS`（用于缓存场景）
    - **`SH`（`Source Hashing`，源地址哈希）** ：按客户端源 `IP` 哈希，相同来源的请求到同一台 `RS`（天然实现会话保持）。注意"始终"是简化说法——`RS` 增删/权重变化会重建哈希表，且 `SH` 有 `sh-port`（`IP`+端口哈希）和 `sh-fallback`（故障回退）两个 `flag`
    - **`MH`（`Maglev Hashing`，`Maglev` 哈希，`Linux 4.18` 合入）** ：基于源 `IP` 的一致性哈希（`Google Maglev` 论文，`NSDI'16`），表容量按权重分配，支持 `mh-port/mh-fallback flag`。天然适合会话保持，`RS` 变化时受影响连接少

- **动态调度算法**（根据后端实时负载决策）
    - **`LC`（`Least Connections`，最少连接）** ：把请求分给当前连接数最少的 `RS`。严格语义是"活动连接数加权"（内核公式 (`activeconns<<8`) + `inactconns`）
    - **`WLC`（`Weighted Least Connections`，加权最少连接）** ：`LC` + 权重，按"连接数/权重"最小的原则分配。是 `ipvsadm` 的缺省算法（内核层面无默认）
    - **`SED`（`Shortest Expected Delay`，最短期望延迟）** ：考虑"活动连接数 + 1"与权重的比值`（(active+1)/weight）`，预测哪个 `RS` 延迟最短
    - **`NQ`（`Never Queue`，永不排队）** ：`SED` 的改进——如果某台 `RS` 活动连接数为 `0`，直接分配给第一个空闲者，避免请求排队；否则用 `SED`
    - **`LBLC`（`Locality-Based Least Connections`，基于局部性的最少连接）** ：目标 `IP` 哈希 + 最少连接结合，适合 `Cache` 集群（相同目标 `IP` 优先到同一台 `RS`）
    - **`LBLCR`（带复制的局部性最少连接）** ：`LBLC` 的改进——每个目标 `IP` 对应一个服务器集合（`set`），集合内按最少连接选、目标 `IP` 热度高时集合扩容（注意不是"复制流量"，是集合内多台 `RS` 可选）
    - **`FO`（`Weighted Failover`，加权故障转移）** ：把连接发给当前可用且权重最高的 `RS` ——— 类似主备故障转移（注意：不是"失效次数/权重"，那是网上的以讹传讹，内核源码就是"选权重最高的 `dest` 全给流量"）
    - **`OVF`（`Overflow-Connection`，溢出连接）** ：优先把连接给权重最高的 `RS`，当活动连接数超过其 `weight` 时"溢出"到权重次高者；全部过载则调度失败（无 `WLC` 回退）；只统计活动连接，不适合 `UDP`
    - **`TWOS`（`Power of Two Choices`，两随机选择，`Linux 6.4` 合入）** ：按权重随机抽两台候选 `RS`，比较活动连接数归一化开销，选更空闲的一台

- **选型建议**
    - 通用 `Web` 集群、无特殊需求：`WLC`（`ipvsadm` 默认）
    - 后端性能差异大：`WRR`（静态权重明确）
    - 需要会话保持：`SH` / `MH`（源地址哈希，`RS` 变化影响小）或配合持久性（`persistence`）
    - `Cache`/缓存集群：`DH` 或 `LBLC`（目标地址哈希保证缓存命中）
    - 防止单台过载：`OVF`
    - 高可用故障转移倾向：`FO`

- **协助记忆**
    - 静态五兄弟：`RR`、`WRR`、`DH`、`SH`、`MH`（"轮询、加权轮询、目标哈希、源哈希、`Maglev` 哈希"）
    - 动态九兄弟：`LC`、`WLC`、`SED`、`NQ`、`LBLC`、`LBLCR`、`FO`、`OVF`、`TWOS`
    - 默认是 `WLC`（`ipvsadm` 缺省） ，大多数场景够用；有特殊需求（会话保持、缓存命中）再换

- **进阶思考**
    - **`SH`（源地址哈希）和持久性（`persistence`）都能做会话保持，有什么区别？**
        - `SH` 是基于 `IP` 哈希的确定性分配 ——— 同一源 `IP` 按哈希到同一台 `RS`，不依赖连接状态；但 `NAT` 后面的多个用户共享一个 `IP` 会被分到同一台（负载不均）；且 `RS` 宕机时哈希到它的用户受影响（可通过 `sh-fallback` 缓解）。持久性（`persistence`）是基于连接跟踪的超时绑定——在一定时间窗口（默认 `300` 秒）内把同一来源的请求绑定到同一台 `RS`，超时重新分配。持久性更灵活（支持故障转移、负载更均衡），是更推荐的会话保持方式

    - **加权算法（`WRR`/`WLC`）的权重怎么确定？**
        - 权重反映 `RS` 的处理能力差异——通常按 `CPU` 核数、内存大小或压测结果定。例如两台服务器，一台 `8` 核一台 `4` 核，权重可设为 `2:1`。注意：权重是相对值不是绝对值；权重相同（如 `1:1`）就退化为普通轮询/最少连接。权重要根据实际压测调整，不能拍脑袋


## 🤔 简述 HTTP Cookie 和 Session 区别和联系？
- **`Cookie` 和 `Session` 都是 `Web` 应用中记录用户状态的机制，核心区别：`Cookie` 存在客户端（浏览器），`Session` 存在服务端。两者常配合使用 ——— `Session` 通过 `Cookie` 传递 `Session ID` 来识别用户。**

    - **`Cookie`（客户端状态）**
        - **定义**：由服务器通过 `Set-Cookie` 响应头下发的小段文本数据，浏览器保存在本地，后续请求自动通过 `Cookie` 请求头携带
        - **特点**：
            - **存储位置**：浏览器（客户端）
            - **容量**：约 `4KB` 上限（单个 `Cookie` 约 `4096` 字节，浏览器实现惯例；注意 `RFC 6265` 规范并未强制规定这个数值）
            - **生命周期**：可通过 `Expires/Max-Age` 设置过期时间——持久 `Cookie` 或会话 `Cookie`（无 `Expires/Max-Age` 的"会话 `Cookie`"与服务器 `Session` 是两个概念，仅名字相似）
            - **作用域**：同域（`Domain` + `Path` 限定），默认同站发送
            - **可被篡改**：客户端可修改 `Cookie` 内容（需签名/加密防篡改）

        - **常见属性**：`Secure`（仅 `HTTPS`）、`HttpOnly`（禁止 `JS` 读取）、`SameSite`（缓解 `CSRF`）、`Path/Domain`（作用域）

    - **`Session`（服务端状态）**
        - **定义**：服务器为每个会话创建的状态数据，存储在服务端（内存、文件、数据库、Redis）
        - **特点**：
            - **存储位置**：服务端
            - **容量**：无 `Cookie` 那样的硬限制（受服务端内存/存储限制）
            - **生命周期**：由服务端超时控制（如 `30` 分钟无活动过期），也可主动销毁
            - **标识**：通过 `Session ID`（一串随机字符串）识别，`Session ID` 对应服务端存储的会话数据
            - **安全性**：数据在服务端，客户端无法直接篡改


    - **联系**：`Session` 靠 `Cookie` 传递 `Session ID`
        - **典型流程**：用户登录 → 服务器创建 `Session` 并生成 `Session ID` → 通过 `Set-Cookie` 下发 `Session ID` → 浏览器存储 → 后续请求自动携带 → 服务器根据 `Session ID` 找到对应 `Session` 数据
        - **`Session ID` 的传递方式**：
            - **`Cookie` 方式（主流）** ：`Session ID` 存在 `Cookie` 中，自动携带
            - **`URL` 重写方式（备用）** ：`Session ID` 拼在 `URL` 参数中（如 `?jsessionid=xxx`）——适用于禁用了 `Cookie` 的场景，但有泄露风险（`URL` 可能被记录在日志/历史中）

        - **也就是说**：`Session` 本身不一定依赖 `Cookie`，但实践中几乎都靠 `Cookie` 传递 `Session ID`

    - **核心区别对比**
        - **存储位置**：客户端（`Cookie`）vs 服务端（`Session`）
        - **容量**：约 `4KB`（`Cookie`）vs 无硬限制（`Session`）
        - **安全性**：可篡改（`Cookie`）vs 服务端可控（`Session`）
        - **生命周期控制**：客户端可控制 vs 服务端控制
        - **性能**：每次请求都传输（`Cookie`）vs 服务端查询（`Session`）
        - **跨域**：`Cookie` 同域限制 vs `Session` 数据本身无域概念（但 `Session ID` 靠 `Cookie` 传递时仍受 `Cookie` 域限制）

    - **`2026` 年演进**
        - **`SameSite` 默认 `Lax`**：自 `Chrome 80（2020）`/`Firefox 86/Safari 13.1` 起默认。精确语义：跨站子资源、`fetch/XHR`、`iframe` 内请求默认不发送，但顶级导航 `GET` 请求仍携带（用户从别的站点击链接进入本站时会带 `Cookie`）；且默认值在 `Chromium` 系稳定为 `Lax`，其他浏览器略有差异
        - **第三方 `Cookie` 逐步淘汰**：`Safari`（`ITP`，`2020` 起）默认全面阻止；`Firefox` 默认启用 `ETP` + `Total Cookie Protection`（第三方 `Cookie` 按站点分区存储，标准模式仅拦截已知追踪器，并非拦截所有）；`Chrome` 至今不默认拦截，仅无痕模式或用户设置时拦截，`2024` 年放弃强制淘汰后逐步推出用户选择模式 
        - **无状态趋势**：`JWT` 等无状态 `token` 兴起 ——— 不依赖服务端 `Session` 存储，天然适合分布式；但主动失效难。`2026` 年业界出现回调反思——纯 `JWT` 存在吊销难、密钥轮换等痛点，部分场景回归"分布式服务端 `Session（Redis）`"或混合方案

- **协助记忆**
    - 一句话：`Cookie` 是"存在浏览器里的便签"，`Session` 是"存在服务器上的档案柜"，`Session ID` 是"打开档案柜的钥匙"（钥匙放便签里）
    - 钥匙在客户端，档案在服务端：客户端只有 `Session ID`（钥匙），真正的数据（档案）在服务端
    - 安全对比：档案（`Session` 数据）比便签（`Cookie`）安全，因为档案在服务器上，便签在用户手里随时可能被改

- **进阶思考**
    - **`Session` 数据都放服务端，为什么还需要 `Cookie`？不能不用 `Cookie` 吗？**
        - `HTTP` 是无状态协议，服务器不记得"你是谁"。`Session` 数据虽然存在服务端，但服务器要知道"这个请求对应哪份 `Session` 数据" ——— 这个对应关系（·）必须由客户端每次带来。· 是最自然的携带方式（自动发送）。如果不用 `Cookie`，只能靠 `URL` 重写传 `Session ID`，但 `URL` 易泄露（日志、历史记录、分享链接），所以实践中几乎都用 `Cookie`

    - **`Cookie` 和 `Session` 哪个更安全？**
        - 单从"存储"角度 `Session` 更安全 ——— 数据在服务端，客户端碰不到。`Cookie` 的主要风险其实是被窃取（`XSS` 窃取、明文传输窃听、`CSRF`）而非篡改 ——— 内容篡改在服务端签名校验后危害有限。`Session` 的风险在 `Session ID` 失窃（`XSS` 窃取 `Cookie`、中间人窃听）导致会话劫持；注意会话固定（`Session Fixation`）与窃取机制不同——它是攻击者预先固定一个 `ID` 给受害者使用（`RFC 6265 §8.4` 专述）。完整方案需组合：`Session ID` 用 `HttpOnly` + `Secure Cookie` 保护、传输用 `HTTPS`、防 `XSS（CSP）`、`SameSite` 缓解 `CSRF`

## 🤔 网站跨域报错 Access-Control-Allow-Origin 如何解决？	
- **跨域报错是浏览器同源策略（`Same-Origin Policy`）的保护机制：浏览器默认阻止网页脚本跨域读取响应。`CORS`（`Cross-Origin Resource Sharing`）是让服务器声明"允许哪些来源访问"的机制，报错 `Access-Control-Allow-Origin` 说明服务器没有正确声明允许的来源。**

    - **先理解跨域和同源**
        - **同源**：协议 + 域名 + 端口都相同才算同源。`http://a.com` 和 `https://a.com` 不同源（协议不同）、和 `http://a.com:8080` 不同源（端口不同）
        - **跨域请求**：前端页面（`http://a.com`）向后端（`http://api.b.com`）发请求，就是跨域
        - **同源策略**：浏览器只允许脚本读取同源响应，跨域响应默认被浏览器拦截（注意：请求可能已发出、服务器可能已处理，只是浏览器拦截了响应）

    - **两种请求类型（决定处理方式）**
        - **简单请求**：满足特定条件（`GET/HEAD/POST` + `Content-Type` 限定值 + 仅 `safelisted` 请求头） ——— 浏览器直接发送，服务器只需返回 `Access-Control-Allow-Origin`。注意 `Content-Type` 只有三个值算简单请求：`application/x-www-form-urlencoded`、`multipart/form-data`、`text/plain`（`application/json` 会触发预检）
        - **预检请求（Preflight）** ：非简单请求（如带 `Authorization` 头、`Content-Type: application/json`、`PUT/DELETE` 方法）——— 浏览器先发 `OPTIONS` 请求探测，服务器必须正确响应 `OPTIONS`（且必须返回 `2xx`）并返回允许的方法/头，才继续发真实请求
        - **`2024-2026` 新机制 `PNA`（`Private Network Access`）** ：`Chrome` 从 `2024` 年底（`localhost` 场景 `Chrome 130`、内网 `RFC1918` 场景 `Chrome 137`）起，对"公网页面 → 内网/本地"的请求无条件强制预检——无论方法、无论 `mode`（甚至 `no-cors` 和同源请求），新增 `Access-Control-Request-Private-Network: true` /  `Access-Control-Allow-Private-Network: true` 头。`2025-2026` 年"莫名 `OPTIONS`_预检/请求被拦"很大比例是 `PNA` 导致

    - **解决方案**：后端加 `CORS` 响应头（根本解法）
        - **在服务器响应中加**：
            - `Access-Control-Allow-Origin: http://a.com`（允许的来源）
            - `Access-Control-Allow-Methods: GET, POST, PUT, DELETE`（允许的方法）
            - `Access-Control-Allow-Headers: Content-Type, Authorization`（允许的请求头）
            - `Access-Control-Allow-Credentials: true`（允许携带凭证，值为字面量 `true`；配了它就不能用 `*`）
            - `Access-Control-Max-Age: 3600`（预检结果缓存时间，减少 `OPTIONS` 请求）
            - `Access-Control-Expose-Headers`（可选）：默认前端只能读 `6` 个简单响应头，自定义响应头需在此列出

        - **配置位置**：
            - `Nginx：add_header Access-Control-Allow-Origin ...`
            - `Spring Boot`：`@CrossOrigin` 注解或全局 `CORS` 配置
            - `Node.js：cors` 中间件

        - **注意通配符的坑**：`Access-Control-Allow-Origin: *` 允许所有来源，但不能和 `credentials`（携带 `Cookie`）一起用——用了 `withCredentials` 时不允许用 `*`，必须写具体来源

    - **解决方案**：`Nginx` 反向代理（同源化）
        - 前端和后端域名不同导致跨域时，用 `Nginx` 反代把 `/api` 转发到后端，前端只访问同源路径 ——— 从根上消除跨域
        - 配置示例：前端访问 `http://a.com/api`，`Nginx` 把 `/api` 代理到 `http://api.b.com`
        - 优点：不需要改后端代码、避免暴露多个 `CORS` 配置
        - 适用范围：前后端域名固定对应时是常见推荐方案；但 `API` 需被多个未知域名、第三方或移动端调用时（`SaaS`、开放平台），`CORS` 头方案才是唯一可行且是行业标配

    - **解决方案**：`JSONP`（历史遗留方案）
        - 利用 `script` 标签不受同源策略限制的特点，通过回调函数拿数据
        - 缺点：只支持 `GET`、安全性差（需要服务端配合）、已过时
        - 仅用于兼容老系统的特殊场景

- **协助记忆**
    - 一句话：跨域报错 = 服务器没声明允许这个来源访问
    - 两条路：后端加 `CORS` 头（改后端）或 `Nginx` 反代同源化（改架构）
    - 通配符的坑：`*` 和 `credentials` 不能同时用
    - 记住预检：带 `Authorization` / `JSON body` 的请求会先发 `OPTIONS` 预检，服务器要正确处理（`OPTIONS` 必须返回 `2xx`）

- **进阶思考**
    - **为什么有时候 `OPTIONS` 请求返回 `403`/`404`？**
        - 预检请求（`OPTIONS`）可能没被正确处理。常见原因：一是网关/防火墙拦截了 `OPTIONS` 方法（只放行 `GET`/`POST`）；二是后端没有对 `OPTIONS` 返回正确的 `CORS` 响应头（`Access-Control-Allow-Methods`/`Headers`）；三是 `Nginx` 的 `add_header` 默认只对部分 `2xx/3xx` 状态码添加（`200/201/204/206/301/302/303/304/307/308`），需加 `always` 参数才对所有响应码添加——且预检必须返回 `2xx`，`OPTIONS` 返回 `403` 时即使加了头也必然失败。排查：用 `curl` 手动发 `OPTIONS` 请求模拟预检，看响应头是否包含 `Allow-Methods/Headers` 且状态码为 `2xx`

    - **`Access-Control-Allow-Origin: *` 和 `withCredentials` 为什么冲突？**
        - 安全设计。如果服务器返回 `*` 允许所有来源，同时又允许携带凭证，任意站点就能代表用户跨域发带凭证请求并读取响应——比只写不读的 `CSRF` 更严重。所以浏览器规定：凭证请求下 `ACAO` 必须为具体来源（`*` 判失败），且要配合 `Access-Control-Allow-Credentials: true`。补充：现代浏览器 `Cookie` 默认 `SameSite=Lax`，跨站 `fetch/XHR`（非顶级导航）默认不携带 `Lax Cookie`，只有 `SameSite=None`; `Secure` 的 `Cookie` 会带上——实际攻击面比"任何恶意网站都能带上 `Cookie`"更小，但"带凭证 + 可读响应"的底线依然不能破

- **扩展信息**  
安全响应头是 `Nginx` 层可以统一配置的 `Web` 安全防护。核心是 `CSP`（`Content-Security-Policy`，内容安全策略）——— 它告诉浏览器"页面允许加载哪些来源的资源"，`CSP` 是浏览器侧缓解 `XSS` 最重要、最有效的防御机制之一，但不能替代输入校验（`Sanitization`）与输出编码（`Encoding`）等后端/开发层面的安全措施。
    - **`CSP` 是什么**
        - `CSP` 通过响应头声明"允许加载什么"，浏览器强制执行：不符合策略的资源（脚本、图片、样式、iframe）被阻止加载
        - 缓解 `XSS` 原理：即使攻击者注入 `<script>`，`CSP` 如果没允许内联脚本/不可信来源，浏览器会拒绝执行
        - `CSP` 的威胁模型：缓解内容注入（`XSS`）与页面被恶意嵌入（`clickjacking`——注意这是 `UI redress` 而非"内容注入"）
    - **`CSP` 常用指令**
        - **`default-src`**：大多数 `fetch` 类指令的兜底策略。但需注意：`frame-ancestors`、`base-uri`、`form-action`、`sandbox` 等指令不会继承 `default-src`（未指定即默认为无限制）。
        - **`script-src`**：脚本来源（最关键的指令）
        - **`style-src`**：样式来源
        - **`img-src`**：图片来源
        - **`connect-src`**：`XHR/fetch/WebSocket` 连接来源（注意它和 `CORS` 是两层不同机制——`CSP` 是浏览器端限制，`CORS` 是服务端授权）
        - **`frame-ancestors`**：允许哪些来源嵌入本页（防点击劫持，取代 `X-Frame-Options`）
        - **`object-src`**：`<object>/<embed>` 来源（建议 'none'）
        - **base-uri**：`<base>` 标签来源（限制可被篡改的基准 `URL`）；`form-action`：表单提交目标
        - **`upgrade-insecure-requests`**：页面内 `HTTP` 请求自动升级为 `HTTPS`
        - **`report-to`**：违规上报（`report-uri` 已废弃，用 `Reporting-Endpoints` + `report-to` 替代）
        - **源关键字**：`'self'`、`'none'`、`'unsafe-inline'`（不推荐）、`'unsafe-eval'`（不推荐）、`'strict-dynamic'`（注意：`strict-dynamic` 应与 `nonce` 或 `hash` 一起使用。在支持该特性的浏览器中，会忽略传统脚本白名单（如 `'self'`、主机白名单），改由受信任脚本继续加载其他脚本。）、`'nonce-xxx'`（每次响应随机生成）
    - **`CSP` 配置案例**
        - **基础案例（静态站）** ：`Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'`
        - **进阶案例（带 `CDN` 和 `API`）** ： `Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; img-src 'self' data: https://cdn.example.com; connect-src 'self' https://api.example.com; style-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'`
        - **上线前先开报告模式（只报告不拦截）**：`Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'self' Reporting-Endpoints: csp-endpoint="https://example.com/csp-violations" Content-Security-Policy-Report-Only: ...; report-to csp-endpoint`； 注意两个坑：`Report-Only` 模式下 `frame-ancestors` 不生效（既不拦截也不产生报告）；若未配置 `report-to/report-uri`（或 `Reporting-Endpoints`），`Report-Only` 依然在浏览器端生效：它会在开发者工具控制台输出违规警告，并可被前端 `securitypolicyviolation` 事件捕获，只是不会向服务器发送 `HTTP` 违规上报包。
        - **`Nginx` 配置 `CSP`**: `add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;`
            - **`always` 必须加**：`add_header` 默认只对 `200/201/204/206/301/302/303/304/307/308` 生效，加 `always` 才对所有响应码生效（错误页也带 `CSP`）
            - **`add_header` 继承坑**：子 `location` 写了 `add_header` 会覆盖父级全部（`nginx 1.29.3+` 可用 `add_header_inherit merge`; 改为追加）
        - **`Nginx` 其他安全头（一起配，注意与 CSP 语义一致）**
            ```ini
            add_header X-Content-Type-Options "nosniff" always;
            add_header X-Frame-Options "DENY" always;             # 与 frame-ancestors 'none' 配套
            add_header Referrer-Policy "strict-origin-when-cross-origin" always;
            add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
            add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;  # 仅 HTTPS 站点
            ```
      - **策略一致性**：`CSP` 的 `frame-ancestors 'none'` 应配 `X-Frame-Options "DENY"`（或 `'self' + SAMEORIGIN`），两者语义要一致——不要出现"`CSP` 禁止嵌入、`XFO` 允许同源嵌入"的自相矛盾
      - **`HSTS` 的坑**：`includeSubDomains` 要求子域全部 `HTTPS`，否则误伤子域；`preload` 需去 `hstspreload.org` 提交后才生效
      - `X-Frame-Options` 有效值只有两档（`DENY/SAMEORIGIN`）——— `ALLOW-FROM` 已废弃且会导致整个头被浏览器忽略
    - **进阶思考**
      - **为什么 `'unsafe-inline'` 不推荐？`nonce` 和它什么关系？**
        - `'unsafe-inline'` 允许所有内联脚本执行——`XSS` 注入的正是内联脚本，等于开大后门。完全禁止内联脚本后，页面自带的 `<script>` 也会被拦截，需要移到外部 `.js` 或用 `nonce/hash` 白名单。在必须用内联脚本时，`nonce` 远比 `'unsafe-inline'` 安全、且比完全禁止更灵活 ——— `nonce` 每次响应随机生成，攻击者无法预知。实操坑：`nonce` 每次响应都变，页面被浏览器/`CDN` 缓存后 `nonce` 过期、脚本被误杀——这是高频事故，缓存页面慎用 `nonce`
      - **`CSP` 和 `X-Frame-Options` 都能防点击劫持，用哪个？**
        - `frame-ancestors（CSP）`是现代推荐、`X-Frame-Options` 是旧方案。`frame-ancestors` 更灵活（可精确指定多个来源），`X-Frame-Options` 只有 `DENY/SAMEORIGIN` 两档。两者同时存在时，`CSP` 优先（仅在 `enforce` 模式成立；`X-Frame-Options` 在响应含 `frame-ancestors` 时被忽略）。为兼容老浏览器可两者都配，但语义必须一致


## 🤔 简述 CDN 工作原理？
- **`CDN`（`Content Delivery Network`，内容分发网络）的核心思想：把内容缓存到离用户更近的节点，让用户从"附近的服务器"而不是"远方的源站"获取内容。本质是"空间换时间"——用分布在全国/全球的缓存节点，换取更短的访问延迟。**

    - **`CDN` 的核心组件**
        - **边缘节点（`Edge Node` / `POP`，`Points of Presence`）** ：分布在各地区/运营商的缓存服务器，真正响应最终用户请求
        - **中心节点 / 源站（`Origin`）** ：内容的原始来源（你的服务器）
        - **`GSLB`（`Global Server Load Balancing`，全局负载均衡）** ：负责把用户引导到"最合适"的边缘节点——通常以智能 `DNS` 方式实现，根据用户地理位置、所属运营商、节点负载，返回最优边缘节点 `IP`
        
    - **`CDN` 工作原理流程（一次访问）**
        1. 用户访问 `http://www.example.com`，发起 `DNS` 解析
        2. 智能 `DNS` / `GSLB` 调度：`DNS` 服务器根据用户所属网络的递归解析器出口 `IP` 判断地理位置和运营商，返回就近的边缘节点 `IP`（北京用户解析到北京节点，电信用户到电信节点。严格说 `DNS` 默认看到的是递归解析器出口 `IP`，启用 `EDNS Client Subnet（RFC 7871）`才能获知用户真实 `IP` 前缀）
        3. 用户请求打到边缘节点
        4. 边缘节点判断缓存：
        - 命中：边缘节点有缓存且未过期，直接返回
        - 未命中：边缘节点回源——向源站请求内容，缓存一份后返回（实际大型 `CDN` 是多级缓存 `L1` 边缘/`L2` 区域/`L3` 中心，边缘未命中常先查上层节点而非直接回源）

        5. 后续同地区用户请求同一内容，直接命中边缘节点缓存

    - **`CDN` 缓存与回源的关键点**
        - **``TTL`（缓存有效期）`** ：由源站响应头控制 ——— `Cache-Control: s-maxage`（共享缓存专用，优先于 `max-age`）、`max-age`、`HTTP/1.0` 的 `Expires`；也可在 `CDN` 平台配置"强制缓存"忽略源站头
        - **`revalidation`（304 校验）** ：缓存未命中时，边缘节点先发 `If-Modified-Since/ETag` 条件请求回源，源站返回 `304` 则复用缓存、不传 `body`——这是缓存回源的重要环节
        - **缓存刷新**：源站内容更新后，需主动刷新（`purge`）`CDN` 缓存或等 `TTL` 过期
        - **回源率 / 缓存命中率（Cache Hit Ratio）** ：衡量 `CDN` 效率的关键指标——命中率越高，源站压力越小

    - **`CDN` 的作用**
        - **加速访问**：用户从就近节点拿内容，延迟大幅降低（尤其跨地域、跨境场景）
        - **减轻源站压力**：大部分请求被边缘节点缓存吸收，源站只处理未命中请求
        - **抗 `DDoS`**：用海量带宽池（`Anycast`）吸收流量型攻击、用 `WAF` 与限速过滤应用层（`CC`）攻击，同时隐藏源站 `IP` 使其无法被直接打击
        - **隐藏源站 `IP`**：用户只和边缘节点通信，源站 `IP` 降低暴露面（需配合回源 `IP` 白名单）

    - **现状与前沿（2026 年）**
        - **已是标配（成熟多年）** ：`HTTP/3`（`QUIC 2021` 标准化、`HTTP/3 2022` 标准化，主流 `CDN 2019` 年已商用，如今是标配）；`CDN` 证书托管/自动续期（用户侧边缘证书 + 源站回源证书是两个独立维度，可选 `mTLS`）；基础边缘计算（`Cloudflare Workers 2017`、`Lambda@Edge 2017`）
        - **当前热点（2023-2026）** ：边缘 `AI` 推理（`Workers AI`、`GPU` 边缘节点、`AI Gateway`）、边缘无服务器渲染/`SSR`（`Vercel`/`Netlify edge runtime`）、边缘 `KV`/数据库（`Durable Objects` 等）、`SASE`/零信任与 `CDN` 融合（`Cloudflare Zero Trust` 等）

- **协助记忆**
    - 一句话：`CDN` = 把内容放到离用户近的地方，就近取货
    - 三个角色：边缘节点（附近的分店）、源站（总仓库）、GSLB（导购员，告诉你哪家分店近）
    - 类比快递：CDN 是"提前把货放到你楼下的便利店"，用户不用每次去总部仓库取

- **进阶思考**
    - **`CDN` 一定能提升访问速度吗？什么场景反而更慢？**
        - 不一定。`CDN` 加速依赖"就近取货"——纯动态内容（用户专属页面、实时数据）不适合缓存（核心原因是缓存收益低/需个性化，不完全是"缓存了必然旧数据"，一致性可由 TTL 控制）；小体量站点用 `CDN` 可能增加 `DNS` 解析和节点跳转额外开销；跨运营商调度不当反而更慢。适合 `CDN` 的是：静态资源（图片、CSS、JS、视频）、大文件下载、`API` 只读数据。补充：现代 `CDN` 有动态加速（`DSA`） ——— 不缓存，但通过优化 `TCP/TLS`、智能路由选路来加速动态请求

    - **`CDN` 隐藏源站 `IP` 就一定安全吗？**
        - 不是绝对的。`CDN` 隐藏源站 `IP` 是"降低暴露面"，不是"绝对隐藏"——攻击者仍可能通过：源站直接对外解析的历史 `DNS` 记录、邮件头/证书透明度日志、源站自身的外发请求、子域爆破等找到真实 `IP`。真正加固需要：源站只允许 CDN 回源 `IP` 访问（回源 `IP` 白名单）、关闭源站不必要的对外端口。`CDN` 隐藏 `IP` 只是多层防护中的一层

## 🤔 四层与七层负载均衡有什么区别？	
- **四层（`L4`）和七层（`L7`）负载均衡工作在网络模型的不同层级，核心区别：`L4` 在传输层（`TCP/UDP`）基于 `IP`+端口转发，`L7` 在应用层（`HTTP`）基于内容路由。**
    - **四层负载均衡（`L4`）**
        - **工作层级**：传输层（`OSI` 第 `4` 层），基于 `IP` + 端口转发，不解析应用层内容（需解析 `TCP/UDP` 头）
        - **代表产品**：`LVS`、`HAProxy`（`tcp` 模式）、云厂商 `NLB`、`Katran`（`Meta` 开源，`XDP/eBPF` 转发面） ；`F5 BIG-IP LTM` 是 `L4`–`L7` 一体化 · 设备（不能仅归 `L4`）；`Nginx` 的 `stream` 模块也能做 `L4`
        - **转发方式**：`NAT`、`DR`（`DSR`，直接服务器返回——返回流量不经过 `LB`）、隧道（`LVS` 三模式）
        - **特点**：
            - **性能高**：内核态或硬件转发，权威性能指标是 `pps`（每秒包数）而非并发连接数
            - **透明**：看不到 `HTTP` 内容
            - **无法做**：`URL` 路由、内容改写、`Cookie` 会话保持（传统上基于源 `IP`/五元组哈希——注意标准术语是五元组：源/目的 `IP`+端口+协议，`Katran` 即用 `5-tuple` 一致性哈希）

        - **TLS**：传统软件 `L4`（`LVS`、`nginx stream` 透传、`HAProxy tcp passthrough`）只能透传，不能终止；但现代云 `L4`（`AWS`/阿里云 `NLB` 等，`2019` 年起）支持 `TLS` 监听器/终止（多在网卡/硬件卸载，`ACM` 管理证书）；另有灰色地带——`L4` 可只读 `TLS ClientHello` 的 `SNI` 做路由而不解密（`nginx stream ssl_preread`）
        - **健康检查**：传统 `LVS` 是 `TCP` 端口探测；现代 `L4`（`NLB` 等）同样支持 `HTTP`/`HTTPS` 健康检查与响应码匹配
        - 适用：高吞吐场景、`TCP/UDP` 通用协议、作为七层的入口

    - **七层负载均衡（`L7`）**
        - **工作层级**：应用层（`OSI` 第 `7` 层），解析 `HTTP` 内容
        - **代表产品**：`Nginx`、`HAProxy`（`http` 模式）、`Envoy`/`Traefik`（云原生/服务网格默认 `L7`）、云厂商 `ALB`
        - **特点**：
            - 功能丰富：`URL` 路径路由、`Header` 改写、`Cookie` 会话保持、限流、缓存、`TLS` 终止、`gRPC/HTTP3` 支持
            - 性能低于 `L4`（需解析报文）
            - 能感知应用状态（基于应用层健康检查）

        - **会话保持**：基于 `Cookie`
        - **健康检查**：`HTTP` 探测（检查具体 `URL` 的响应码，能感知应用是否真的可用）
        - **适用**：`HTTP/HTTPS` 业务、需要路由/改写的场景

    - **关键区别对比**
        - **工作层级**：传输层 vs 应用层
        - **性能**：`L4` 高（`pps` 指标）vs `L7` 低（需解析）
        - **功能**：`L4` 简单转发 vs `L7` 内容路由/改写
        - **`TLS` 终止**：传统 `L4` 只能透传，现代云 `L4` 支持 vs `L7` 原生支持
        - **会话保持**：`L4` 传统基于五元组 vs `L7` 基于 `Cookie`
        - **健康检查**：`L4` 传统 TCP 探测 vs `L7 HTTP` 探测（现代 `L4` 也支持 `HTTP`）
        - **典型代表**：`LVS/Katran` vs `Nginx/Envoy`

    - **常见架构组合**
        - **大型架构**：`L4`（入口，扛高并发）→ `L7`（应用路由）→ 后端服务器
        - **经典组合**：`LVS` /` Katran`（四层）→ `Nginx` / `Envoy`（七层）→ 应用

- **协助记忆**
    - 一句话：`L4` 是"快递分拣员"（只看地址不分内容），`L7` 是"前台接待员"（听你需求再安排）
    - `L4` 管"到不到"（`IP`+端口转发），`L7` 管"给谁"（`URL/Host` 路由）——仅为简化助记，严格说 `L4` 也做健康检查、`L7` 路由同样基于 `IP`+端口
    - 选型：只要转发选 `L4`，要路由/改写/TLS 终止选 `L7`，大流量先用 `L4` 挡一层

- **进阶思考**
    - **`L4` 能做 `TLS` 终止吗？**
        - 分情况。传统软件 `L4`（`LVS`、`nginx stream` 透传、`HAProxy tcp passthrough`）不能 ——— `TLS` 终止需要解密报文，`L4` 只转发加密 `TCP` 流，只能透传到后端解密。现代云 `L4`（`AWS`/阿里云 `NLB` 等，`2019` 年起）支持 `TLS` 监听器，多在网卡/硬件上卸载加解密。所以"`HTTPS` 业务 + `TLS` 终止"传统选 `L7`（`Nginx/ALB`），但现代架构也可以用支持 `TLS` 的云 `L4` 做入口

    - **四层负载均衡的性能优势有多大？**
        - `L4` 权威指标是 `pps`（每秒包数）——— `Katran` 等 `XDP/eBPF` 方案在 `DPDK`/网卡卸载下可达千万级 `pps`；`L7`（`Nginx`）通常几万到几十万 `QPS`（受 `HTTP` 解析和 `TLS` 开销限制），数字随硬件/配置浮动。差距来自：`L4` 不解析应用层内容、`L7` 要解析 `HTTP` 报文且常做 `TLS` 终止。注意量纲：`L4` 的"百万级"通常是并发连接数/`pps`，`L7` 的"几万"是 `QPS` ——— 两者不是同一个量纲，对比要说清楚

## 🤔 网站报错 502 Bad Gateway 怎么解决？

## 🤔 网站 QPS 突降，如何快速定位问题？	
## 🤔 简述 HTTPS 工作原理？
## 🤔 如何设计高可用的 Web 服务架构？	
## 🤔 秒杀系统，怎么优化？
## 🤔 在高并发情况下，如何避免数据库的性能瓶颈？	
## 🤔 如何设计高并发的 Web 服务架构？
## 🤔 网站会监控哪些指标？	
## 🤔 请介绍下你之前维护的网站整体架构？
## 🤔 HTTP 长连接和短连接有什么区别及适用场景？	
## 🤔 HTTPS 中 CA 证书在客户端还是在服务端？作用是什么？
## 🤔 打开 APP 后页面空白，怎么排查问题？	

---

> 作者: [0x5c0f](https://blog.0x5c0f.cc)  
> URL: https://blog.0x5c0f.cc/posts/other/%E8%BF%90%E7%BB%B4%E5%B8%B8%E8%A7%81%E9%A2%98-%E7%BD%91%E7%AB%99%E7%BB%B4%E6%8A%A4/  

