5.Nginx 反向代理

本文最后更新于:2023年12月5日 晚上

反向代理:reverse proxy,指的是代理外网用户的请求到内部指定的服务器,并将数据返回给用户的一种方式,这是用的比较多的一种方式

nginx 除了可以在企业提供高性能的 web 服务之外,还可以将 nginx 本身不具备的请求通过某种预定义的协议转发至其他服务器处理,不同的协议就是 nginx 服务器进行通信的一种规范,主要在不同的场景使用以下模块实现不同的功能

# 将客户端的请求以http协议转发至指定服务器进行处理
ngx_http_proxy_module

# 允许定义一组服务器。它们可以在指令proxy_pass、 fastcgi_pass和 memcached_pass中被引用到
ngx_http_upstream_module

# 将客户端的请求以tcp协议转发至指定服务器处理
ngx_stream_proxy_module

# 将客户端对php的请求以fastcgi协议转发至指定服务器助理
ngx_http_fastcgi_module

# 将客户端对Python的请求以uwsgi协议转发至指定服务器处理
ngx_http_uwsgi_module

逻辑调用关系:

生成环境部署结构:

访问逻辑图:

http 反向代理

反向代理配置参数

  • proxy_pass

    设置后端服务器的协议和地址,协议支持 http 和 https,地址既可以是域名也可以是 ip,还可以配置端口

    语法: proxy_pass URL;
    默认值: —
    上下文: location, if in location, limit_except

    URL 分为两种:结尾加”/“和结尾不加”/“,如果不加”/“,proxy_pass 类似 root,如果加”/“,proxy_pass 类似 alias

    范例:

    location /web {
     # http://to2b.cn/web/index.html ==> http://127.0.0.0.1:8080/web/index.html
     proxy_pass http://127.0.0.1:8080
    }
    
    location /web {
     # http://to2b.cn/web/index.html ==> http://127.0.0.0.1:8080/index.html
     proxy_pass http://127.0.0.1:8080/
    }
    
    # 如果location定义uri时使用了正则表达式模式(包括~和~*),则结尾不能加"/"
    location ~|~* /uri/ {
     proxy_pass http://host:port; # proxy_pass后面的url 不能加/
    }
  • proxy_hide_header

    nginx 作为反向代理服务器时,在返回给客户端 http 响应时,隐藏后端服务器响应头的信息

    语法: proxy_hide_header field;
    默认值: —
    上下文: http, server, location

    示例:隐藏后端服务器的 ETag 首部字段

    location /web {
     index index.html;
     proxy_pass http://10.0.0.18:8080/;
     proxy_hide_header ETag;
    }
  • proxy_pass_header

    允许传送被屏蔽的后端服务器响应头到客户端。

    默认 nginx 在响应报文中不传递后端服务器的首部字段 Date, Server, X-Pad, X-Accel 等参数,如果要传递的话则要使用proxy_pass_header field声明将后端服务器返回的值传递给客户端

    field 首部字段大小不敏感

    语法: proxy_pass_header field;
    默认值: —
    上下文: http, server, location

  • proxy_pass_request_body

    是否向后端服务器发送 HTTP 实体部分,默认开启

    Syntax: proxy_pass_request_body on | off;
    Default: proxy_pass_request_body on;
    Context: http, server, location

  • proxy_pass_request_headers

    是否将客户端的请求头部转发给后端服务器

    Syntax: proxy_pass_request_headers on | off;
    Default: proxy_pass_request_headers on;
    Context: http, server, location

  • proxy_set_header

    可更改或添加客户端的请求头部信息内容并转发至后端服务器,比如在后端服务器想要获取客户端的真实 IP 的时候,就要更改每一个报文的头部

    语法: proxy_set_header field value;
    默认值: proxy_set_header Host $proxy_host;
    proxy_set_header Connection close;
    上下文: http, server, location

    示例:

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
  • proxy_connect_timeout

    配置 nginx 服务器与后端服务器尝试建立连接的超时时间,默认为 60 秒,超时会返回客户端 504 响应码

    语法: proxy_connect_timeout time;
    默认值: proxy_connect_timeout 60s;
    上下文: http, server, location

    示例:

    proxy_connect_timeout 6s;
  • proxy_read_timeout

    定义从后端服务器读取响应的超时。此超时是指相邻两次读操作之间的最长时间间隔,而不是整个响应传输完成的最长时间。如果后端服务器在超时时间段内没有传输任何数据,连接将被关闭

    语法: proxy_read_timeout time;
    默认值: proxy_read_timeout 60s;
    上下文: http, server, location

  • proxy_send_timeout

    定义向后端服务器传输请求的超时。此超时是指相邻两次写操作之间的最长时间间隔,而不是整个请求传输完成的最长时间。如果后端服务器在超时时间段内没有接收到任何数据,连接将被关闭

    语法: proxy_send_timeout time;
    默认值: proxy_send_timeout 60s;
    上下文: http, server, location

  • proxy_http_version

    语法: proxy_http_version 1.0 | 1.1;
    默认值: proxy_http_version 1.0;
    上下文: http, server, location

    设置代理使用的 HTTP 协议版本。默认使用的版本是 1.0,而 1.1 版本则推荐在使用 keepalive 连接时一起使用

  • proxy_ignore_client_abort

    决定当客户端在响应传输完成前就关闭连接时,nginx 是否应关闭后端连接,默认为 off

    当客户端网络中断请求时,nginx 服务器中断其对后端服务器的请求。即如果设置 on 开启,服务器会忽略客户端中断并一直等着代理服务执行返回,如果设置 off,则客户端中断后 Nginx 也会中断客户端请求并立即记录 499 日志

    语法: proxy_ignore_client_abort on | off;
    默认值: proxy_ignore_client_abort off;
    上下文: http, server, location

  • proxy_headers_hash_bucket_size

    当配置了 proxy_hide_header 和 proxy_set_header 的时候,用于设置 nginx 保存 HTTP 报文头的 hash 表的上限,默认 64 字节

    示例:

    proxy_headers_hash_bucket_size 128;
  • proxy_headers_hash_max_size

    设置 proxy_headers_hash_bucket_size 的最大可用空间,默认 512 字节

  • server_names_hash_bucket_size

    server_name hash 表申请空间大小

  • server_names_hash_max_size

    设置服务器名称 hash 表的上限大小

缓存 proxy_buffering 和 proxy_cache

为了方便,我们定义三个角色:A 客户端 browser、B 代理服务器 Nginx,C 被代理服务器 PHP

proxy_buffering:缓冲,实现被代理服务器的数据和客户端的请求异步,A发起请求到B,B再去请求C,C反馈的数据先到 B 的 buffer 上

proxy_cache:缓存,B 将从 C 获取到的数据缓存起来,之后 A 再请求时,直接将缓存的数据返回,而不必再向 C 去获取

缓冲

  • proxy_buffering

    开启缓冲,默认开启,如果不开启,C 返回的数据实时的通过 B 发送给 A

    proxy_buffering 如果设置为 off,proxy_buffers 和 proxy_busy_buffers_size 这两个指令将会失效

  • proxy_buffers

    设置 B 的缓冲区占用的 buffer 的个数和每个 buffer 的大小,所以缓冲区的大小为这两个数字的乘积

    示例:

    proxy_buffers 8 4k;  # 缓冲区总的大小为32k
  • proxy_busy_buffers_size

    proxy_busy_buffers_size 通常设置为两个 buffer 的大小,示例:

    proxy_busy_buffers_size 8k;

    上例中,proxy_busy_buffers_size 设置为 8K,如果返回的数据小于 8K,则 B 从 C 请求的数据全部到位后,一次性返回给 A;如果返回的数据大于 8K,则 B 从 C 请求的数据分每次 8K 返回给 A,例如一共 20K 数据,分 8K、8K、4K 三次返回给 A

  • proxy_buffer_size

    该参数用来设置一个特殊 buffer 大小,C 到 B 的第一部分相应数据就存在这个 buffer 中,通常是 header,如果该参数设置太小,会出现 502 错误,不论 proxy_buffering,是否生效,proxy_buffer_size 都生效

    示例:

    proxy_buffer_size 4k; # 通常设置为单个buffer的大小
  • proxy_temp_path

    缓冲区的容量毕竟有限,如果并发请求太多,响应的数据量太大,超出缓冲区的数据会储存在临时目录下

    proxy_temp_path 定义临时目录

    语法: proxy_temp_path path [level1 [level2 [level3]]];
    默认值: proxy_temp_path proxy_temp;
    上下文: http, server, location

    至多设置三层子目录,目录的命名规则和 client_body_temp_path 一样

  • proxy_max_temp_file_size

    临时文件的最大容量,默认 1024m,也就是 1G

  • proxy_temp_file_write_size

    每次写入临时文件的数据量, 通常和 proxy_busy_buffers_size 设置的值一样,两个 buffer 的大小

缓存

  • proxy_cache

    指定用于页面缓存的共享内存。同一块共享内存可以在多个地方使用。off 参数可以屏蔽从上层配置继承的缓存功能

    语法: proxy_cache zone | off;
    默认值: proxy_cache off;
    上下文: http, server, location

    zone 是缓存区域的名称,需要 proxy_cache_path 事先定义

  • proxy_cache_path

    语法: proxy_cache_path path [levels=levels] keys_zone=name:size [inactive=time] [max_size=size] [loader_files=number] [loader_sleep=time] [loader_threshold=time];
    默认值: —
    上下文: http

    示例:

    proxy_cache_path /var/cache/nginx/proxy_cache levels=1:2:2 keys_zone=proxycache:20m inactive=120s max_size=1g
    
    # /var/cache/nginx/proxy_cache  定义缓存保存路径,proxy_cache会自动创建
    # levels=1:2:2  定义缓存目录结构层次,1:2:2可以生成2^4x2^8x2^8=2^20=1048576个目录
    # keys_zone=proxycache:20m  内存中缓存的大小,主要用于存放key和metadata
    # inactive=120s  缓存有效时间
    # max_size=1g 最大磁盘占用空间,磁盘存入文件内容的缓存空间最大值
  • proxy_cache_key

    定义如何生成缓存的键,默认值就挺好,一般不用设置

    语法: proxy_cache_key string;
    默认值: proxy_cache_key $scheme$proxy_host$request_uri;
    上下文: http, server, location

  • proxy_cache_valid

    为不同的响应状态码设置不同的缓存时间

    语法: proxy_cache_valid [code …] time;
    默认值: —
    上下文: http, server, location

    示例:

    proxy_cache_valid 200 302 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_valid any 1m;
  • proxy_cache_use_stale

    如果后端服务器出现状况,nginx 可以使用过期的响应缓存。这条指令就是定义何种条件下允许开启此机制

    语法: proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_404 | off …;
    默认值: proxy_cache_use_stale off;
    上下文: http, server, location

    示例:

    proxy_cache_use_stale error http_502 http_503;
  • proxy_cache_methods

    对哪些客户端请求方法对应的响应进行缓存,虽然 GET 和 HEAD 方法总是被缓存,但是建议显式的指定

    示例:

    proxy_cache_methods GET | HEAD | POST ...;

添加头部报文信息

基于模块 ngx_http_headers_module,可以实现对后端服务器响应给客户端的报文中添加指定的响应首部字段

Syntax: add_header name value [always];
Default: —
Context: http, server, location, if in location

示例:

add_header X-Via $server_addr;      # 当前nginx主机的IP
add_header X-Cache $upstream_cache_status;   # 是否缓存命中
add_header X-Accel $server_name;     # 客户访问的FQDN

# 添加自定义响应信息的尾部,使用较少,1.13.2版后支持
add_trailer name value [always];

反向代理高级应用

基于 ngx_http_upstream_module 模块,实现服务器分组转发、权重分配、状态监测、调度算法等高级功能

  • upstream

    定义一组服务器。 这些服务器可以监听不同的端口。 而且,监听在 TCP 和 UNIX 域套接字的服务器可以混用

    语法: upstream name { … }
    默认值: —
    上下文: http

    示例:

    upstream backend {
        server backend1.example.com weight=5;
        server 127.0.0.1:8080       max_fails=3 fail_timeout=30s;
        server unix:/tmp/backend3;
    }
  • server

    在 upstream 内,至少要有一个 server 服务器配置

    语法: server address [parameters];
    默认值: —
    上下文: upstream

    定义服务器的地址和其他参数,地址可以是域名、ip、unix 套接字,如果没有指定端口,默认 80 端口

    如果一个域名解析到了多个 ip,本质就是在 upstream 定义了多个 server

    parameters:

    • weight=number:设定服务器的权重,默认是 1
    • max_conns=number:给当前 server 设置最大活动链接数,默认为 0 表示没有限制
    • max_fails=number:fail_timeout 规定时间内,对后端服务器连续监测失败的次数,超过则标记为不可用,默认为 1 次,当客户端访问时,才会利用 TCP 触发对探测后端服务器健康性检查,而非周期性的探测
    • fail_timeout=time:fail_timeout 时间内,连续监测失败 max_fails 次;或者 fail_timeout 时间内,一直监测失败,则标记服务器不可用
    • backup:标记为备用服务器,当其他服务器都不可用时,才会启用此服务器
    • down:标记为 down 状态
    • resolve:如果 server 定义的是主机名,当 A 记录发生变化会自动应用新 IP 而不用重启 Nginx

    示例:

    upstream backend {
        server backend1.example.com weight=5;
        # 30s内,对后端服务器连续监测失败3次,标记次服务器不可用
        server 127.0.0.1:8080       max_fails=3 fail_timeout=30s;
        server unix:/tmp/backend3;
        server backup1.example.com:8080 backup;
    }
  • hash

    Syntax: hash key [consistent];
    Default: —
    Context: upstream

    基于指定的 key 做 hash 计算,key 来自请求报文中首部字段或者 URI,consistent 定义使用一致性 hash 运算,一
    致性 hash 基于取模运算,适用于后端是 Cache 服务器(如 varnish)时使用

    示例:

    hash $request_uri consistent; # 基于用户请求的uri做hash
    hash $cookie_sessionid;  # 基于cookie中的sessionid这个key进行hash调度,实现会话绑定

  • ip_hash

    源地址 hash 调度方法,基于的客户端的 remote_addr(源地址 IPv4 的前 24 位或整个 IPv6 地址)做 hash 计算,以实现会话保持

  • least_conn

    最少连接调度算法,优先将客户端请求调度到当前连接最少的后端服务器,相当于 LVS 中的 WLC

示例:

http {
    upstream webserver {
        #hash $request_uri consistent;
        #hash $cookie_sessionid
        #ip_hash;
        #least_conn;
        server 10.0.0.101:80 weight=1 fail_timeout=5s max_fails=3; #后端服务器状态监测
        server 10.0.0.102:80 weight=1 fail_timeout=5s max_fails=3;
        #server 127.0.0.1:80 weight=1 fail_timeout=5s max_fails=3 backup;
    }
    server {
        listen 80;
        server_name www.magedu.org;
        location / {
        index index.html index.php;
        root /data/nginx/html/pc;
    }
    location /web {
        index index.html;
        proxy_pass http://webserver/; # 结尾加"/",proxy_pass类似alias
    }
}

反向代理客户端 IP 透传

# 第一个代理服务器 10.0.0.8
location / {
 proxy_pass http://10.0.0.18;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# 第二个代理服务器 10.0.0.18
location / {
 proxy_pass http://10.0.0.28;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# 第三个服务器 10.0.0.28,最终会接收到客户端和中间两个代理服务器的ip

nginx 把客户端的请求轮询到不同的服务器,对于已经登录的客户端(用户信息储存在 cookie),就不适合轮询了,应该始终访问储存登录信息的那台服务器

http {
 upstream websrvs {
  hash $cookie_PHPSESSID; # PHPSESSID是cookie的key的名称
  server 10.0.0.101:80 weight=2;
  server 10.0.0.102:80 weight-1;
 }
 server {
  proxy_pass http://websrvs;
 }
}

tcp 负载均衡

基于 ngx_stream_proxy_module 模块,允许通过 TCP、UDP、和 unix 域套接字代理数据流,nginx1.9.0 版本支持

编译安装需要添加 –with-stream 编译选项

stream

没有在官方文档中找到这个参数,但是它确实存在,和 http 同级别,位于 main 上下文

负载均衡 redis

stream {
    upstream redis_server {
        #hash $remote_addr consistent;
        server 10.0.0.18:6379 max_fails=3 fail_timeout=30s;
        server 10.0.0.28:6379 max_fails=3 fail_timeout=30s;
    }
    server {
        listen 10.0.0.8:6379;
        proxy_connect_timeout 3s;
        proxy_timeout 3s;
        proxy_pass redis_server; # server处于stream上下文,这里proxy_pass的服务器组没有http
    }
}

负载均衡 mysql

stream {
    upstream mysql_server {
        least_conn;
        server 10.0.0.18:3306 max_fails=3 fail_timeout=30s;
    }
    server {
        listen 10.0.0.8:3306;
        proxy_connect_timeout 6s;
        proxy_timeout 15s;
        proxy_pass mysql_server;
    }
}

FastCGI

CGI 和 FastCGI

最早的 web 服务器只能处理静态文件,对于 php、java 这样的动态语言文件,apache 实现的方式是打补丁,nginx 则通过某种特定的协议将客户端请求转发给语言解析器,这个特定的协议就是 CGI(通用网关接口 common gateway interface),语言解析器新建进程处理请求

CGI 协议解决了语言解析器和 web 服务器之间通讯的问题,但是它的效率太低,因为每处理一个请求,就要新建进程,处理完之后就销毁进程,而 FastCGI 解决了这个问题,每次处理完请求后不会关闭进程,等待处理下一个进程,直到超时没有任务才会被销毁

php-fpm

FastCGI Process Manager,FastCGI 进程管理器,是一个实现了 FastCGI 的程序,提供进程管理功能,进程包括 master 进程和 worker 进程。master 进程只有一个,负责监听端口,接受来自 web server 的请求。worker 进程一般会有多个,每个进程中会嵌入一个 PHP 解析器,进行 PHP 代码的处理

FastCGI 配置指令

fastcgi_pass address:port; # 请求转发到后端服务器,指定后端服务器的地址和端口
fastcgi_index name;  # 默认的主页资源
fastcgi_param parameter value [if_not_empty]; # 设置传递的参数
# 示例
fastcgi_param REMOTE_ADDR $remote_addr; #客户端源IP
fastcgi_param REMOTE_PORT $remote_port; #客户端源端口
fastcgi_param SERVER_ADDR $server_addr; #请求的服务器IP地址
fastcgi_param SERVER_PORT $server_port; #请求的服务器端口
fastcgi_param SERVER_NAME $server_name; #请求的server name

动静分离

# 静态文件
location / {
    proxy_pass http://10.0.0.28;
    index index.html;
}
# 动态文件
location ~ \.php$ {
    root /data/php;
    fastcgi_pass 10.0.0.18:9000;
    fastcgi_index index.php;
    #fastcgi_param SCRIPT_FILENAME /data/php$fastcgi_script_name;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

5.Nginx 反向代理
http://blog.lujinkai.cn/运维/Nginx/5.Nginx反向代理功能/
作者
像方便面一样的男子
发布于
2020年12月30日
更新于
2023年12月5日
许可协议