Nginx 反向代理
代理基础知识
代理分为两种,分别是正向代理和反向代理
正向代理(Forward Proxy) 和 反向代理(Reverse Proxy) 是两种常见的代理服务器,它们用于处理 网络通信中的不同方向和用途
正向代理(Forward Proxy)
特点
- 代理服务器位于客户端和目标服务器之间
- 客户端向代理服务器发送请求,代理服务器将请求发送到目标服务器,并将目标服务器的响应返回给客户端
- 目标服务器不知道客户端的存在,它只知道有一个代理服务器向其发送请求
- 客户端通过正向代理访问互联网资源时,通常需要配置客户端来使用代理
用途
- 突破访问限制:用于绕过网络访问限制,访问受限制的资源
- 隐藏客户端身份:客户端可以通过正向代理隐藏其真实 IP 地址
反向代理(Reverse Proxy)
特点
- 代理服务器位于目标服务器和客户端之间
- 客户端向代理服务器发送请求,代理服务器将请求转发给一个或多个目标服务器,并将其中一个目标服务器的响应返回给客户端
- 目标服务器不知道最终客户端的身份,只知道有一个代理服务器向其发送请求
- 用于将客户端的请求分发给多个服务器,实现负载均衡
用途
- 负载均衡:通过将流量分发到多个服务器,确保服务器的负载均匀分布
- 缓存和加速:反向代理可以缓存静态内容,减轻目标服务器的负载,并提高访问速度
- 安全性:隐藏真实服务器的信息,提高安全性,同时可以进行 SSL 终止(SSL Termination)
相同和不同
相同点
- 中间层:正向代理和反向代理都是位于客户端和目标服务器之间的中间层。
- 代理功能:它们都充当了代理的角色,处理请求和响应,使得通信更加灵活和安全
不同点
- 方向:正向代理代理客户端,反向代理代理服务器
- 目的:正向代理主要用于访问控制和隐藏客户端身份,反向代理主要用于负载均衡、缓存和提高安全性
- 配置:客户端需要配置使用正向代理,而反向代理是对服务器透明的,客户端无需感知
Nginx 和 LVS
Nginx 和 LVS(Linux Virtual Server) 都是流行的代理和负载均衡解决方案,但它们有一些不同的特点 和应用场景
选择使用 Nginx 还是 LVS 取决于具体的应用需求和复杂度。Nginx 更适合作为 Web 服务器和应用层负 载均衡器,而 LVS 更适用于传输层负载均衡
相同点
- 负载均衡:Nginx 和 LVS 都可以作为负载均衡器,将流量分发到多个后端服务器,提高系统的可用 性和性能。
- 性能:Nginx 和 LVS 都具有高性能的特点,能够处理大量并发连接和请求
不同点
- 层次:Nginx 在应用层进行负载均衡和反向代理,而 LVS 在传输层进行负载均衡
- 功能:Nginx 除了负载均衡外,还可以作为反向代理和静态文件服务器;而 LVS 主要专注于负载均 衡,实现简单而高效的四层分发
- 配置和管理:Nginx 配置相对简单,易于管理,适用于各种规模的应用;LVS 需要深入了解 Linux 内核和相关配置,适用于大规模和对性能有更高要求的场景
LVS 不监听端口,不处理请求数据,不参与握手流程,只会在内核层转发数据报文
Nginx 需要在应用层接收请求,根据客户端的请求参数和Nginx中配置的规则,再重新作为客户端向后 端服务器发起请求
LVS 通常做四层代理,Nginx 做七层代理
实现 http 协议反向代理
相关指令和参数
Nginx 可以基于ngx_http_proxy_module 模块提供 http 协议的反向代理服务,该模块是 Nginx 的默认模块
proxy_pass URL; # 转发的后端服务器地址,可以写主机名,域名,IP地址,也可以额外指定端口,
# 作用域 location, if in location, limit_except
proxy_hide_header field; # Nginx 默认不会将后端服务器的 Date,Server,X-Pad,X-Accel-... 这些响应头信息传给
# 客户端,除了这些之外的响应头字段会回传,
# 可以使用 proxy_hide_header 显式指定不回传的响应头字段
# 作用域 http, server, location
proxy_pass_header field; # 显式指定要回传给客户端的后端服务器响应头中的字段,
#作用域 http, server, location
proxy_pass_request_body on|off; # 是否向后端服务器发送客户端 http 请求的 body 部份,默认 on,
# 作用域 http, server, location
proxy_pass_request_headers on|off; # 是否向后端服务器发送客户端 http 请求的头部信息,默认 on
# 作用域 http, server, location
proxy_connect_timeout time; # Nginx与后端服务器建立连接超时时长,默认60S,超时会向客户端返回504
# 作用域 http, server, location
proxy_read_timeout time; # Nginx 等待后端服务器返回数据的超时时长,默认60S,超时会向客户端返回504
# 作用域 http, server, location
proxy_send_timeout time; # Nginx 向后端服务器发送请求的超时时长,默认60S,超时会向客户端返回408
# 作用域 http, server, location
proxy_set_body value; # 重新定义传给后端服务器的请求的正文,可以包含文本,变量等,
# 作用域 http, server, location
proxy_set_header field value; # 更改或添加请求头字段并发送到后端服务器,
# 作用域http, server, location
proxy_http_version 1.0|1.1; # 设置向后端服务器发送请求时的 http 协议版本,默认值1.0,
# 作用域http, server, location
proxy_ignore_client_abort on|off; # 客户端中断连接,Nginx 是否继续执行与后端的连接,默认值 off
#客户端中断,Nginx 也会中断后端连接, on 表示 客户端中断,nginx 还会继续处理与后端在连接
# 作用域 http, server, location
proxy_headers_hash_bucket_size size; # 当配置了 proxy_hide_header和proxy_set_header的时候,
# 用于设置nginx保存HTTP报文头的hash表的大小,默认值 64
# 作用域 http, server, location
proxy_headers_hash_max_size size; # 上一个参数的上限,默认值 512
# 作用域 http, server, location
proxy_next_upstream error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_403|http_404|http_429|non_idempotent| off ...;
# 当前配置的后端服务器无法提供服务时,因为何种错误而去请求下一个后端服务器
# 默认值 error timeout, 表示当前后端服务器因为error 和 timeout 错误时,去请求 另一个后端服务器
# 作用域 http, server, location
proxy_cache zone|off; # 是否启用代理缓存,默认 off,不启用,zone 指缓存名称,
# 作用域 http, server, location
proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size
[inactive=time] [max_size=size] [min_free=size] [manager_files=number]
[manager_sleep=time] [manager_threshold=time] [loader_files=number]
[loader_sleep=time] [loader_threshold=time] [purger=on|off]
[purger_files=number] [purger_sleep=time] [purger_threshold=time];
# 开启代理缓存后指定缓存数据的存放路径,作用域http,默认没有设置
# path 表示缓存数据存放路径,要保证nginx 有写权限
# levels 表示缓存数据目录层级,16进制表示,levels=1:2 表示第一级有16个目录,0-f,第一级中每个目录下有16*16个子目录,00-ff
# keys_zone=name:size zone 表示缓存名称,先定义后使用,size 表示该zone 空间大小
# inactive 表示缓存数据生命周期,默认值10分钟
# max_size 表示缓存占用的磁盘空间最大能有多大
proxy_cache_key string; # 指定缓存数据的key,不同的key 对应不同的缓存文件,
#作用域 http, server, location
# 默认值 $scheme$proxy_host$request_uri
proxy_cache_valid [code ...] time; # 为不同响应状态码的数据设置不同的缓存时长,可设置多条,默认不设置,
# 作用域 http, server, location
proxy_cache_use_stale error|timeout|invalid_header|updating|http_500|http_502|http_503|http_504|http_403|http_404|http_429|off ...;
# 在后端服务器报哪些错误的情况下,直接使用过期缓存数据响应客户端请求默认off,
#作用域 http, server, location
proxy_cache_methods GET|HEAD|POST ...; # 缓存哪些请求类型的数据,默认值 GET HEAD,
#作用域 http, server, location
基本配置
#转发到指定IP
server{
listen 80;
server_name www.a30.com;
#root /var/www/html/www.a30.com;
location /{
proxy_pass http://10.0.0.161; #161要开启WEB服务,请求的是默认default_Serve 配置
}
}
[root@ubuntu ~]# curl http://10.0.0.161
hello world
#被转发到后端161
[root@ubuntu ~]# curl http://www.a30.com
hello world
#转发到指定IP指定端口
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://10.0.0.161:8080;
}
}
#后端主机配置
server {
listen 8080;
root /var/www/html/8080;
}
[root@ubuntu ~]# curl www.a30.com
hello 8080
#转发到指定域名
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://www.node-1.com;
}
}
#后端主机配置
server {
listen 80;
root /var/www/html/www.node-1.com;
server_name www.node-1.com;
}
[root@ubuntu ~]# echo "node-1" > /var/www/html/www.node-1.com/index.html
#测试
[root@ubuntu ~]# curl www.a30.com
node-1
#透传指定参数
#在上述请求中,客户端访问 http://www.a30.com,该主机收到请求后作为客户端去请求http://www.node-1.com
#客户端主机配置
[root@ubuntu ~]# cat /etc/hosts
10.0.0.206 www.a30.com
#中间主机配置
[root@ubuntu ~]# cat /etc/hosts
10.0.0.161 www.a30.com
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://10.0.0.161;
proxy_set_header Host $http_host; #将客户端从请求头中传来的 Host 值传给后端服务器
}
}
#后端主机配置
server {
listen 80;
root /var/www/html/www.a30.com;
server_name www.a30.com;
}
[root@ubuntu ~]# echo "a30" > /var/www/html/www.a30.com/index.html
#客户端测试
[root@ubuntu ~]# curl http://www.a30.com
a30
#如果后端服务不可用,从客户端访问会返回502
#停止后端nginx
[root@ubuntu ~]# systemctl stop nginx.service
#客户端访问中间Nginx
[root@ubuntu ~]# curl http://www.a30.com
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx</center>
</body>
</html>
[root@ubuntu ~]# curl http://www.a30.com -I
HTTP/1.1 502 Bad Gateway
Server: nginx
Date: Wed, 18 Sep 2024 11:32:16 GMT
Content-Type: text/html; charset=utf8
Content-Length: 150
Connection: keep-alive
实现动静分离
根据条件进行调度,实现动静分离
角色
IP
Client
10.0.0.158
Proxy Server
10.0.0.157
API Server
10.0.0.161
Static Server
10.0.0.151
Client配置
[root@Rocky-9 ~]# cat /etc/hosts
10.0.0.157 www.a30.com
Proxy Server 配置
server{
listen 80;
server_name www.a30.com;
#root /var/www/html/www.a30.com;
location /static{
proxy_pass http://10.0.0.151;
proxy_set_header Host "static.a30.com";
}
location /api{
proxy_pass http://10.0.0.161;
proxy_set_header Host "api.a30.com";
}
API Server配置
server{
listen 80;
server_name api.a30.com;
root /apps/nginx/html/api.a30.com/;
}
[root@ubuntu24 sites-enabled]# cat /apps/nginx/html/api.a30.com/api/index.html
api.a30.com
Static Server配置
server{
listen 80;
server_name static.a30.com;
root /var/www/html/static.a30.com/;
}
[root@ubuntu22:sites-enabled]# cat /var/www/html/static.a30.com/static/index.html
static.a30.com
Client测试
[root@Rocky-9 ~]# curl http://www.a30.com/api/index.html
api.a30.com
[root@Rocky-9 ~]# curl http://www.a30.com/static/index.html
static.a30.com
proxy_pass 后面加斜线和不加斜线的区别
#没有斜线是追加
# http://www.a30.com/api/index.html ----> http://api.a30.com/api/index.html
location /api{
proxy_pass http://10.0.0.161;
proxy_set_header Host "api.a30.com";
}
#有斜线是替换
# http://www.a30.com/api/index.html -----> http://api.a30.com/index.html
location /api{
proxy_pass http://10.0.0.161;
proxy_set_header Host "api.a30.com";
}
实现对特定资源的代理
location ~ \.(jpe?g|png|bmp|gif)$ {
proxy_pass http://10.0.0.161;
proxy_set_header Host "api.a30.com";
}
代理服务器实现数据缓存
前置条件:各服务器时间和时区先统一,方便测试
#Proxy Server 配置
#定义缓存
proxy_cache_path /tmp/proxycache levels=1:2 keys_zone=proxycache:20m inactive=60s max_size=1g;
server{
listen 80;
server_name www.a30.com;
location /static{
proxy_pass http://10.0.0.157/;
proxy_set_header Host "static.a30.com";
proxy_cache proxycache; #使用缓存
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 301 90s;
proxy_cache_valid any 2m; #此处一定要写,否则缓存不生效
}
}
#重载,生成缓存目录
[root@ubuntu ~]# nginx -s reload
[root@ubuntu ~]# ll /tmp/proxycache/
total 8
drwx------ 2 www-data root 4096 Feb 12 23:09 ./
drwxrwxrwt 14 root root 4096 Feb 12 23:09 ../
#Static Server 配置
server {
listen 80;
root /var/www/html/static.a30.com;
server_name static.a30.com;
}
[root@ubuntu24 sites-enabled]# ls -lh /var/www/html/static.a30.com/
total 8.0K
-rw-r--r-- 1 root root 657 Sep 19 09:55 fstab
-rw-r--r-- 1 root root 18 Sep 19 09:55 index.html
#客户端测试
[root@Rocky-9 ~]# curl http://www.a30.com/static/fstab
#查看 Proxy Server 上的缓存数据,文件名就是key 的 hash 值
[root@ubuntu22:sites-enabled]# tree /tmp/proxycache/
/tmp/proxycache/
└── 3
└── ab
└── 2d291e4d45687e428f0215bec190aab3
2 directories, 1 file
#并不是一个文本文件
[root@ubuntu22:sites-enabled]# file /tmp/proxycache/3/ab/2d291e4d45687e428f0215bec190aab3
/tmp/proxycache/3/ab/2d291e4d45687e428f0215bec190aab3: data
#查看当前时间和缓存文件时间
[root@ubuntu22:sites-enabled]# date
Thu Sep 19 10:03:38 AM CST 2024
[root@ubuntu22:sites-enabled]# stat /tmp/proxycache/3/ab/2d291e4d45687e428f0215bec190aab3
File: /tmp/proxycache/3/ab/2d291e4d45687e428f0215bec190aab3
Size: 1254 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 4194322 Links: 1
Access: (0600/-rw-------) Uid: ( 33/www-data) Gid: ( 33/www-data)
Access: 2024-09-19 10:03:16.497567601 +0800
Modify: 2024-09-19 10:02:55.416164600 +0800
Change: 2024-09-19 10:02:55.416164600 +0800
Birth: 2024-09-19 10:02:55.416164600 +0800
#除了文件内容外,还有头部信息
[root@ubuntu22:sites-enabled]# cat /tmp/proxycache/3/ab/2d291e4d45687e428f0215bec190aab3
?ꦘꧥꤪeudU"66eb8498-291"
KEY: /static/fstab
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 19 Sep 2024 02:05:25 GMT
Content-Type: application/octet-stream
Content-Length: 657
Last-Modified: Thu, 19 Sep 2024 01:55:36 GMT
Connection: close
ETag: "66eb8498-291"
Accept-Ranges: bytes
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/ubuntu-vg/ubuntu-lv during curtin installation
/dev/disk/by-id/dm-uuid-LVM-H5gHRIOORHBs4Od7xVmGBfQE2ZJSd1kxqhie9niuE1re5q1XVMJmVvoQ7stlUKe2 / ext4 defaults 0 1
# /boot was on /dev/sda2 during curtin installation
/dev/disk/by-uuid/8a0cc0fa-cc21-4fe4-ab25-4a43540d9f02 /boot ext4 defaults 0 1
/swap.img none swap sw 0 0
#生命周期结束后文件被删除
#但是在缓存有效期内,后端服务器内容发生了更新,客户端获取的还是缓存数据
#后端真实数据删除,客户端还能拿到缓存数据
实现客户端IP地址透传
在使用Nginx 做代理的情况下,默认后端服务器无法获取客户端真实IP地址
角色
IP
Client
10.0.0.158
Proxy Server
10.0.0.157
Real Server
10.0.0.151
默认情况下,后端服务器无法获取真实客户端IP
Proxy Server 配置
server{
listen 80;
server_name www.a30.com;
location / {
proxy_pass http://10.0.0.151;
}
Real Server 配置
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
return 200 ${remote_addr}---${http_x_real_ip}---${http_x_forwarded_for};
}
}
Client测试
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.157------
#后端服务器只能获取代理服务器IP
修改代理服务器配置,透传真实客户端IP
server{
listen 80;
server_name www.a30.com;
location / {
proxy_pass http://10.0.0.151;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#表示将客户端IP追加请求报文中X-Forwarded-For首部字段,多个IP之间用逗号分隔,如果请求中没有X-Forwarded-For,就使用$remote_addr
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#客户端测试 $remote_addr 获取代理IP,$http_x_real_ip 获取真实客户端IP,
$http_x_forwarded_for 获取真实客户端IP
#客户端测试
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.157---10.0.0.158---10.0.0.158
实现多级代理客户端IP透传
角色
IP
Client
10.0.0.158
Proxy Server - First
10.0.0.161
Proxy Server - Second
10.0.0.157
Real Server
10.0.0.151
158 -----> 161 -----> 157 -----> 151
#Proxy Server - First 配置
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://10.0.0.157;
}
}
#Proxy Server - Second 配置
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
proxy_pass http://10.0.0.151;
}
}
#Real Server 配置
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
return 200 ${remote_addr}---${http_x_real_ip}---${http_x_forwarded_for};
}
}
#客户端测试,Real Server 只能通过 $remote_addr 获取上一级代理的IP
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.157------
修改第一级代理服务器配置
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://10.0.0.157;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
#客户端测试 $remote_addr 获取上一级代理IP,$http_x_real_ip 获取真实客户端IP,$http_x_forwarded_for 获取真实客户端IP
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.157---10.0.0.158---10.0.0.158
继续修改第二级代理服务器配置
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
proxy_pass http://10.0.0.151;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
#客户端测试
# $remote_addr 获取上一级代理IP
# $http_x_real_ip 获取上上一级代理IP
# $http_x_forwarded_for 累加了第一级代理的 X-Forwarded-For 和第二级代理的 X-Forwarded-For
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.157---10.0.0.161---10.0.0.158, 10.0.0.161
第一级代理不透传,不添加请求头字
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://10.0.0.157;
#proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.157---10.0.0.161---10.0.0.161
实现 http 协议反向代理的负载均衡
相关指令和参数
在实现 Nginx 反向代理的基础上,可以基于 ngx_http_upstream_module 模块实现后端服务器的分 组,权重分配,状态监测,调度算法等高级功能
upstream name { server address [parameters]; } # 定义一个后端服务器组,可以包含一台或多台服务器,
# 定义好后在 proxy_pass 指令中引用,作用域 http
server address [parameters]; # 在 upstream 中定义一个具体的后端服务器,作用域upstream
# address 指定后端服务器 可以是IP地址,主机名,或UNIX Socket,可以加端口号
# parameters 是可选掺数,具体有以下几个属性
# weight=number 指定该 server 的权重,默认值都是1
# max_conns=number 该 Server 的最大活动连接数,达到后将不再给该 Server 发送请求,默认值0,表示不限制
# max_fails=number 后端服务器的下线条件,当客户端访问时,对本次调度选中的后端服务器连续进行检测多少次,如果都失败就标记为不可用,默认为1次,当客户端访问时,才会利用TCP触发对探测后端服务器健康性检查,而非周期性的探测
# fail_timeout=time 后端服务器的上线条件,对已经检测到处于不可用的后端服务器,每隔此时间间隔再次进行检测是否恢复可用,如果发现可用,则将后端服务器参与调度,默认为10秒
# backup 标记该 Server 为备用,当所有后端服务器不可用时,才使用此服务器
# down 标记该 Server 临时不可用,可用于平滑下线后端服务器,新请求不再调度到此服务器,原有连接不受影响
hash key [consistent]; # 使用自行指定的 Key 做 hash 运算后进行调度,Key 可以是变量,比如请求头中的字段,URI等,如果对应的 server 条目配置发生了变化,会导致相同的 key 被重新hash
# consistent 表示使用一致性 hash,此参数确保该upstream 中的 server 条目发生变化时,尽可能少的重新 hash,适用于做缓存服务的场景,提高缓存命中率
# 作用域 upstream
ip_hash; # 源地址hash调度方法,基于的客户端的remote_addr(源地址IPv4的前24位或整个IPv6地址)做hash计算,以实现会话保持,作用域 upstream
least_conn; # 最少连接调度算法,优先将客户端请求调度到当前连接最少的后端服务器,相当于LVS中的 LC 算法
# 配合权重,能实现类似于 LVS 中的 WLC 算法
# 作用域 upstream
keepalive connections; # 为每个 worker 进程保留最多多少个空闲保活连接数,超过此值,最近最少使用的连接将被关闭
# 默认不设置,作用域 upstream
keepalive_time time; # 空闲连接保持的时长,超过该时间,一直没使用的空闲连接将被销毁
# 默认值 1h,作用域 upstream
基本配置
角色
IP
Client
10.0.0.158
Proxy Server
10.0.0.157
Real Server - 1
10.0.0.161
Real Server - 2
10.0.0.151
# Proxy Server 配置
upstream group1{
server 10.0.0.161;
server 10.0.0.151;
}
server{
listen 80;
server_name www.a30.com;
#root /var/www/html/www.a30.com;
keepalive_timeout 15 30;
autoindex on;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
# Real Server-1 配置
server {
listen 80;
root /var/www/html/www.a30.com;
server_name www.a30.com;
}
[root@ubuntu24 www.a30.com]# cat /apps/nginx/html/www.a30.com/index.html
10.0.0.161
# Real Server-2 配置
server {
listen 80;
root /var/www/html/www.a30.com;
server_name www.a30.com;
}
[root@ubuntu22:sites-enabled]# cat /var/www/html/www.a30.com/index.html
www.a30.com
10.0.0.151
#客户端测试-轮循调度到后端服务器
[root@Rocky-9 ~]# curl www.a30.com
www.a30.com
10.0.0.151
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.161
[root@Rocky-9 ~]# curl www.a30.com
www.a30.com
10.0.0.151
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.161
设置权重
#每个 server 配置的默认权重是1,这种写法,两个 server 被调度的比例为 3:1
upstream group1{
server 10.0.0.161 weight=3;
server 10.0.0.151;
}
[root@Rocky-9 ~]# curl www.a30.com
www.a30.com
10.0.0.151
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.161
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.161
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.161
[root@Rocky-9 ~]# curl www.a30.com
www.a30.com
10.0.0.151
限制最大活动连接数
#10.0.0.161 同时只能维持两个活动连接
upstream group1{
server 10.0.0.161 max_conns=2;
server 10.0.0.151;
}
#后端服务器配置
server {
listen 80;
root /var/www/html/www.a30.com;
server_name www.a30.com;
limit_rate 10k;
}
#客户端测试,开6个窗口下载文件
[root@ubuntu ~]# wget http://www.a30.com/test.img
#查看 10.0.0.161 上的连接,2个活动连接
[root@ubuntu ~]# ss -tnpe | grep 80
#查看 10.0.0.151 上的连接,4个活动连接
#客户端继续测试,新的请求都不会调度给 10.0.0.161
后端服务器健康性检查
Nginx 的 upstream 指令对于后端服务器的健康性检查是被动检查,当有客户端请求被调度到该服务器 上时,会在TCP协议层的三次握手时检查该服务器是否可用,如果不可用就调度到别的服务器,当不可 用的次数达到指定次数时(默认是1次,由 Server 配置中的 max_fails 选项决定),在规定时间内(默 认是10S,由 Server 配置中的 fail_timeout 选项决定),不会再向该服务器调度请求,直到超过规定时 间后再次向该服务器调度请求,如果再次调度该服务器还是不可用,则继续等待一个时间段,如果再次 调度该服务器可用,则恢复该服务器到调度列表中
upstream group1{
server 10.0.0.161;
server 10.0.0.151;
}
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
#停止 10.0.0.161 上的Nginx 服务
[root@ubuntu ~]# systemctl stop nginx.service
#客户端测试,检测到 10.0.0.161不可用,将请求都调度到 10.0.0.151
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
#启用 10.0.0.161 上的Nginx 服务,客户端需要在距离上次检测该服务器不可用10S后才能调度到该服务器
[root@ubuntu ~]# systemctl stop nginx.service
#如果后端服务器上的资源不存在,则不会影响调度,会返回对应的状态码和状态页
[root@ubuntu ~]# mv /var/www/html/www.a30.com/index.html{,bak}
#客户端测试
[root@ubuntu ~]# curl www.a30.com/index.html
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com/index.html
<html>
<head><title>404 Not Found</title></head>
设置备用服务器
#设置 backup,当 10.0.0.161 和 10.0.0.151 都不可用时,请求会被调度到 10.0.0.213
upstream group1{
server 10.0.0.161;
server 10.0.0.151;
server 10.0.0.213 backup;
}
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
#当前151 和 161 可用,客户端测试
[root@ubuntu ~]# curl www.a30.com
10.0.0.161 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.161 index
#停止151和161的 Nginx 服务后再次测试
[root@ubuntu ~]# curl www.a30.com
10.0.0.213
设置后端服务器平滑下线
#Proxy Server 配置
upstream group1{
server 10.0.0.161;
server 10.0.0.151;
}
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
#后端服务器配置
server {
listen 80;
root /var/www/html/www.a30.com;
server_name www.a30.com;
limit_rate 10k;
}
#客户端测试 - 开启两个窗口下载文件
[root@ubuntu ~]# wget http://www.a30.com/test.img
#在10.0.0.161 上查看,有一个连接
[root@ubuntu ~]# ss -tnep | grep 80
#在10.0.0.151 上查看,也有一个连接
[root@ubuntu ~]# ss -tnep | grep 80
#修改Proxy Server 配置,将 10.0.0.161 下线
upstream group1{
server 10.0.0.161 down;
server 10.0.0.151;
}
#重载生效
[root@ubuntu ~]# nginx -s reload
#你会发现原来保持的下载连接没有中断,但新的请求,不会再被调度到 10.0.0.161
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
负载均衡调度算法
源IP地址hash
ip_hash 算法只使用 IPV4 的前 24 位做 hash 运算,如果客户端IP前24位一致,则会被调度到同一台后 端服务器
#Proxy Server 配置
upstream group1{
ip_hash;
server 10.0.0.161;
server 10.0.0.151;
}
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
#10.0.0.208 - 客户端测试,被调度到 10.0.0.151
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
#10.0.0.213 - 客户端测试,被调度到 10.0.0.151
[root@rocky ~]# curl www.a30.com
10.0.0.151 index
[root@rocky ~]# curl www.a30.com
10.0.0.151 index
[root@rocky ~]# curl www.a30.com
10.0.0.151 index
使用自行指定的 key 做 hash 调度
#Proxy Server 配置
#三台 server 权重一样,调度算法 hash($remoute_addr)%3,值为 0,1,2根据不同的值调度到不同server
upstream group1{
hash $remote_addr;
server 10.0.0.161;
server 10.0.0.151;
server 10.0.0.213;
}
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
#10.0.0.208 - 客户端测试,被调度到 10.0.0.151
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl www.a30.com
10.0.0.151 index
#10.0.0.207 - 客户端测试,被调度到 10.0.0.213
[root@rocky ~]# curl www.a30.com
10.0.0.213 index
[root@rocky ~]# curl www.a30.com
10.0.0.213 index
[root@rocky ~]# curl www.a30.com
10.0.0.213 index
#三台 server 权重不一样,调度算法 hash($remoute_addr)%(1+2+3),值为 0,1,2,3,4,5
#0 调度到 161
#1,2调度到 151
#3,4,5调度到 213
upstream group1{
hash $remote_addr;
server 10.0.0.161 weight=1;
server 10.0.0.151 weight=2;
server 10.0.0.213 weight=3;
}
#Proxy Server 配置
#根据 request_uri 进行调度,不同客户端访问同一个资源会被调度到同一台后端服务器上
upstream group1{
hash $request_uri;
server 10.0.0.161;
server 10.0.0.151;
server 10.0.0.213;
}
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
#10.0.0.208 - 客户端测试
[root@ubuntu ~]# curl www.a30.com/index.html
10.0.0.213 index
[root@ubuntu ~]# curl www.a30.com/index.html
10.0.0.213 index
[root@ubuntu ~]# curl www.a30.com/index.html
10.0.0.213 index
[root@ubuntu ~]# curl www.a30.com/a.html
10.0.0.161 aaa
[root@ubuntu ~]# curl www.a30.com/a.html
10.0.0.161 aaa
[root@ubuntu ~]# curl www.a30.com/a.html
10.0.0.161 aaa
#10.0.0.207 - 客户端测试
[root@rocky ~]# curl www.a30.com
10.0.0.213 index
[root@rocky ~]# curl www.a30.com
10.0.0.213 index
[root@rocky ~]# curl www.a30.com
10.0.0.213 index
[root@rocky ~]# curl www.a30.com/a.html
10.0.0.161 aaa
[root@rocky ~]# curl www.a30.com/a.html
10.0.0.161 aaa
最少连接调度算法
#Proxy Server 配置,最少连接调度算法
upstream group1{
least_conn;
server 10.0.0.161;
server 10.0.0.151;
}
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
#客户端开启一个下载连接,限速,让该连接一直保持
[root@ubuntu ~]# wget www.a30.com/test.img
#下载请求被调度到 10.0.0.161上了
[root@ubuntu ~]# ss -tnep | grep 80
#新开客户端测试,请求不会被调度到 10.0.0.161 上
[root@rocky ~]# curl www.a30.com
10.0.0.151 index
[root@rocky ~]# curl www.a30.com
10.0.0.151 index
[root@rocky ~]# curl www.a30.com
10.0.0.151 index
一致性 hash
#Proxy Server 配置
#三台 server 权重一样,调度算法 hash($remoute_addr)%3,值为 0,1,2 根据不同的值调度到不
同 server
upstream group1{
hash $remote_addr;
server 10.0.0.161;
server 10.0.0.151;
server 10.0.0.213;
}
server{
listen 80;
server_name www.a30.com;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
在上述配置中,三台后端服务器的权重都为 1,则总权重为 3,再使用客户端IP的 hash 值对总权重求余
假设当前调度情况如下
hash($remoute_addr)
hash($remoute_addr)%3
server
3,6,9
0,0,0
10.0.0.161
1,4,7
1,1,1
10.0.0.151
2,5,8
2,2,2
10.0.0.213
此时如果后端新增一台服务器,则总权重会变为 4,那么同样的 hash 值,最后的调度结果如下
hash($remoute_addr)
hash($remoute_addr)%4
server
4,8
0,0
10.0.0.161
1,5,9
1,1,1
10.0.0.151
2,6
2,2
10.0.0.213
3,7
3,3
10.0.0.223
我们会发现,新增后端服务器后,总权重发生变化,则所有前端的请求都会被重新计算,调度到和原来 不同的后端服务器上了,这样会导致在原来后端服务器上创建的数据,在新的服务器上没有了减少后端服务器或修改后端服务器权重,都会导致重新调度,会导致原有缓存数据失效(例如登录状 态,购物车等)
一致性哈希
一致性哈希(Consistent Hashing)是一种用于分布式系统中数据分片和负载均衡的算法,其中 的"hash环"是该算法的核心概念之一
在一致性哈希中,所有可能的数据节点或服务器被映射到一个虚拟的环上。这个环的范围通常是一个固 定的哈希空间,比如0到2^32-1,每个数据节点或服务器被映射到环上的一个点,通过对其进行哈希计 算得到。这个哈希值的范围也是在0到2^32-1之间
在这个环上,数据会被分散到最接近它的节点。当有新的数据要存储时,首先通过哈希计算得到该数据 的哈希值,然后在环上找到离这个哈希值最近的节点,将数据存储在这个节点上。同样,当要查询数据 时,也是通过哈希计算得到数据的哈希值,然后找到最近的节点进行查询
由于哈希环是一个环形结构,节点的添加和删除对整体的影响相对较小。当添加或删除节点时,只有相 邻的节点受到影响,而其他节点保持不变。这使得一致性哈希算法在分布式系统中能够提供较好的负载 均衡性能,同时减小了数据迁移的开销
总的来说,一致性哈希中的哈希环是通过哈希计算将数据节点映射到环上,以实现数据分片和负载均衡 的分布式算法
环偏移
在一致性哈希中,哈希环可能会面临的一个问题是环偏移(Ring Wrapping)。环偏移指的是哈希环上 的某个区域过于拥挤,而其他区域相对空闲,这可能导致负载不均衡。为了解决这个问题,一些改进的 一致性哈希算法引入了虚拟节点(Virtual Nodes)的概念
虚拟节点是对物理节点的一种扩展,通过为每个物理节点创建多个虚拟节点,将它们均匀地分布在哈希 环上。这样一来,每个物理节点在环上的位置会有多个副本,而不是只有一个位置。这样一来,即使哈 希环上的某个区域过于拥挤,也可以通过调整虚拟节点的数量来使得负载更均衡
综合案例
实现 http 自动重定向至 https,并将客户端 https 请求通过负载均衡的方式反向代理到后端的多台 http 服务器上
# upstream 配置
upstream group1{
server 10.0.0.161;
server 10.0.0.151;
}
# http 重定向到 https
server{
listen 80;
server_name www.a30.com;
return 302 https://$server_name$request_uri;
}
# https 配置
server{
listen 443 ssl http2;
server_name www.a30.com;
ssl_certificate /usr/share/easy-rsa/pki/www.a30.com.pem;
ssl_certificate_key /usr/share/easy-rsa/pki/private/www.a30.com.key;
ssl_session_cache shared:sslcache:20m;
ssl_session_timeout 10m;
location /{
proxy_pass http://group1;
proxy_set_header host $http_host;
}
}
#客户端测试
[root@ubuntu ~]# curl -Lk www.a30.com
10.0.0.151 index
[root@ubuntu ~]# curl -LkI www.a30.com
HTTP/1.1 302 Moved Temporarily
Server: nginx
Date: Sat, 17 Feb 2024 13:55:01 GMT
Content-Type: text/html
Content-Length: 138
Connection: keep-alive
Location: https://www.a30.com/
HTTP/2 200
server: nginx
date: Sat, 17 Feb 2024 13:55:01 GMT
content-type: text/html; charset=utf8
content-length: 17
last-modified: Wed, 14 Feb 2024 02:45:15 GMT
etag: "65cc293b-11"
accept-ranges: bytes
Nginx 的四层代理和负载
相关指令和参数
Nginx在1.9.0版本开始支持tcp模式的负载均衡,在1.9.13版本开始支持udp协议的负载,udp主要用于 DNS的域名解析,其配置方式和指令和http 代理类似,其基于ngx_stream_proxy_module模块实现tcp 负载,另外基于模块ngx_stream_upstream_module实现后端服务器分组转发、权重分配、状态监测、 调度算法等高级功能
如果编译安装,需要指定 –with-stream 选项才能支持 ngx_stream_proxy_module模块
实现TCP协议的反向代理
#10.0.0.206 Proxy Server 配置,此配置要写在最外层
stream{
server{
listen 3306;
proxy_pass 10.0.0.161:3306;
}
server{
listen 6379;
proxy_pass 10.0.0.151:6379;
}
}
#10.0.0.161 安装mysql-server,并配置远程用户
[root@ubuntu ~]# apt update;apt install mysql-server
mysql> create user proxyer@'10.0.0.%' identified by '123456';
Query OK, 0 rows affected (0.01 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
#当前mysql-server 只监听了本机的3306
[root@ubuntu ~]# ss -tnlp | grep 3306
#修改配置,注释掉这两行
[root@ubuntu ~]# vim /etc/mysql/mysql.conf.d/mysqld.cnf
#bind-address = 127.0.0.1
#mysqlx-bind-address = 127.0.0.1
skip-name-resolve #添加此行,跳过主机名反解
#重启服务
[root@ubuntu ~]# systemctl restart mysql.service
[root@ubuntu ~]# ss -tnlp | grep 3306
#10.0.0.151 安装redis-server
[root@ubuntu ~]# apt update;apt install redis-server
#当前只监听了127.1
[root@ubuntu ~]# ss -tnlp | grep 6379
#修改配置并重启
[root@ubuntu ~]# vim /etc/redis/redis.conf
#bind 127.0.0.1 ::1
protected-mode no #关闭保护模式
[root@ubuntu ~]# systemctl restart redis-server.service
[root@ubuntu ~]# ss -tnlp | grep 6379
#客户端配置,并测试
[root@ubuntu ~]# apt update;
[root@ubuntu ~]# apt install mysql-client-8.0 redis
#直连测试
[root@ubuntu ~]# mysql -h 10.0.0.161 -uproxyer -p'123456'
#redis 测试,客户端直连
[root@ubuntu ~]# redis-cli -h 10.0.0.151
#代理测试
[root@ubuntu ~]# mysql -h 10.0.0.206 -uproxyer -p'123456'
#redis 测试
[root@ubuntu ~]# redis-cli -h 10.0.0.206
实现TCP协议的负载均衡
Proxy Server 配置,此配置要写在最外层
stream{
upstream mysql{
server 10.0.0.161:3306;
server 10.0.0.151:3306;
}
upstream redis{
server 10.0.0.161:6379;
server 10.0.0.151:6379;
}
server{
listen 3306;
proxy_pass mysql;
}
server{
listen 6379;
proxy_pass redis;
}
}
实现 FastCGI 代理
相关指令和参数
fastcgi_index name; # 后端 FastCGI 服务器默认资源,默认值为空,
# 作用域 http, server, location
fastcgi_pass address; # 指定后端 FastCGI 服务器地址,可以写 IP:port,也可以指定socket 文件
# 作用域 location, if in location
fastcgi_param parameter value [if_not_empty];# 设置传递给FastCGI服务器的参数值,可以是文本,变量或组合,可用于将Nginx的内置变量赋值给自定义key
# 作用域 http, server, location
版权归原作者 amo的代码园 所有, 如有侵权,请联系我们删除。