反向代理和负载均衡是什么?

进阶原理实践约 8 分钟读完

一句话回答

反向代理站在服务端前面,代表服务端接收请求、再转发给后端,客户端不知道背后有哪些真实的服务器;正向代理正好相反,代表客户端去访问外部。负载均衡是反向代理最常见的用途:按轮询、加权、最少连接、哈希等算法把请求分发到多台服务器,并通过健康检查自动摘除故障节点。按工作的层级,负载均衡分为四层(按 IP 和端口转发,如 LVS)和七层(理解 HTTP 内容,如 Nginx)。

详细解析

正向代理和反向代理

正向代理 反向代理
代表谁 客户端 服务端
谁知道它的存在 客户端,需要主动配置代理 服务端;客户端以为自己在直接访问服务器
隐藏了谁 客户端:服务端看到的是代理的地址 后端:客户端不知道背后有哪些真实的服务器
例子 公司内网的上网代理、开发时用的抓包工具 Nginx、云厂商的负载均衡、API 网关、CDN

反向代理的作用

  • 负载均衡:把请求分发到多台后端服务器
  • TLS 终止:在代理上统一处理 HTTPS 和证书,代理到后端走内网 HTTP,后端不用各自配置证书
  • 缓存与压缩:缓存静态资源或部分接口的响应,统一开启 gzip 压缩
  • 隐藏后端、安全防护:后端不直接暴露在公网;可以在代理上做限流、IP 黑白名单、WAF
  • 统一入口:同一个域名下按路径转发,比如 / 返回前端页面,/api/ 转发给后端服务。前端调用接口就是同源请求,不存在跨域问题(见跨域)

负载均衡算法

算法 规则 适用场景
轮询 按顺序依次分配 服务器配置相近,请求耗时差不多
加权轮询 按权重分配,权重高的分到更多请求 服务器配置不同
最少连接 分给当前活跃连接最少的服务器 请求耗时差异大,比如有长连接、慢请求
IP 哈希 对客户端 IP 做哈希,同一个客户端固定到同一台服务器 需要会话保持
一致性哈希 服务器和请求的 key 映射到同一个哈希环上,增减服务器时只有少部分请求受影响 缓存集群,让同一个 key 尽量落到同一台机器
随机 随机选择,也可以加权 简单场景,请求量大时效果接近轮询

四层和七层负载均衡

四层负载均衡 七层负载均衡
工作层级 传输层 应用层
转发依据 IP 和端口,转发 TCP / UDP 流量,不看内容 URL、域名、请求头、Cookie 等 HTTP 内容
性能 高 相对低,要解析协议,HTTPS 还要解密
能力 只做转发 按路径路由、改写请求头、缓存、TLS 终止、灰度发布
代表 LVS、云厂商的网络型负载均衡 Nginx、HAProxy、云厂商的应用型负载均衡

Nginx 用 stream 模块也能做四层转发。大型系统常把两者组合起来:外层用四层负载均衡承接流量,内层用 Nginx 集群做七层路由。分层的概念见网络分层模型。

健康检查

  • 主动检查:定期探测后端(建立 TCP 连接,或者请求 /health 这类接口),连续失败若干次就摘除,恢复后再加回来
  • 被动检查:根据真实请求的失败情况判断,比如 Nginx server 指令的 max_fails 和 fail_timeout 参数。Nginx 开源版要做主动检查,需要借助第三方模块或商业版 NGINX Plus

会话保持

后端有多台服务器时,如果会话存在某一台的内存里,下一个请求落到别的机器上就找不到了。用 IP 哈希或粘性会话把同一个用户固定到同一台服务器,可以解决问题,但会导致负载不均,那台服务器故障时会话照样丢失。更好的做法是让服务无状态:会话存到 Redis 等共享存储里,或者改用 JWT(见 Cookie、Session、Token、JWT 的区别)。

获取真实的客户端 IP

经过代理后,后端看到的连接来源是代理的 IP,代理要通过请求头传递客户端的地址:

  • X-Forwarded-For:形如 X-Forwarded-For: 203.0.113.7, 10.0.0.2,最左边是客户端的地址;每经过一层代理,代理就把它看到的上一跳地址追加到末尾
  • X-Real-IP:Nginx 常用的做法,只放一个地址,通常取自 $remote_addr

这些请求头客户端可以伪造,只能信任由自己的代理追加的值,见下面的追问。

代码示例

Nginx 配置:

Nginx
upstream api_servers {
  least_conn;                       # 最少连接;不写则默认是加权轮询
  # ip_hash;                        # 换成 IP 哈希:同一个客户端固定到同一台
  server 10.0.0.11:3000 weight=3;   # 权重越高,分到的请求越多
  # 被动健康检查:30 秒内失败 3 次,接下来 30 秒不再分配请求
  server 10.0.0.12:3000 weight=1 max_fails=3 fail_timeout=30s;
  keepalive 32;                     # 每个 worker 进程最多保留的空闲长连接数(1.29.7 起默认开启)
}

server {
  listen 443 ssl;
  server_name example.com;
  ssl_certificate     /etc/nginx/certs/example.com.pem;
  ssl_certificate_key /etc/nginx/certs/example.com.key;

  # 前端单页应用
  location / {
    root /var/www/app;
    try_files $uri $uri/ /index.html;
  }

  # 接口转发到后端集群,和页面同域,不存在跨域
  location /api/ {
    proxy_pass http://api_servers;  # 不带路径:/api/users 原样转发
    proxy_http_version 1.1;         # 这一行和下一行配合 keepalive 复用连接(1.29.7 起已是默认值)
    proxy_set_header Connection '';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

面试官可能追问

一致性哈希的原理是什么?
  • 把哈希值的空间(比如 0 到 2 的 32 次方减 1)首尾相连,组成一个环
  • 每台服务器按自己的哈希值放到环上;请求的 key 也算出哈希值,从这个位置顺时针找到的第一台服务器,就是目标服务器
  • 增加或删除一台服务器时,只有相邻区间的 key 需要迁移;而普通的取模(hash(key) % N)在 N 变化时,几乎所有 key 的位置都会变
  • 服务器少时,在环上容易分布不均,可以给每台物理服务器设置多个虚拟节点,让负载更均匀
负载均衡器自己挂了怎么办?
  • 主备 + 虚拟 IP:两台负载均衡器用 Keepalived(基于 VRRP 协议)共享一个虚拟 IP,平时由主节点持有;主节点故障时,备节点自动接管这个 IP,客户端无感知
  • 多活:DNS 解析到多个入口 IP,某个入口故障时从解析中摘除
  • 云厂商的负载均衡服务本身就是集群化、跨可用区部署的
X-Forwarded-For 可以信任吗?

不能直接信任。客户端可以自己在请求里带一个伪造的 X-Forwarded-For,代理只会在后面追加,所以最左边的值完全可能是假的。可信的只有自己的代理追加的部分:从右往左看,跳过自己信任的代理地址,遇到的第一个其他地址就是真实的客户端 IP。只有一层代理时,直接用代理按 $remote_addr 设置的 X-Real-IP 即可。后端要正确配置可信代理,比如 Nginx 的 set_real_ip_from 和 real_ip_header,Express 的 trust proxy。

易错点

  • 正向代理和反向代理的技术实现差不多,区别在于代表谁、对谁透明
  • proxy_pass 的地址带不带路径,行为不同:写成 http://api_servers/ 时,/api/users 会被转发成 /users
  • IP 哈希不保证均衡:大量用户在同一个 NAT 出口后面时,请求会集中到一台服务器上;Nginx 的 ip_hash 只用 IPv4 地址的前三段计算,同一个 /24 网段的用户都会落到同一台

AI 模拟面试官

用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮

登录后就可以和 AI 面试官对练,面试记录也会保存下来。登录

这道题你掌握了吗?

选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。

学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。