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 的默认模块

bash 复制代码
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
基本配置
bash 复制代码
#转发到指定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配置

bash 复制代码
[root@Rocky-9 ~]# cat /etc/hosts

10.0.0.157 www.a30.com

Proxy Server 配置

bash 复制代码
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配置

bash 复制代码
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配置

bash 复制代码
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测试

bash 复制代码
[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 后面加斜线和不加斜线的区别

bash 复制代码
#没有斜线是追加
# 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";
}

实现对特定资源的代理

bash 复制代码
location ~ \.(jpe?g|png|bmp|gif)$ {
   proxy_pass http://10.0.0.161;
   proxy_set_header Host "api.a30.com";
}
代理服务器实现数据缓存

前置条件:各服务器时间和时区先统一,方便测试

bash 复制代码
#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 配置

bash 复制代码
server{
  listen 80;
  server_name www.a30.com;
  location / {
	proxy_pass http://10.0.0.151;
}

Real Server 配置

bash 复制代码
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测试

bash 复制代码
[root@Rocky-9 ~]# curl www.a30.com
10.0.0.157------
#后端服务器只能获取代理服务器IP

修改代理服务器配置,透传真实客户端IP

bash 复制代码
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

bash 复制代码
#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------

修改第一级代理服务器配置

bash 复制代码
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

继续修改第二级代理服务器配置

bash 复制代码
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

第一级代理不透传,不添加请求头字

bash 复制代码
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 模块实现后端服务器的分 组,权重分配,状态监测,调度算法等高级功能

bash 复制代码
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
bash 复制代码
# 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

设置权重

bash 复制代码
#每个 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

限制最大活动连接数

bash 复制代码
#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 选项决定),不会再向该服务器调度请求,直到超过规定时 间后再次向该服务器调度请求,如果再次调度该服务器还是不可用,则继续等待一个时间段,如果再次 调度该服务器可用,则恢复该服务器到调度列表中

bash 复制代码
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>

设置备用服务器

bash 复制代码
#设置 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

设置后端服务器平滑下线

bash 复制代码
#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位一致,则会被调度到同一台后 端服务器

bash 复制代码
#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 调度

bash 复制代码
#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
bash 复制代码
#三台 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

最少连接调度算法

bash 复制代码
#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
bash 复制代码
#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 服务器上

bash 复制代码
# 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协议的反向代理
bash 复制代码
#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协议的负载均衡
bash 复制代码
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 代理

相关指令和参数
bash 复制代码
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
相关推荐
九河云2 小时前
AWS账号注册费用详解:新用户是否需要付费?
服务器·云计算·aws
Lary_Rock2 小时前
RK3576 LINUX RKNN SDK 测试
linux·运维·服务器
幺零九零零3 小时前
【计算机网络】TCP协议面试常考(一)
服务器·tcp/ip·计算机网络
云飞云共享云桌面4 小时前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
一坨阿亮5 小时前
Linux 使用中的问题
linux·运维
幺零九零零7 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
wclass-zhengge7 小时前
Docker篇(Docker Compose)
运维·docker·容器
李启柱7 小时前
项目开发流程规范文档
运维·软件构建·个人开发·设计规范
free8 小时前
netstat中sendq/recvq用于排查发送端发送数据的问题
服务器
力姆泰克9 小时前
看电动缸是如何提高农机的自动化水平
大数据·运维·服务器·数据库·人工智能·自动化·1024程序员节