首页  /  Nginx 进阶  /  正文

水平扩展与集群化入门

Nginx 进阶 2026-10-02📖 11 分钟👁 —
🚀 Nginx 水平扩展与优化第 1 / 8 页12345678📖 完整导航 →

一、水平扩展简单介绍

扩容提升整体吞吐量的几种方式
接着是数据异构化、服务异步化,还有扩容原则

二、单机垂直扩容

直接增加硬件资源。

垂直扩容具体加什么,整机、CPU、网卡、磁盘

三、水平扩展:集群化

会话保持本身不是集群化,它是集群化之后必须解决的问题。

会话管理的几条路,ip_hash、其他 Hash 和 Redis 加 SpringSession

比直接买新设备更便宜,也更常见。

传统接入方式和改成流量接入层之后的对比
流量接入层的完整链路,右边是上游的 Backend Server

概念理解

后端的服务器负责提供数据,是上游服务器。

后端 Nginx 是下游,用 keepalived 保持高可用。

最前端的用户是中端用户。

用户请求转发到上游服务器,每一台上游服务器的代码都需要一样,达到集群的效果,也就是请求到任一服务器的效果一样,通过 proxy_pass 和 upstream 中转。

如下图,后端服务器不一样的是分布式。

业务服务器集群里各种角色的机器,Backend、Gateway、DB、Monitor、Logic 这些

本块理解 ip_hash

保持会话,使得用户一直和一台服务器会话,除了 ip_hash 也可以用 Redis + SpringSession,借助第三方存储,不在每一台独立存储 session,都去 redis 找,之前基础篇提到过。但是它也有缺点,java 的性能不如 nginx,而且增加服务器会加大开发和运维的成本。

ip_hash 保持会话

nginx 官方原生自带的一种方式。

通过 hash 算法把用户的 IP 取 hash 值,根据后端服务器取余数,由此定位后端服务器。

(例:假设用户 IP 为 x.x.0.1,ip_hash(0.1)=125,125 和后端服务器个数取余数,假设后端有 2 台就 125/2,余 1,请求指向服务器 1,假设是 0 就转发到第一台服务器。hash 是十六进制,方便演示用此表示)

(相同 IP 指向相同服务器的问题:很多用户都走公网 IP,这时候大量的请求都走一个服务器,会有流量倾斜,压力会大,所以 ip_hash 不适用于需要在局域网用的项目,但是它效率高啊,满足中小项目在初期的快速扩容需求)

问题:

  1. IP 过于集中
  2. 后端服务器宕机,用户会话就会消失

具体配置

现在有三台如图所示。

一台 nginx 网关后面挂两台后端,分别是 172.16.0.9 和 10.1.4.16

现在做测试:将 nginx 的请求负载到两台上,配置如下

# 在nginx上配置默认页面转发规则
upstream httpget {

    ip_hash;

    server 172.16.0.9;
    server 10.1.4.16;
    }



server {
    listen 80;
    server_name 168kaguyachi.site;

    root /var/www/html;
    index index.html;


    location / {
    proxy_pass http://httpget;
    proxy_set_header Host $host;
    add_header Content-Type "text/html; charset=utf-8";# 中文不乱码
    resolver 127.0.0.1 valid=30s;# 本地DNS响应更快
    }

}

# 在两台机器分别写入一句话
# 172.16.0.9 上执行
echo "分压2" > /tmp/index.html
cd /tmp && python3 -m http.server 80 &

# 10.1.4.16 上执行
echo "分压1" > /tmp/index.html
cd /tmp && python3 -m http.server 80 &

请求走一趟大概是这个顺序:

浏览器

  ↓ 访问 168kaguyachi.site

Nginx 网关 (VM-0-12-ubuntu)

  ↓ proxy_pass http://httpget;

  ↓ ip_hash 根据你的IP选择后端

后端 Python 服务 (10.1.4.16)

  ↓ 读取 /tmp/index.html 的内容

  ↓ 返回 "这是服务器16"

Nginx 网关

  ↓ 把后端的响应原样返回

你的浏览器 → 显示 "这是服务器16"

当访问默认端口会一直显示一个后端的标识,因为用了 ip_hash:

浏览器里一直显示分压1

当然在 upstream 去掉 ip_hash 就变成轮询了。

补充内容,后端服务器仅对内网 nginx 开放,配置如下

sudo ufw allow ssh
sudo ufw allow from 10.1.0.12 to any port 80 proto tcp
sudo ufw --force enable
sudo ufw status numbered

URI 保持会话

访问相同的 url 被转发到同一服务器,有的情况会话用户不支持 cookie,直接把会话保持密码写到 url,如 http://IP:8080/index.jsp?pageNum=100&jsessionid=??,直接在 url 加入 jsessionid。

或者 url 请求了一个文件,根据文件定位到服务器上。

上述情况都需要有哈希算法,如果后端服务器上缓存的内容不一样,比如:

就用 url_hash,所有访问 /video/1.mp4 的请求全部打到服务器 9,命中缓存,不用去磁盘读。如果用了 ip_hash,两个不同的客户端访问同一个视频,可能会被分配到不同的服务器,缓存就浪费了。

具体配置

ip_hash 配置的部分不变,只改一下 upstream 即可(nginx 原生支持 urihash 算法)。

upstream httpget {

    hash        $request_uri;# 只修改这里

    server 172.16.0.9;
    server 10.1.4.16;
    }



server {
    listen 80;
    server_name 168kaguyachi.site;

    root /var/www/html;
    index index.html;


    location / {
    proxy_pass http://httpget;
    proxy_set_header Host $host;
    add_header Content-Type "text/html; charset=utf-8";
    resolver 127.0.0.1 valid=30s;
    }

}

至此,负载均衡的方式已经学的差不多了,可以解决大部分的实际需求。

第一次登录服务器给用户下发文件,记录 cookie,下次登录携带此文件请求服务器,可以通过此文件取哈希值(不止 IP 和文件,很多东西都能取哈希值,因为哈希算法的本质就是把任意长度的数据映射成固定长度的数字,关键是如何辨别不同用户),与服务器会话连接。

写语法加 $ 的意思就是需要用到 nginx 的内部变量。

直接在 upstream 块配置好 hash $cookie_jsessionid,其余的与 ip_hash 一样。如果使用 java 程序,下发的就是 jsessionid,如果没有部署 tomcat 则不能用 jsessionid,也就是取的 hash 值是数据包 cookie 中 name 字段的值(value),所以里面 name 的值只要有都可以用。

Cookie 里的 JSESSIONID 和它那串值

好处:每个用户根据会话信息做负载均衡,请求不会一直打到一台服务器。

第三方模块 sticky

视频里的技术被淘汰了,该项目无人维护,现在商业版内置了 sticky 功能,开源版用之前讲的玩就行。

如果一个模块网上搜不到一些教程,可能已经被淘汰了。

upstream httpget {
    sticky; # 商业版直接写,自动生成路由Cookie
    server 172.16.0.9:8080;
    server 10.1.4.16:8080;
}

商业版的 sticky 会自动给客户端生成一个名为 route 的 Cookie,后面的请求只要带着这个 Cookie,就会被转发到同一台后端服务器。如果某台后端挂了,它会自动把请求切换到另一台,并更新客户端的 Cookie。还有更精细的 sticky learn 模式,可以抓后端应用自己发的 JSESSIONID 或 PHPSESSID 这种 Cookie,和 Tomcat、PHP 的 Session 机制直接对接,这部分用到再看。

Cookie 里多出来的 route

这个 route 可以改,cookie 的过期时间也可以变。

upstream httpget {
    sticky cookie qianzaoaiyin expires=1h;
    server 172.16.0.9:8080;
    server 10.1.4.16:8080;
}

这个名字不能和后端下发的 jsessionid 冲突,否则会失效。

多标签页会共享 cookie。

sticky 还能保持后端是静态服务器下的状态维持。