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
相关推荐
Michael_lcf17 分钟前
kubernetes对于一个nginx服务的增删改查
java·nginx·kubernetes
周湘zx21 分钟前
nginx不在默认的yum仓库的解决方法
运维·python·nginx
tonngw24 分钟前
Docker 使用指南
运维·docker·容器
byxdaz1 小时前
QT编程之HTTP服务端与客户端技术
开发语言·qt·http
乙龙2 小时前
在麒麟系统(基于Ubuntu或Debuntu)的离线环境中创建本地APT仓库
linux·运维·ubuntu·kylin
舰长1152 小时前
Ubuntu docker安装milvusdb
linux·运维·服务器
niuTaylor2 小时前
Linux驱动开发实战之PCIE驱动(一)
linux·运维·驱动开发
云动雨颤2 小时前
Xshell7连接Debian12系统,中文显示乱码,解决办法一览!
运维·debian
s_little_monster2 小时前
【Linux】深入理解进程和文件及内存管理
linux·运维·服务器·经验分享·笔记·学习·学习方法