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_exceptproxy_hide_header field; # Nginx 默认不会将后端服务器的 Date,Server,X-Pad,X-Accel-... 这些响应头信息传给 # 客户端,除了这些之外的响应头字段会回传, # 可以使用 proxy_hide_header 显式指定不回传的响应头字段 # 作用域 http, server, location proxy_pass_header field; # 显式指定要回传给客户端的后端服务器响应头中的字段,#作用域 http, server, locationproxy_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, locationproxy_set_header field value; # 更改或添加请求头字段并发送到后端服务器,# 作用域http, server, locationproxy_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, locationproxy_headers_hash_max_size size; # 上一个参数的上限,默认值 512# 作用域 http, server, locationproxy_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, locationproxy_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, locationproxy_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
基本配置
#转发到指定IPserver{ 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.161hello 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.comhello 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.comnode-1#透传指定参数#在上述请求中,客户端访问 http://www.a30.com,该主机收到请求后作为客户端去请求http://www.node-1.com#客户端主机配置[root@ubuntu ~]# cat /etc/hosts10.0.0.206 www.a30.com#中间主机配置[root@ubuntu ~]# cat /etc/hosts10.0.0.161 www.a30.comserver{ 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.coma30#如果后端服务不可用,从客户端访问会返回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 -IHTTP/1.1 502 Bad GatewayServer: nginxDate: Wed, 18 Sep 2024 11:32:16 GMTContent-Type: text/html; charset=utf8Content-Length: 150Connection: 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/hosts10.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.htmlapi.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.htmlstatic.a30.com
Client测试
[root@Rocky-9 ~]# curl http://www.a30.com/api/index.htmlapi.a30.com[root@Rocky-9 ~]# curl http://www.a30.com/static/index.htmlstatic.a30.com
proxy_pass 后面加斜线和不加斜线的区别
#没有斜线是追加# http://www.a30.com/api/index.html ----> http://api.a30.com/api/index.htmllocation /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.htmllocation /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 8drwx------ 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 └── 2d291e4d45687e428f0215bec190aab32 directories, 1 file#并不是一个文本文件[root@ubuntu22:sites-enabled]# file /tmp/proxycache/3/ab/2d291e4d45687e428f0215bec190aab3/tmp/proxycache/3/ab/2d291e4d45687e428f0215bec190aab3: data#查看当前时间和缓存文件时间[root@ubuntu22:sites-enabled]# dateThu 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 fileDevice: fd00h/64768dInode: 4194322 Links: 1Access: (0600/-rw-------) Uid: ( 33/www-data) Gid: ( 33/www-data)Access: 2024-09-19 10:03:16.497567601 +0800Modify: 2024-09-19 10:02:55.416164600 +0800Change: 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/fstabHTTP/1.1 200 OKServer: nginxDate: Thu, 19 Sep 2024 02:05:25 GMTContent-Type: application/octet-streamContent-Length: 657Last-Modified: Thu, 19 Sep 2024 01:55:36 GMTConnection: closeETag: "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.imgnoneswapsw00#生命周期结束后文件被删除#但是在缓存有效期内,后端服务器内容发生了更新,客户端获取的还是缓存数据#后端真实数据删除,客户端还能拿到缓存数据
实现客户端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.com10.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_addrproxy_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.com10.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.com10.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.com10.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.com10.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.com10.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计算,以实现会话保持,作用域 upstreamleast_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.html10.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.htmlwww.a30.com10.0.0.151#客户端测试-轮循调度到后端服务器[root@Rocky-9 ~]# curl www.a30.comwww.a30.com10.0.0.151[root@Rocky-9 ~]# curl www.a30.com10.0.0.161[root@Rocky-9 ~]# curl www.a30.comwww.a30.com10.0.0.151[root@Rocky-9 ~]# curl www.a30.com10.0.0.161
设置权重
#每个 server 配置的默认权重是1,这种写法,两个 server 被调度的比例为 3:1upstream group1{server 10.0.0.161 weight=3;server 10.0.0.151;}[root@Rocky-9 ~]# curl www.a30.comwww.a30.com10.0.0.151[root@Rocky-9 ~]# curl www.a30.com10.0.0.161[root@Rocky-9 ~]# curl www.a30.com10.0.0.161[root@Rocky-9 ~]# curl www.a30.com10.0.0.161[root@Rocky-9 ~]# curl www.a30.comwww.a30.com10.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.com10.0.0.151 index[root@ubuntu ~]# curl www.a30.com10.0.0.151 index[root@ubuntu ~]# curl www.a30.com10.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.html10.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.213upstream 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.com10.0.0.161 index[root@ubuntu ~]# curl www.a30.com10.0.0.151 index[root@ubuntu ~]# curl www.a30.com10.0.0.161 index#停止151和161的 Nginx 服务后再次测试[root@ubuntu ~]# curl www.a30.com10.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.com10.0.0.151 index[root@ubuntu ~]# curl www.a30.com10.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.com10.0.0.151 index[root@ubuntu ~]# curl www.a30.com10.0.0.151 index[root@ubuntu ~]# curl www.a30.com10.0.0.151 index#10.0.0.213 - 客户端测试,被调度到 10.0.0.151[root@rocky ~]# curl www.a30.com10.0.0.151 index[root@rocky ~]# curl www.a30.com10.0.0.151 index[root@rocky ~]# curl www.a30.com10.0.0.151 index
使用自行指定的 key 做 hash 调度
#Proxy Server 配置#三台 server 权重一样,调度算法 hash($remoute_addr)%3,值为 0,1,2根据不同的值调度到不同serverupstream 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.com10.0.0.151 index[root@ubuntu ~]# curl www.a30.com10.0.0.151 index[root@ubuntu ~]# curl www.a30.com10.0.0.151 index#10.0.0.207 - 客户端测试,被调度到 10.0.0.213[root@rocky ~]# curl www.a30.com10.0.0.213 index[root@rocky ~]# curl www.a30.com10.0.0.213 index[root@rocky ~]# curl www.a30.com10.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调度到 213upstream 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.html10.0.0.213 index[root@ubuntu ~]# curl www.a30.com/index.html10.0.0.213 index[root@ubuntu ~]# curl www.a30.com/index.html10.0.0.213 index[root@ubuntu ~]# curl www.a30.com/a.html10.0.0.161 aaa[root@ubuntu ~]# curl www.a30.com/a.html10.0.0.161 aaa[root@ubuntu ~]# curl www.a30.com/a.html10.0.0.161 aaa#10.0.0.207 - 客户端测试[root@rocky ~]# curl www.a30.com10.0.0.213 index[root@rocky ~]# curl www.a30.com10.0.0.213 index[root@rocky ~]# curl www.a30.com10.0.0.213 index[root@rocky ~]# curl www.a30.com/a.html10.0.0.161 aaa[root@rocky ~]# curl www.a30.com/a.html10.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.com10.0.0.151 index[root@rocky ~]# curl www.a30.com10.0.0.151 index[root@rocky ~]# curl www.a30.com10.0.0.151 index
一致性 hash
#Proxy Server 配置#三台 server 权重一样,调度算法 hash($remoute_addr)%3,值为 0,1,2 根据不同的值调度到不同 serverupstream 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 重定向到 httpsserver{ 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.com10.0.0.151 index[root@ubuntu ~]# curl -LkI www.a30.comHTTP/1.1 302 Moved TemporarilyServer: nginxDate: Sat, 17 Feb 2024 13:55:01 GMTContent-Type: text/htmlContent-Length: 138Connection: keep-aliveLocation: https://www.a30.com/HTTP/2 200server: nginxdate: Sat, 17 Feb 2024 13:55:01 GMTcontent-type: text/html; charset=utf8content-length: 17last-modified: Wed, 14 Feb 2024 02:45:15 GMTetag: "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-servermysql> 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.1skip-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 ::1protected-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, locationfastcgi_pass address; # 指定后端 FastCGI 服务器地址,可以写 IP:port,也可以指定socket 文件 # 作用域 location, if in location fastcgi_param parameter value [if_not_empty];# 设置传递给FastCGI服务器的参数值,可以是文本,变量或组合,可用于将Nginx的内置变量赋值给自定义key# 作用域 http, server, location