Nginx - 反向代理 Proxy 原理及实战

48 阅读5分钟

一、代理

正向代理

  1. 定义:代理服务器代理客户端(C 端),服务端(S 端)无法知道客户端的 IP

  2. 工作流程

    1. 客户端向代理服务器发送请求
    2. 代理服务器转发请求至服务端
    3. 服务端将响应发送给代理服务器
    4. 代理服务器再将响应传递给客户端
  3. 例子:科学上网

反向代理

  1. 定义:代理服务器代理服务端(S 端),客户端(C 端)无法知道服务端的 IP

  2. 工作流程

    1. 客户端向代理服务器发送请求
    2. 代理服务器将请求转发给多台服务器中的一台
    3. 服务器处理请求并将响应发送给代理服务器
    4. 代理服务器将响应传递给客户端
  3. 例子:Nginx 的负载均衡

优缺点

  1. 优点

    1. 隐藏真实 IP,隐私
    2. 在代理服务器上设置缓存可以加快访问
    3. 突破地域限制,绕过特定地区的 IP 访问限制(科学上网)
  2. 缺点

    1. 虽然客户端和服务端的 IP 对彼此隐藏,但都会暴露给代理服务器
    2. 网络链路多了代理服务器的节点,降低访问速度

二、概述

⭐ 官方文档:[Module ngx_http_proxy_module (nginx.org)](http://nginx.org/en/docs/http/ngx_http_proxy_module.html)
  1. 定义:统一接收客户端请求,但是 nginx 自己不处理,而是转发给上游服务器(upstream server)处理

  2. 工作流程

    1. 接收:代理服务器接收 internet 上的连接请求 (request)
    2. 路由:代理服务器判断向何处(原始服务器)转交请求
    3. 转发:代理服务器转发 request 给内部网络上的服务器
    4. 返回:代理服务器接收服务器的返回结果,并将结果返回给客户端
  3. 实现流程

    1. 在 nginx 配置文件中,声明一个新的 server ,用于指定上游服务器
    2. 在 nginx 配置文件中,指定上游服务器的完整 “地址+端口号”(http.server.location.proxy_pass)
    3. 在 nginx 配置文件中,声明客户端获取资源的路径(http.server.location)
  4. 功能

    1. 安全防护:将防火墙后面的服务器提供给 Internet 用户访问
    2. 负载均衡:平衡多台原始服务器的负载
    3. 缓冲服务:为后端较慢的服务器提供缓冲
    4. HTTP 反向代理
  5. ⭐ 注意

    • 路径结尾不带 “/” ⇒ Nginx 会保留 location 中匹配到的路径前缀,并将其添加到上游服务器的地址后面
    • proxy_pass 结尾带 “/” ⇒ Nginx 会删除 location 中匹配的路径前缀,只保留剩下的路径部分,并将其直接添加到上游服务器的地址后面

Quick Start

  1. 功能:将请求转发给 github 处理
http {
    include mime.types;
    server_tokens off;

    # 下面配置反向代理的参数
    server {
        listen    8866;

        # 1. 用户访问 <http://ip>:port,则反向代理到 <https://github.com>
        location / {
            proxy_pass  <https://github.com>;                                # 将请求转发到指定的上游服务器
            proxy_redirect     off;                                        # 将响应从上游服务器传递给客户端时是否修改Location响应头中的URI
            proxy_set_header   Host             $host;                     # 设置向上游服务器发送的HTTP请求头
            proxy_set_header   X-Real-IP        $remote_addr;              # 设置发送给上游服务器的客户端的真实IP地址
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;   # 记录从客户端到最后一个代理服务器之间所有节点的IP地址
        }

        # 2.用户访问 <http://ip>:port/README.md,则反向代理到https://github.com/.../README.md
        location /README.md {
            proxy_set_header  X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass <https://github.com/moonbingbing/openresty-best-practices/blob/master/README.md>;
        }
    }
}

三、示例

技术方案

  1. 全局配置

    1. 并发能力:3 个工作进程,每个进程最多 3000 个连接(可以处理 3×3000=9000 个并发连接)
    2. 日志记录:日志记录路径为 logs/error.log,只记录 warn 级别以上的日志
  2. 本地服务器

    1. 端口:80

    2. 功能

      1. 处理所有默认到达此服务器的请求
      2. 转发 /remote_upstream/ 的请求到 “远程服务器”
      3. 转发 /local_upstream/ 的请求到本地的 8082 端口服务
  3. 本地服务

    1. 地址:localhost:8082
    2. 功能:处理本地服务器转发到 8082 的请求,保护本地服务(无法从外部访问)
  4. 远程服务器

    1. 地址:localhost:8080
    2. 根目录:/data/remote_upstream
    3. 功能:作为一个上游服务器,配置中有其他路径会将请求转发到这里(proxy_pass

工作流程

  1. /remote_upstream/

    1. 用户访问 http://localhost/remote_upstream/my_resource.html
    2. 本地服务器接收默认请求(80 端口)
    3. 本地服务器识别 URL,将请求转发到 “远程服务器”
    4. “远程服务器”接收请求并从 root 路径获取 my_resource.html 资源
  2. 用户访问 localhost/xxx.gif ,则由第二个 server 从服务器的 /data/images/ 地址获取 /data/images/xxx.gif 资源

代码实现

  1. 配置文件(nginx/conf/nginx.conf)

    worker_processes 3;
    
    pid logs/nginx.pid;
    error_log logs/error.log warn;
    
    events {
        worker_connections 3000;
    }
    
    http {
    
    		# 本地服务器
    		server {
    		    listen 80 default_server;                        # 监听 80 端口,并设置为默认服务器
    		    location /remote_upstream/ {                           # 代理 /remote_upstream/ 路径的请求到 localhost:8080
    		        proxy_pass <http://localhost:8080/>;
    		    }
    		
    		    location ~ \.(gif|jpg|png)$ {                    # 匹配 .gif, .jpg, .png 结尾的请求,并从 /data/images 目录中提供这些文件
    		        root /data/images;
    		    }
    		    
    		    # 反向代理 /local_upstream/ 路径的请求到 localhost 的 8082 端口
    		    location /local_upstream/ {
    		        proxy_pass <http://localhost:8082/>;            # local_upstream 服务所在地址
    		        proxy_set_header Host $host;                  # 传递客户端原始的域名
    		        proxy_set_header X-Real-IP $remote_addr;      # 传递客户端的真实 IP 地址
    		        # 传递 "原始客户端的 IP 地址" 和 "可能由其他代理服务器添加的 X-Forwarded-For 头部信息"
    		        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    		        proxy_set_header X-Forwarded-Proto $scheme;   # 传递客户端请求使用的协议(http || https)
    		        
    		        proxy_http_version 1.1;                       # 此协议可以支持长连接(keep-alive)
    		        proxy_set_header Connection "keep-alive";     # 保持后端连接
    		        keepalive_timeout 3600s;                      # 保持连接时间为 3600 秒(1h)
    		
    						# 设置超时时间
    		        proxy_connect_timeout 90s;                    # 连接到后端服务器的超时时间为 90 秒
    		        proxy_send_timeout 90s;                       # 发送请求到后端服务器的超时时间为 90 秒
    		        proxy_read_timeout 90s;                       # 从后端服务器读取响应的超时时间为 90 秒
    		        
    		        client_max_body_size 100m;                    # 限制上传到后端服务器的请求体最大为 100MB
    		    }
    		}
    		
    		
        # 模拟的一个远程服务器
    		server {
    		    listen 8080;
    		    root /data/remote_upstream;
    		    location / {
    		    }
    		}
        
        
    }
    

四、location 正则匹配

⭐ 参考资料:[location 匹配规则 · OpenResty最佳实践 (gitbooks.io)](https://moonbingbing.gitbooks.io/openresty-best-practices/content/ngx/nginx_local_pcre.html)
  1. 实现方式:location 的正则匹配功能

  2. 匹配规则

    模式含义
    location = /uri= 表示精确匹配,只有完全匹配上才能生效
    location ^~ /uri^~ 开头对URL路径进行前缀匹配,并且在正则之前。
    location ~ pattern开头表示区分大小写的正则匹配
    location ~* pattern开头表示不区分大小写的正则匹配
    location /uri不带任何修饰符,也表示前缀匹配,但是在正则匹配之后
    location /通用匹配,任何未匹配到其它location的请求都会匹配到,相当于switch中的default
  3. 最大匹配原则:前缀匹配,如果有包含关系时,按最大匹配原则进行匹配

    1. 示例:存在前缀匹配location /dir01 与 location /dir01/dir02,客户端请求 http://localhost/dir01/dir02/file 将最终匹配到 location /dir01/dir02