MeteorCat / Nginx代理转发

Created Fri, 29 Sep 2023 12:43:26 +0800 Modified Wed, 29 Oct 2025 23:24:53 +0800
2408 Words

Nginx代理转发

Nginx 现在的代理转发功能已经日趋完善, 可以用来进行大规范的数据负载转发, 比较知名的方案就是 Nginx-Tomcat/Nginx-Jar 的转发负载架构.

常见的 Java 高并发负载也是通过 Nginx 挂起代理转发到不同 Jar 服务进行处理.

Nginx 有两种转发方式:

  • HTTP转发: 只针对 HTTP 数据转发, 可以通过设置代理头转发源客户端数据.
  • Stream转发: 针对 TCP/UDP 的数据流转发, 目前转发的时候无法识别源IP.

如果仅仅作为流量不需要做任何日志和IP判断可以采用 Stream 转发, 剩下只要不涉及这种情况完全不推荐( 应该采用专门的 HAProxy 转发数据 )

Stream转发

这里说明 Stream 转发两种方式: TCP/UDP:

# 注意 Stream 转发不会涉及 HTTP 模块, 所以不需要在 HTTP 之中编写
http{
    .......
}
 
# 这里直接和 http 模块一样, 创建出新的模块
stream {
    # 这里可以追加其他默认配置, 比如重设日志输出格式
    log_format proxy '$remote_addr [$time_local] '
                     '$protocol $status $bytes_sent $bytes_received '
                     '$session_time "$upstream_addr" '
                     '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
 
    # 设置默认的日志访问
    access_log /var/log/nginx/proxy.log proxy;
 
    # 直接手动引入配置, 这里会默认加载目录下所有 xxxx.conf 后缀的配置文件
    include /etc/nginx/stream.d/*.conf;
}

TCP转发

这里就是文件配置, 用于加载主文件获取加载目录下面的配置, 这里演示如何配置 TCP 和 UDP 的转发, 首先是 TCP 转发:

# 这里假设采用转发 SSH 的 TCP 协议来演示转发端口数据.
# sudo vim /etc/nginx/stream.d/ssh.conf
upstream ssh_proxy {
    # 注意这里有几种并发请求分配算法设置, 不含NGINX商业版本算法.
    # `backup` 这个是备份服务, 当所有的服务器宕机无法访问的时候就会转发到该服务.
 
    # 默认轮流算法, 请求会轮流切换分配访问的服务器
    server server1.example.com:22;
    server server2.example.com:22;
    server server4.example.com:22 backup;
 
    # 权重分配算法, 按照权重来选择权重最高的服务器
    server server1.example.com:22 weight=3;
    server server2.example.com:22 weight=5;
 
    # 客户端IP的哈希算法, 比较老版本暂时不推荐( 不支持 IPv6 和版本不支持 )
    ip_hash;
    server server1.example.com:22;
    server server2.example.com:22;
 
    # 自动分配最少请求服务器, 这种方式最简单方便
    least_conn;
    server server1.example.com:22;
    server server2.example.com:22;
 
    # 手动哈希分配算法, 对指定的某项客户端配置进行哈希分配服务器( 这里采用访问IP地址 )
    # `consistent` 参数可有可无如果设置则默认采用一致性哈希, 能够更快命中某些缓存服务( Memcache/Redis )
    hash $remote_addr consistent;
    server server1.example.com:22;
    server server2.example.com:22;
 
    # 一致性哈希分配, 这里是简化版手动哈希分配( 根据 HTTP-GET 地址哈希比较选择 )
    consistent_hash $request_uri;
    server server1.example.com:22;
    server server2.example.com:22;
 
    # 负载检测分配, 这个方法采用后台服务器转发响应时间作为依赖, 并非可信的完全负载判断.
    server server1.example.com:22;
    server server2.example.com:22;
    fair;
 
    # 这里支持的服务器方式可以支持以下方式
    # 支持最大错误次数( max_fails=3 ), 每次最大错误的超时时间( fail_timeout=30s )
    # 错误判断用于给服务器识别异常从而转发下一级的服务器( 超时/无法访问等情况 )
    server server1.example.com:22 max_fails=3 fail_timeout=30s; # 域名服务器
    server 127.0.0.1:22 max_fails=3 fail_timeout=30s; # IP服务器
    server unix:/tmp/ssh.sock max_fails=3 fail_timeout=30s; # UnixDomain服务器
}
 
# TCP 挂起服务器连接池
server {
    # 设定监听端口, 可以采用多配置项目, 这里说明几个关键配置
    # `reuseport`, 开启了端口快速复用缓解 TIME_WAIT 回收端口请求资源过慢导致请求
    # `udp`, 声明该代理的是 UDP 服务
    # 以上都是比较简单的配置项, 日常基本上就这些参数配置需要注意
    listen 10022 reuseport;
 
    # 启用 TCP 的 NODELAY 特性
    # 网络默认 TCP 启用 `Nagle` 算法, 该算法通过减少传输的数据包( 读写缓冲区集中发放 )
    #
    # `Nagle` 算法的好处: 减少传输的数据包数量, 将传输数据集中一起统一发送, 从而有效提高网络利用率.
    # 但是坏处也是明显, 数据并不会实时传输具有数据传输延时的问题( 因为数据必须等到写缓冲区满足条件发送 )
    #
    # `TCP_NODELAY` 禁用 `Nagle` 算法的好处: 直接实时发送对应数据包, 能够有效降低数据传输的延时
    # 坏处则是会频繁实时发送数据包, 网络会频繁处于活跃状态导致网络利用率低下.
    #
    # 当然如果想要实时传输且提高网络利用率, 则可以采用编写自定义UDP传输协议处理
    tcp_nodelay on;
 
    # 设定连接的超时时间( CONNECT 状态 )
    proxy_connect_timeout 3s;
 
    # 设定代理的请求超时时间( WAIT 状态 )
    proxy_timeout 15s;
 
    # 调用指定的后端服务池
    proxy_pass ssh_proxy;
}
 
# 也可以直接设置转发配置
server {
    listen 20022;
 
    # 直接简单的转发配置
    proxy_pass 127.0.0.1:22;
}

这里配置方式基本上是常见配置, 剩下可以参考官方文档来配置.

UDP转发

UDP 转发配置基本上也差不多, 所以直接采用简单的配置说明:

# 这里假设采用转发 DNS 的 UDP 协议来演示转发端口数据.
# sudo vim /etc/nginx/stream.d/dns.conf
upstream dns_proxy {
    # 自动分配最少请求服务器, 这种方式最简单方便
    least_conn;
    server dns1.example.com:53;
    server dns2.example.com:53;
}
 
# 转发 DNS 服务
server {
    # 设置监听端口和端口重用/UDP转发声明
    listen 53 reuseport udp;
 
    # 直接简单的转发配置
    proxy_pass dns_proxy;
}

UDP 基本上配置和 TCP 差不多.

HTTP转发

这种方式转发比较常规, 被广泛应用在 Web 应用高并发处理, 能够有效起到负载分流功能( 这种又称 反向代理 ):

# 注意, 这里只在 http 配置块配置
http{
    ......
 
    # 这里一般都是加载 /etc/nginx/conf.d/xxx.conf 之中配置
    # 创建代理服务池服务
    upstream tomcat_service {
        # 采用自动分流功能
        least_conn;
 
        # 假设内网多服务器分流, 设置最大错误和错误时间
        server 192.168.1.100:8080 max_fails=2 fail_timeout=15s;
        server 192.168.1.102:8080 max_fails=2 fail_timeout=15s;
        server 192.168.1.104:8080 max_fails=2 fail_timeout=15s;
        server 192.168.1.108:8080 max_fails=2 fail_timeout=15s;
 
        # 追加双机宕机备份
        server 192.168.1.110:8080 max_fails=2 fail_timeout=15s backup;
        server 192.168.1.112:8080 max_fails=2 fail_timeout=15s backup;
    }
 
    # 挂起 http 服务器监听
    server {
        # 设置监听端口
        listen 80;
        # 设置服务器访问域名
        server_name www.example.com;
        ......
 
        # 这里需要设置 localhost 块
        location / {
            # 声明代理转发给服务器池
            proxy_pass http://tomcat_service;
 
            # 设置是否启用服务重定向, 默认 off 不启用代理转发
            proxy_redirect off;
 
            # 将客户端请求的域名转发给代理的服务器, 让接受代理的服务器可以识别出客户端访问的域名
            # 如果采用多端口请求的服务, 需要追加端口信息 `$server_port`, 如下配置:
            # proxy_set_header Host $host:$server_port;
            proxy_set_header Host $host;
 
            # 将域名追加请求协议头
            # `$proxy_add_x_forwarded_for` 该变量会将代理机和客户端IP一起合并( 逗号区分 )发放.
            # `$remote_addr` 则是直接把客户端IP写入而不会加上代理IP
            # 两者按需选择
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-real-ip $remote_addr;
 
            # 设置 CONNECT(连接) 状态超时时间( 默认单位: 秒,60s )
            proxy_connect_timeout 60s;
 
            # 设置 SEND(请求发送) 状态超时时间( 默认单位: 秒,60s )
            proxy_send_timeout 60s;
 
            # 设置 READ(请求发送) 状态超时时间( 默认单位: 秒,60s )
            proxy_read_timeout 60s;
        }
    }
}

WebSocket转发

WebSocket 转发则比较特别, 虽然是长连接请求但是内部配置是在 Http 配置块.

Nginx 代理转发要求 1.3+ 版本

配置示例如下:

# 注意, 这里只在 http 配置块配置
http{
    ......
 
    # 这里一般都是加载 /etc/nginx/conf.d/xxx.conf 之中配置
    # 创建代理服务池服务
    upstream websocket_service {
        # 采用自动分流功能
        least_conn;
 
        # 假设内网多服务器分流, 设置最大错误和错误时间
        server 192.168.1.100:10800 max_fails=2 fail_timeout=15s;
        server 192.168.1.102:10800 max_fails=2 fail_timeout=15s;
    }
 
    # 挂起 http 服务器监听
    server {
        # 设置监听端口
        listen 1080;
        # 设置服务器访问域名
        server_name ws.example.com;
        ......
 
        # 这里需要设置 localhost 块
        location / {
            # 声明代理转发给服务器池
            proxy_pass http://websocket_service;
 
            # 将客户端请求的域名转发给代理的服务器, 让接受代理的服务器可以识别出客户端访问的域名
            proxy_set_header Host $host:$server_port;
 
            # 将域名追加请求协议头
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-real-ip $remote_addr;
 
            # 以上都是常规的配置, 之后就是专属的 WebSocket 配置
            # 以下三行可以直接简单将 http 请求升级成 ws 请求.
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}